Skip to content
← Todas las publicaciones
· Delta1 Labs Blazor.NETOfuscaciónWebAssembly

Cómo ofuscar una aplicación Blazor WebAssembly en .NET 8/9/10 (WebCil)

El Blazor WebAssembly moderno envuelve tus ensamblados .NET reales en WebCil, les pone una huella y los protege con hashes de integridad — de modo que los ofuscadores posteriores a la publicación rompen la aplicación. Aquí tienes por qué, y cómo proteger una aplicación Blazor WASM de la forma correcta: ofuscar en tiempo de compilación, antes de que el SDK empaquete en WebCil.

Para ofuscar una aplicación Blazor WebAssembly en .NET 8, 9 o 10, aplica la ofuscación a tu ensamblado de aplicación en tiempo de compilación — antes de que el SDK de .NET lo empaquete en WebCil — en lugar de a la salida de publicación terminada. Hazlo así y el SDK emite WebCil correcto, huellas correctas, hashes de integridad correctos y compresión correcta sobre tu código ya protegido. Hazlo a la antigua, reescribiendo las DLL en la carpeta de publicación, y la aplicación simplemente no cargará.

Ese detalle de “antes, no después” es toda la partida en el Blazor moderno. Aquí tienes por qué cambió, y cómo hacerlo bien.

Por qué el Blazor WASM moderno es difícil de proteger

Blazor WebAssembly ejecuta tus ensamblados .NET reales y compilados en el navegador. Eso siempre lo ha convertido en el destino de .NET más expuesto — los ensamblados reales se descargan a cada visitante, que puede guardarlos y decompilarlos de vuelta a C#. Lo que cambió en las versiones recientes de .NET es cómo se empaquetan esos ensamblados, y cada uno de esos cambios se interpone en el camino de una pasada de ofuscación ingenua.

Los ensamblados van en WebCil dentro de .wasm

Desde .NET 8, el valor por defecto del SDK es WebCil: cada ensamblado administrado se envuelve en un pequeño contenedor con forma de WebAssembly y se sirve como un archivo .wasm. WebCil existe porque algunos proxies corporativos, CDN y firewalls de host eliminan, bloquean o reescriben las descargas de .dll; envolver el ensamblado en un sobre .wasm sortea eso y carga limpiamente a través de la canalización de WebAssembly del navegador. La consecuencia práctica para la protección: no hay ninguna .dll normal en la salida que darle a un ofuscador. Lo que hay en disco es un contenedor WebCil que primero tendrías que desenvolver.

Los nombres de archivo llevan huella

.NET 8+ pone una huella en los activos del framework con un hash de contenido en el nombre del archivo — verás algo como MyApp.a1b2c3d4e5.wasm en _framework. La huella se deriva del contenido del archivo, y cada referencia al archivo (en el manifiesto de arranque y en otros sitios) usa el nombre con huella. Cambia los bytes a posteriori y la huella será incorrecta, y todo lo que apunta al nombre antiguo ahora apunta a un archivo que no existe.

La integridad se impone, y no hay blazor.boot.json que parchear

El cargador verifica cada activo contra un hash de integridad (SRI) registrado antes de ejecutarlo. Aquí es donde el consejo antiguo realmente se desmorona: en .NET 9 y 10 no hay ningún blazor.boot.json independiente que abrir y editar — la configuración de arranque (la lista de recursos y sus hashes de integridad) la genera el SDK y se incrusta en la maquinaria de arranque de dotnet.js. El manifiesto que solías parchear a mano ya no está ahí como archivo. Además de eso, el SDK distribuye copias precomprimidas .br (Brotli) y .gz de cada activo que el runtime prefiere.

Por qué las herramientas ingenuas posteriores a la publicación lo rompen

Pon todo eso junto y entenderás por qué “publica, luego pasa un ofuscador sobre la salida” ya no funciona. Para intercambiar un ensamblado ofuscado después de publicar, una herramienta tendría que:

  1. Desenvolver el contenedor WebCil de vuelta a un ensamblado en bruto.
  2. Reescribir el ensamblado con las transformaciones de ofuscación.
  3. Volver a envolverlo como WebCil.
  4. Recalcular la huella de hash de contenido y renombrar el archivo.
  5. Recalcular el hash de integridad SRI y volver a escribirlo en la configuración de arranque — que puede estar incrustada en dotnet.js, no en un archivo JSON.
  6. Regenerar las copias .br y .gz para que el runtime no cargue un original obsoleto.

Omite cualquiera de esos pasos y obtienes un fallo de integridad o una pantalla en blanco. Y como el formato, el esquema de huellas y el lugar donde vive el manifiesto se han movido entre .NET 8, 9 y 10, un reescritor posterior a la publicación persigue un objetivo en movimiento y tiende a romperse con la siguiente subida de versión del SDK. Es el lugar equivocado de la canalización para intervenir.

