Some Blazor Server extensions
A very simple middleware that adds headers to requests using the Response.OnStarting
hook. In fact, it allows executing any code on an HttpContext at the start of a request, as it expects a type that implements the interface
public interface ISecurityHeadersPolicy
{
void ApplyHeaders(HttpContext context, IWebHostEnvironment environment);
}
I needed a simple way to manage security headers on a Blazor Server site and, well, the name stuck.
Add the required services to the WebApplicationBuilder
and, optionally, configure the only two settings available
using Sotsera.Blazor.Server.SecurityHeaders.Blazor;
using Sotsera.Blazor.Server.SecurityHeaders.Policies;
using Sotsera.Blazor.Server.SecurityHeaders.Policies.Permissions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSecurityHeaders(c =>
{
c.DisableKestrelServerHeader = true;
c.AntiforgeryTokenPrefix = "SuperSecretToken";
});
Add the middleware to the pipeline specifying the default policy (example defined below)
var app = builder.Build();
app.UseSecurityHeaders(new DefaultPolicy());
Override the policy on any IEndpointConventionBuilder like, for example, on a group
// This endpoint will have the default policy
app.MapGet("with-default-headers", () => "default headers");
// Override the security headers for a specific or group of endpoints
var group = app.MapGroup("api")
.RequireSecurityHeaders(new ApiPolicy());
// This endpoint will have the api policy
group.MapGet("with-api-headers", () => "api headers");
Disable the security headers for an IEndpointConventionBuilder
group.MapGet("without-headers", () => "without headers")
.DisableSecurityHeaders();
Override the policy specifically for Blazor server with interactivity auto or web assembly. The library contains a SHA-256 provider for the importmap script added by the <ImportMap />
component which can be resolved by a policy in order to include the sha in the Content Security Policy (CSP).
app.MapRazorComponents<App>().AddInteractiveServerRenderMode()
.RequireSecurityHeaders(new BlazorPolicy());
// very basic policy
internal class DefaultPolicy : ISecurityHeadersPolicy
{
public virtual void ApplyHeaders(HttpContext context, IWebHostEnvironment environment)
{
var headers = context.Response.Headers;
headers.Remove("-- header name --");
headers.XContentTypeOptions = "-- value --";
}
}
// derived policy
internal class ApiPolicy : DefaultPolicy
{
public override void ApplyHeaders(HttpContext context, IWebHostEnvironment environment)
{
base.ApplyHeaders(context, environment);
context.Response.Headers.ContentSecurityPolicy = "-- value --";
}
}
// Blazor specific policy with importmap's SHA-256 in the Csp and a simple Permission policy
internal class BlazorPolicy : DefaultPolicy
{
public override void ApplyHeaders(HttpContext context, IWebHostEnvironment environment)
{
// retrieve the SHA-256 for the importmap script created by the <ImportMap /> component
var provider = context.GetRequiredService<IBlazorImportMapDefinitionShaProvider>();
var sha = provider.GetSha256(context);
// append the sha to the allowed sources
context.Response.Headers.ContentSecurityPolicy = $"script-src-elem {sha}";
// disable the camera and geolocation usage
context.Response.Headers["Permissions-Policy"] = new PermissionsPolicy
{
Camera = "()",
Microphone = "()"
};
}
}
- Andrew Lock (@andrewlocknet) for NetEscapades.AspNetCore.SecurityHeaders
- IconShock (FreeIcons) for the library icon (color: #702AF7)