aspnetcore: Blazor WASM apps being blocked by firewalls due to .NET assemblies, even after renaming DLLs

Describe the bug

Users are unable to access our Blazor application because their corporate firewalls block downloading the .NET assembly files. We followed the guidelines MS published and mentioned in https://github.com/dotnet/aspnetcore/issues/5477#issuecomment-624323998 to rename the ‘.dll’ files to ‘.bin’, but the assembly files are still being blocked.

https://docs.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-5.0#change-the-filename-extension-of-dll-files

One of the firewall systems we’ve identified is TrustWave WebMarshal. Of course such products are configurable, but by default this product appears to block Blazor apps. It appears it may be the content of the .Net assemblies that are being blocked. The firewall security team have made it clear than allowing DLL’s is a hard ‘no’.

Others report in https://github.com/dotnet/aspnetcore/issues/21996 and https://github.com/dotnet/aspnetcore/issues/19552#issuecomment-597065130 that Sophos Antivirus also blocks Blazor application due to the .Net assembly files and that renaming the files does not work there either. Others suggest implementing XOR encoding for transit, but I haven’t found a good implementation.

Others report in https://github.com/dotnet/aspnetcore/issues/23084 that McAfee Web Gateway blocks Blazor .Net assemblies, even when renamed, based on content because they are detected as “application/dotnet-assembly”.

Others mention in https://github.com/dotnet/aspnetcore/issues/21996#issuecomment-641303599 that their (unnamed) firewalls block on content not file extension so renaming does not help and that a fairly complex base64 encoding process to preload the assemblies into the cache and then refresh is a workaround.

Others report in https://github.com/dotnet/aspnetcore/issues/19552#issuecomment-595254961 that Palo Alto firewall and in https://github.com/dotnet/aspnetcore/issues/19552#issuecomment-619235810 and https://github.com/dotnet/aspnetcore/issues/16247 and https://github.com/dotnet/aspnetcore/issues/5477#issuecomment-386625015 and https://github.com/dotnet/aspnetcore/issues/5477#issuecomment-387017251 that Avast Antivirus / AVG Antivirus blocks Blazor, but possibly the renaming of DLLs works there? I could find anyone confirm if that actually worked.

Others report in https://github.com/dotnet/aspnetcore/issues/16247#issuecomment-378967145 that Symantec Web Firewalls blocks Blazor apps, unclear if only by filename or content.

Others report in https://forums.asp.net/t/2161027.aspx that Norton Antivirus blocks Blazor apps by detection of the DLL content.

This appears to be a massive road-block to Blazor adoption. Past security sins mean that no one trusts anything that smell like a dll/exe. I think Blazor needs a native / built-in solution to either encodes the payloads so they they don’t look like traditional security risks. Really I think the Blazor application should be compiled to WASM when published, so it is only wasm code that is being downloaded. There are discussion of upcoming ‘AOT’ compiling feature coming since 2018, which sounds like a proper solution.

@vectorix states it well: “If the Blazor model does not play 100% well with existing firewalls it is almost a show stopper. It should flow through all firewalls effortlessly just like Javascript. If I was a firewall administrator, I am not sure if I would want to add a new white list entry for each new languages that has its own IL format.”

Questions:

Has any developed a workaround that will enable Blazor use in corporate settings?

Does Microsoft have published advice for firewall vendors how to allow Blazor while blocking traditional DLL attacks?

Does the .Net 6.0 preview 1 fix this in any way?

Should deploying Blazor in the real world be this hard? 😀

@mkArtakMSFT you have often commented on this issue, and often say you hear of this issue rarely. But are you sure there lots of people using Blazor with corporate users and not experiencing this problem? If so many products like WebMarshall, Sophos, McAfee, Norton, maybe Symmantec and other all block Blazor/.Net by default on content not filename, that seems like a huge barrier to adoption? And if all of those plus Palo Alto, Avast/AVG, and presumably others block on filename, should ‘.dll’ even be the filename or encoding used for IL files?

To Reproduce

Publish with:

dotnet publish --output foo --configuration Release

Then rename DLLs with:

for f in wwwroot/_framework/*.dll; do mv "$f" "${f/.dll}".bin; done
sed -i 's/\.dll"/.bin"/g' wwwroot/_framework/blazor.boot.json
sed -i 's/\.dll\$/.bin\$/g' wwwroot/service-worker.published.js

The resulting application works fine when the firewall is not blocking it, but try to access it behind many web proxy firewalls will fail.

Exceptions (if any)

Each .Net assembly is blocked by the firewall and fails with:

Class name: TypeError Message: Failed to fetch Tags: UnhandledPromiseRejection

Further technical details

  • ASP.NET Core version 5.0.201

  • Include the output of dotnet --info

.NET SDK (reflecting any global.json): Version: 5.0.201 Commit: a09bd5c86c

Runtime Environment: OS Name: ubuntu OS Version: 18.04 OS Platform: Linux RID: ubuntu.18.04-x64 Base Path: /usr/share/dotnet/sdk/5.0.201/

Host (useful for support): Version: 5.0.4 Commit: f27d337295

.NET SDKs installed: 3.1.404 [/usr/share/dotnet/sdk] 5.0.201 [/usr/share/dotnet/sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 3.1.10 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.4 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.10 [/usr/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.4 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET runtimes or SDKs: https://aka.ms/dotnet-download

  • The IDE (VS / VS Code/ VS4Mac) you’re running on, and its version

Linux CLI and VS 2019

References

https://github.com/dotnet/aspnetcore/issues/5477#issuecomment-624323998 https://github.com/dotnet/aspnetcore/issues/21996 https://github.com/dotnet/aspnetcore/issues/21996#issuecomment-641303599 https://github.com/dotnet/aspnetcore/issues/23084 https://github.com/dotnet/aspnetcore/issues/19552#issuecomment-595254961 https://github.com/dotnet/aspnetcore/issues/19552#issuecomment-619235810 https://github.com/dotnet/aspnetcore/issues/16247 https://github.com/dotnet/aspnetcore/issues/5477#issuecomment-386625015 https://forums.asp.net/t/2161027.aspx Microsoft guidelines for renaming files Encoding files with service worker idea

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 9
  • Comments: 28 (4 by maintainers)

Most upvoted comments

During development of our current Balzor WASM application we selected some of our customers to alpha test our new application, using their network and infrastructure. Internally we never had issues starting our Balzor WASM application but about 50% of the customer that are testing the application can not start our Blazor WASM application due to their firewall blocking the application. Especially if the customer works in finances or insurance. In the financial sector it is not an option to advice the customer to change their firewall settings. We are suffering a massive loss of trust due to this issue. This issue is an absolute killer for Blazor WASM for our industry, and we are losing a lot of development effort if this issue will not be fixed…

Of course we tried workarounds: Like renaming the files. But this did not resolve the issue.

Currently we are trying the “Encoding files with service worker idea“ workaround but without using the service worker. The following sample code demonstrates how we try to solve it:

  1. During our build process we generate for every dll additionally a base64 text files. sample_code.zip (DllEncoder.cs).

  2. We disabled the auto start of the blazor app in our index.html <script src"_framework/blazor.webassembly.js" autostart="false"></script>

  3. We use the “loadBootResource” extension to fetch the dlls and in case of 403 error we fallback to load the base64 text files. sample_code.zip (sample.js)

Probably this can help other developers with the same issue. But at the end we hope that Microsoft will fix the issue. Since tricking out firewall to get Blazor WASM running is not a good nor acceptable plan for us… This will not strengthen the trust in our software.

Just note that if packaging specifically for these firewalls is an extra step, an opt-in, then most Blazor wasm deployments will be inaccessible from these locations (i.e. large corporations with content inspection in proxy). As a developer you need to be aware of the problem and fix it. This means every developer has to learn this (often the hard way) and figure out how to solve it.

In terms of adoption, having a new tech that sometimes doesn’t work for some visitors for mysterious reasons is probably not good.

Just note that if packaging specifically for these firewalls is an extra step, an opt-in, then most Blazor wasm deployments will be inaccessible from these locations (i.e. large corporations with content inspection in proxy). As a developer you need to be aware of the problem and fix it. This means every developer has to learn this (often the hard way) and figure out how to solve it.

In terms of adoption, having a new tech that sometimes doesn’t work for some visitors for mysterious reasons is probably not good.

I completely agree with this. An out-of-the-box solution should work for this without having to do anything. Then, if something needs to be customised for additional requirements, then allow the mechanisms to make the changes or by project configuration choose the method to build those packages. Something like: Project options / Web/ Build/ Packaging : Default (Basic and works), Method2, Method3, None, Custom.

But by default make it work when compiling in Release mode.

Hi folks,

We have an update on this topic here

We added a feature on RC1 that allows people to create their own “packaging format” for blazor applications and have created an experimental package that packages the dlls in a different way as described in the blog post above. You should be able to use our extension package (or create your own package to reuse and share) that customizes how a Blazor app loads and where you can apply any transformation to the app dlls as part of the build process and undo the transformation afterwards.

Just want to add that I’m also running into this issue: our app was blocked by an internal VPN connection that people had to connect to with WFH restrictions in an enterprise setting. This impacts Blazors potential in enterprise environments and this is a real risk for adoption.

I would like to add some information about my experience with ESET Internet Security installed on my own laptop. This laptop has Core i7-6500U processor which is optimized for battery life and not performance. When ESET doesn’t monitor HTTPS traffic I have to wait about 1.5s to download and run default Blazor WASM ASP.NET Hosted application. When HTTPS traffic monitoring is enabled I have to wait three! times as long.

I have confirmed that browser downloads compressed files (*.br) but before they are delivered to the browser they are decompressed and scanned by ESET. This slows down the loading considerably. After scanning file is returned to the browser in uncompressed form and Content-Encoding header is removed. There is no double decompression. The slowdown is only due to an antivirus scanning.

It seems that the only solution is renaming and encoding but before you implement it you should contact with firewall/antivirus vendors and ask what they are going to do. It is possible that they implement an algorithm to decode all of these files, and we’ll be back to where we started.

@guardrex can you use the blog post as a base to document how to do this?

Ok, I tested modifying the header back on client side. It works and is fairly simple to implement. (Except that the published js file is minimized.)

I added the line data[0] = 77; after this line https://github.com/dotnet/aspnetcore/blob/c1d16e3ccd2df6a3ff06b7e0233ad96fe6113d5b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts#L460

This restores header from BZ to MZ with near zero overhead. (Note that this is a dirty proof of concept hack that assumes all files that goes through this function are supposed to start with M.)

Doing this the WebAssembly loaded successfully.

Attaching the complete code for modifying binaries for test, including updating compressed files and updating sha256. It is just a quick hack ran from LINQPad.

void Main()
{
	var dir = @"C:\path\to\project\bin\Release\net6.0\browser-wasm\publish\wwwroot\_framework";
	var dllFiles = Directory.GetFiles(dir, "*.dll");
	// Modify header on .dll-files
	foreach (var file in dllFiles)
		FlipBz(file);

	// Create new compressed copies
	foreach (var file in dllFiles)
	{
		CompressBr(file);
		CompressGz(file);
	}

	// Update sha256 checksum in blazor.boot.json
	var shas = new Dictionary<string, string>();
	foreach (var file in dllFiles)
	{
		using SHA256 mySHA256 = SHA256.Create();
		using var fs = File.Open(file, FileMode.Open, FileAccess.ReadWrite);
		var sha256 = mySHA256.ComputeHash(fs);
		var base64Sha256 = Convert.ToBase64String(sha256);
		shas.Add(Path.GetFileName(file), base64Sha256);
	}
	var jsonFile = Path.Combine(dir, "blazor.boot.json");
	var json = File.ReadAllText(jsonFile);
	foreach (var kvp in shas)
	{
		// This is what happens when you used to be a Perl programmer
		var r = $"(\"{Regex.Escape(kvp.Key)}\":)[^,]+(,)?$";
		json = Regex.Replace(json, r, $"$1 \"sha256-{kvp.Value}\"$2", RegexOptions.Multiline);
	}
	File.WriteAllText(jsonFile, json);
	CompressBr(jsonFile);
	CompressGz(jsonFile);
	//json.Dump();
}
void CompressBr(string file)
{
	using var input = File.OpenRead(file);
	using var output = File.Create(file + ".br");
	using var compressor = new BrotliStream(output, CompressionMode.Compress);
	input.CopyTo(compressor);
}
void CompressGz(string file)
{
	using var input = File.OpenRead(file);
	using var output = File.Create(file + ".gz");
	using var compressor = new GZipStream(output, CompressionMode.Compress);
	input.CopyTo(compressor);
}
void FlipBz(string fn)
{
	using var fs = File.Open(fn, FileMode.Open, FileAccess.ReadWrite);
	using var bw = new BinaryWriter(fs);
	var bz = Encoding.ASCII.GetBytes("BZ");
	bw.Write(bz);
}

@legistek Obfuscation / encryption is not an option in my opinion. “Good viruses” have been using these techniques for years, and antivirus vendors have caught them anyway. This solution will work a few hours, maybe a few days at most.

Hi @eguardiola

we thought about compressing the data too instead of using base64 strings. Currently we did not implement it because we hope there will be a fix in dotnet 6.0 somehow and we can remove the current workaround.

Blazor itself writes the downloaded dlls into the cache. Per client the download happens only once. This mechanism still works when you are using a custom loadBootResource function.

We invested some time to check out some libraries which could help. One simple solution for use would be to use lz-string compression:

Client Side: https://pieroxy.net/blog/pages/lz-string/index.html

.Net on Build Server: https://www.nuget.org/packages/LZStringCSharp/