El enfoque correcto: ofuscar antes del empaquetado de WebCil

La solución es mover la protección aguas arriba del empaquetado. Tu ensamblado de aplicación es un ensamblado administrado perfectamente corriente justo después de que el compilador lo produce y antes de que el SDK lo convierta en WebCil. Ese es el momento de ofuscarlo. Inserta un paso de MSBuild que transforme el ensamblado compilado en ese punto, y luego deja que la canalización normal de publicación del SDK haga todo lo que ya hace — empaquetar WebCil, poner huellas, calcular integridad, recortar, comprimir — solo que ahora lo hace todo sobre tu código protegido.

La parte elegante es que nada aguas abajo necesita parcheo. El SDK calcula la huella y el hash de integridad a partir de los bytes ofuscados, así que son correctos por construcción. El manifiesto de arranque referencia el archivo correcto, viva en dotnet.js o en un archivo JSON. Las copias comprimidas se generan a partir del ensamblado protegido. No hay ningún manifiesto que reparar porque nunca lo invalidaste.

Así es como Nebula.NET protege Blazor WebAssembly. Añades su integración de compilación (una importación de Nebula.Blazor.targets) al proyecto de la aplicación; engancha la ofuscación en el punto correcto de la compilación, por delante del empaquetado de WebCil, de modo que un dotnet publish normal produce una aplicación protegida que arranca exactamente igual que la original:

dotnet publish -c Release

Sin comando de posprocesamiento aparte, sin cirugía de manifiestos, sin regenerar copias comprimidas a mano — la cadena de herramientas hace el empaquetado, como está diseñada para hacer, sobre código que ya ha pasado por la ofuscación. Consulta la documentación de Nebula.NET para la configuración y los ajustes.

Qué funciona en WASM — y qué no puede

No todas las técnicas de protección pueden ejecutarse en un navegador, y vale la pena ser preciso sobre dónde está la línea.

Funciona en Blazor WASM — porque estas se incrustan estáticamente en el ensamblado distribuido y no necesitan nada especial en tiempo de ejecución:

  • El renombrado elimina los nombres de tipos, métodos y campos, así que el código decompilado pierde su capa más legible.
  • El cifrado de cadenas limpia los literales delatores — URL, mensajes, claves, SQL — del ensamblado.
  • El aplanado del flujo de control reescribe los cuerpos de los métodos en una forma que un decompilador no puede reconstruir limpiamente en C# estructurado.

No puede funcionar en Blazor WASM — porque dependen de la emisión de IL en tiempo de ejecución:

  • El cifrado de métodos y la virtualización de código reconstruyen o descifran el cuerpo de un método mientras la aplicación se ejecuta, lo que necesita Reflection.Emit / generación dinámica de IL. El runtime de Blazor WebAssembly — como la compilación AOT en general — no permite generar IL en tiempo de ejecución, así que esas técnicas no se aplican aquí. Son la herramienta correcta en destinos de escritorio y servidor, no en el navegador.

Así que la expectativa honesta para Blazor es: renombrado, cifrado de cadenas y aplanado del flujo de control, aplicados en tiempo de compilación, sobre WebCil empaquetado correctamente.

Una palabra honesta sobre el código del lado del cliente

La ofuscación cambia la economía de la ingeniería inversa; no deroga la regla de que cualquier cosa que se ejecute en la máquina de un usuario puede, en principio, inspeccionarse allí. Un atacante decidido con suficiente tiempo todavía puede avanzar contra cualquier protección del lado del cliente. El objetivo no es “inquebrantable” — eso no existe para el código que entregas al navegador — es elevar el coste de leer tu lógica de trivial (ahora mismo, una aplicación Blazor sin proteger se decompila a algo cercano al código fuente en minutos) a lo bastante caro como para que no valga la pena.

Por eso también importa la división: endurece tu lógica propietaria, reglas de precios y restricciones de funciones en el cliente, pero mantén los secretos de verdad — claves de API, claves de firma, decisiones de derechos con autoridad — en el servidor, detrás de una API, nunca compilados en un ensamblado que distribuyes a cada visitante.

Pruébalo

Si distribuyes Blazor WebAssembly, la forma más rápida de ver la exposición es abrir la salida de tu propio _framework publicado en un decompilador y leer un componente que consideres propietario. Luego añade protección en tiempo de compilación y mira de nuevo.

Descarga Nebula.NET para probarlo en tu propia compilación, o lee la documentación de Nebula.NET para la configuración de Blazor WebAssembly y la lista completa de transformaciones.

Prueba Nebula.NET

Endurece tu código .NET en minutos — empieza con la edición gratuita.