DEV Community

Zero Heartbeat
Zero Heartbeat

Posted on Originally published at delta1labs.com

How to protect a Blazor WebAssembly app from decompilation

Blazor WebAssembly is a genuinely great way to write a .NET SPA. It's also the single most exposed target you can ship .NET to — and most obfuscators quietly ignore it. If you have proprietary logic, a licence check, or an algorithm you'd rather competitors not read, this is the deployment model that hands it over most easily.

Why Blazor WASM is the most exposed .NET target

A Blazor WebAssembly app runs your real, compiled .NET assemblies in the browser. To do that, it downloads them: open the network tab on any Blazor WASM site and you'll see the _framework folder streaming down .dll and .wasm files. Those are your assemblies — the actual ones, not a minified approximation.

Anyone can save them and open them in a free decompiler like our own Glass.NET. As we covered in can .NET code be decompiled?, a managed assembly is a near-complete blueprint of your source: type and method names, control flow, and every string literal come straight back. On the server, at least the DLLs stay on your machine. In Blazor WASM, you're shipping them to every visitor.

Why you can't just obfuscate the DLLs

The obvious fix — run your assemblies through an obfuscator and drop them into the publish output — doesn't work with Blazor, and this is exactly why most tools skip it. Blazor's loader does two things that break a naïve swap:

  1. Integrity checking. blazor.boot.json contains a SHA-256 hash of every assembly. Replace a DLL with an obfuscated one and its hash no longer matches — Blazor refuses to load it.
  2. Pre-compressed copies. A published app ships .br (Brotli) and .gz copies of each file, which the runtime prefers. Obfuscate only the raw .dll and the browser happily loads the stale compressed original instead.

So protecting Blazor isn't just "obfuscate the assembly" — it's "obfuscate the assembly and repair the boot manifest and the compressed copies to match." Miss either and you get a white screen.

Protecting a Blazor app with Nebula

Nebula's nebula blazor command does the whole thing end to end. Publish as normal, then point Nebula at the _framework folder:

dotnet publish -c Release -o publish
nebula blazor --framework publish/wwwroot/_framework --config nebula.config.json
Enter fullscreen mode Exit fullscreen mode

Nebula obfuscates the app's assemblies using your config, then recomputes the integrity hashes in blazor.boot.json and regenerates the .br/.gz copies so everything lines up. The protected app boots exactly like the original — the difference is what a visitor finds when they open the DLLs. Full walkthrough in Protecting Blazor WebAssembly.

Everything in Nebula's toolbox applies to the assemblies it protects:

  • Renaming strips the type, method and field names.
  • Control-flow flattening destroys the structure a decompiler rebuilds.
  • String encryption clears the giveaway literals out of the file.
  • Code virtualization (Enterprise) removes the IL of your most sensitive methods entirely, replacing it with a call into an embedded VM. Crucially, the Nebula runtime targets .NET Standard 2.0, so virtualized methods run inside WebAssembly just as they do on the desktop — see code virtualization.

What to protect — and what still belongs on the server

Obfuscation changes the economics of reverse engineering, but it doesn't repeal the basic rule of client-side code: anything that runs on the user's machine can, in principle, be inspected on the user's machine. So the split is simple.

Harden in the client: proprietary algorithms, pricing or scoring logic, licence and feature gates, anything that is your intellectual property and has to run in the browser.

Keep on the server: real secrets. API keys, signing keys, and authoritative entitlement decisions belong behind an API, not compiled into an assembly you ship to every visitor — obfuscated or not. Nebula makes the client-side code expensive to read; your backend makes the secrets impossible to reach.

Try it

If you ship Blazor WebAssembly, open your own published _framework folder in Glass.NET first and read a component you consider proprietary — it's a fast way to see the exposure. Then run nebula blazor over the same output and look again.

Download Nebula.NET to try it, or read the Blazor protection guide and the full feature list.

Top comments (0)