This article shows how to improve the security of an ASP.NET Core Blazor application by adding security headers to all HTTP Razor Page responses (Blazor WASM hosted in a ASP.NET Core hosted backend). The security headers are added using the NetEscapades.AspNetCore.SecurityHeaders Nuget package from Andrew Lock. The headers are used to protect the session, not for authentication. The application is authenticated using OpenID Connect, the security headers are used to protected the session. The authentication is implemented in the Blazor application using the BFF pattern. The WASM client part is just a view of the server rendered trusted backend and cookies are used in the browser. All API calls are same domain only and protected with a cookie and same site.
Blogs in this series
Improving application security in ASP.NET Core Razor Pages using HTTP headers – Part 1Improving application security in Blazor using HTTP headers – Part 2Improving application security in an ASP.NET Core API using HTTP headers – Part 3
The NetEscapades.AspNetCore.SecurityHeaders and the NetEscapades.AspNetCore.SecurityHeaders.TagHelpers Nuget packages are added to the csproj file of the web application. The tag helpers are added to use the nonce from the CSP in the Razor Pages.
The Blazor definition is very similar to the ASP.NET Core Razor Page one. The main difference is that the CSP script policy is almost disabled due to the Blazor script requirements. We can at least force self on the content security policy header script-src definition.
The Blazor WASM logout link sends a HTTP Form POST request which is redirected to the OpenID Connect identity provider. The CSP needs to allow this redirect and the content secure policy form definition allows this.
bool isDev, string identityProviderHost)
var policy = new HeaderPolicyCollection()
// due to Blazor
// maxage = one year in seconds
policy.AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 60 * 60 * 24 * 365);
Blazor adds the following script to the WASM host file. This means the CSP for scripts cannot be implemented in a good way.
var Module; window.__wasmmodulecallback__(); delete window.__wasmmodulecallback__;
script-src (from CSP evaluator)
‘self’ can be problematic if you host JSONP, Angular or user uploaded files.
‘unsafe-inline’ allows the execution of unsafe in-page scripts and event handlers.
‘unsafe-eval’ allows the execution of code injected into DOM APIs such as eval().
The aspnetcore-browser-refresh.js is also added for hot reload. This also prevents a strong CSP script definition in development. This could be fixed with a dev check in the policy definition. There is no point fixing this, until the wasmmodulecallback script bug is fixed.
I am following the ASP.NET Core issue and hope this can be improved for Blazor.
In the Startup class, the UseSecurityHeaders method is used to apply the HTTP headers policy and add the middleware to the application. The env.IsDevelopment() is used to add or not to add the HSTS header. The default HSTS middleware from the ASP.NET Core templates was removed from the Configure method as this is not required.
The server header can be removed in the program class file of the Blazor server project if using Kestrel. If using IIS, you probably need to use the web.config to remove this.
options.AddServerHeader = false)
When we scan the https://securityheaders.com/ you can view the results. You might need to disable the authentication to check this, or provide a public view.
The content security policy has a warning due to the script definition which is required for Blazor.
The https://csp-evaluator.withgoogle.com/ also displays a high severity finding due the the CSP script definition.
If the application is fully protected without any public views, the follow redirects checkbox on the security headers needs to be disabled as then you only get the results of the identity provider used to authenticate.
Maybe until the CSP script is fixed for Blazor, you probably should avoid using Blazor for high security applications and use ASP.NET Core Razor Page applications instead.
If you use Blazor together with tokens in Azure AD or Azure B2C and this CSP script bug, you leave yourself open to having your tokens stolen. I would recommend using server authentication with Azure which removes the tokens from the browser and also solves the Azure SPA logout problem. Azure AD, Azure B2C do not support the revocation endpoint or introspection, so it is impossible to invalidate your tokens on a logout. It does not help if the IT admin, Azure monitoring can invalidate tokens using CAE.