How to use NetEscapades.AspNetCore.SecurityHeaders
By FoxLearn 1/3/2025 9:59:06 AM 154
What are security headers?
Security headers are HTTP headers that improve your application's security by guiding browsers to activate or deactivate specific features, thus minimizing your attack surface. While some headers apply to all HTTP responses, others are specifically for HTML responses. However, applying HTML-specific headers to all responses can be a good defense-in-depth strategy, as suggested by OWASP.
The NetEscapades.AspNetCore.SecurityHeaders
package helps simplify the process of setting up security headers in ASP.NET Core applications. It offers sensible default configurations and a fluent builder pattern for easy customization to fit your application's needs.
First, add the package to your application:
Using .NET CLI:
dotnet add package NetEscapades.AspNetCore.SecurityHeaders --version 1.0.0-preview.1
Alternatively, add the package directly to your .csproj
file:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> <ItemGroup> <!-- Add the package --> <PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="1.0.0-preview.1" /> </ItemGroup> </Project>
Add the Security Headers Middleware
Configure the middleware by adding it to the services and middleware pipeline in your Program.cs
or Startup.cs
file.
You can add the security headers middleware to the start of your application's middleware pipeline using the UseSecurityHeaders()
extension method as shown below.
var builder = WebApplication.CreateBuilder(); var app = builder.Build(); // Apply security headers app.UseSecurityHeaders(); app.MapGet("/", () => "Hello world!"); app.Run();
Security Headers Provided by Default
The SecurityHeadersMiddleware
automatically adds several important security headers to all HTTP responses. By default, the following headers will be applied:
- X-Content-Type-Options:
nosniff
- X-Frame-Options:
Deny
- Referrer-Policy:
strict-origin-when-cross-origin
- Content-Security-Policy:
object-src 'none'; form-action 'self'; frame-ancestors 'none'
- Cross-Origin-Opener-Policy:
same-origin
- Strict-Transport-Security:
max-age=31536000; includeSubDomains
(applied only to HTTPS responses)
Customizing Content-Security-Policy (CSP)
You might want to allow certain external domains or disable certain types of scripts.
builder.Services.AddSecurityHeaders(options => { options.AddContentSecurityPolicy(builder => { builder .AddDefaultSrc("'self'") .AddScriptSrc("'self'", "https://foxlearn.cdn.com") .AddStyleSrc("'self'", "'unsafe-inline'"); }); });
This example allows scripts to be loaded from your own domain and from https://
foxlearn.cdn.com
, while allowing inline styles.
Customizing Security Headers
To customize security headers, you can create a HeaderPolicyCollection
and use a fluent builder interface to adjust the default headers. For example, you can individually add default headers or remove the Server
header. After creating a custom collection, apply it with the UseSecurityHeaders()
method.
var policyCollection = new HeaderPolicyCollection() .AddFrameOptionsDeny() .AddContentTypeOptionsNoSniff() .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 60 * 60 * 24 * 365) .AddReferrerPolicyStrictOriginWhenCrossOrigin() .RemoveServerHeader() .AddContentSecurityPolicy(builder => { builder.AddObjectSrc().None(); builder.AddFormAction().Self(); builder.AddFrameAncestors().None(); }) .AddCustomHeader("X-My-Test-Header", "Header value"); app.UseSecurityHeaders(policyCollection);
Applying Headers to Different Endpoints
A key feature in this release is the ability to apply different security headers to different endpoints:
- Configure default and named policies for the application.
- Use
UseSecurityHeaders()
to add middleware. - Apply specific policies to certain endpoints using the
WithSecurityHeadersPolicy()
method.
For example, you could apply API-specific headers to a /api
endpoint:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); // 1. Configure the policies for the application builder.Services.AddSecurityHeaderPolicies() .SetDefaultPolicy(p => p.AddDefaultSecurityHeaders()) // Configure the default policy .AddPolicy("API", p => p.AddDefaultApiSecurityHeaders()); // Configure named policies var app = builder.Build(); // 2. Add the security headers middleware app.UseSecurityHeaders(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); app.MapGet("/api", () => "Hello world") .WithSecurityHeadersPolicy("API"); // 3. Apply a named policy to the endpoint app.Run();
Complete Customization with SetPolicySelector
A new SetPolicySelector()
method allows complete customization of headers based on the request. This is useful for scenarios like multi-tenant applications where headers vary based on the tenant.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddSecurityHeaderPolicies() .SetPolicySelector((PolicySelectorContext ctx) => { // TODO: anything you need to build the HeaderPolicyCollection // e.g. use services from the DI container (if you need to) IServiceProvider services = ctx.HttpContext.RequestServices; var selector = services.GetService<TenantHeaderPolicyCollectionSelector>(); var tenant = services.GetService<ITenant>(); HeaderPolicyCollection policy = selector.GetPolicyForTenant(tenant); return policy; // This is the policy that is applied }); var app = builder.Build(); app.UseSecurityHeaders(); app.MapGet("/api", () => "Hello world"); app.Run();
The SetPolicySelector()
method accepts a lambda or method that receives a PolicySelectorContext
with the following information to help decide which security policy to apply:
- HttpContext: The current
HttpContext
for the request. - ConfiguredPolicies: A dictionary of named policies that have been configured for the application.
- DefaultPolicy: The default policy to apply to the request.
- EndpointPolicyName: The name of the specific policy applied to the endpoint, if any.
- EndpointPolicy: The policy applied to the endpoint, or
null
if no specific policy is applied. - SelectedPolicy: The policy that would be applied by default either
EndpointPolicy
orDefaultPolicy
.
This context provides all the necessary details to select or customize the appropriate header policy for each request.
With endpoint-specific policies and the customization options in SetPolicySelector()
, users can now easily tailor security header policies for different requests, without needing to modify the library's internals.
If you want to restore the "document header" functionality, you can recreate it using SetPolicySelector()
.
var builder = WebApplication.CreateBuilder(args); // The mime types considered "documents" string[] documentTypes = [ "text/html", "application/javascript", "text/javascript" ]; var documentPolicy = new HeaderPolicyCollection().AddDefaultSecurityHeaders(); builder.Services.AddSecurityHeaderPolicies() .SetDefaultPolicy(p => p.AddDefaultApiSecurityHeaders()) .SetPolicySelector(ctx => { // If the response is one of the "document" types... if (documentTypes.Contains(ctx.HttpContext.Response.ContentType)) { // ... then return the "document" policy return documentPolicy; } // Otherwise return the original selected policy return ctx.SelectedPolicy; }); var app = builder.Build(); app.UseSecurityHeaders(); app.MapGet("/api", () => "Hello world"); app.Run();
Adding security headers to HTTP responses helps strengthen your application against attacks. NetEscapades.AspNetCore.SecurityHeaders simplifies this process and has recently undergone significant updates.
- Content Negotiation in Web API
- How to fix 'InvalidOperationException: Scheme already exists: Bearer'
- How to fix System.InvalidOperationException: Scheme already exists: Identity.Application
- Add Thread ID to the Log File using Serilog
- Handling Exceptions in .NET Core API with Middleware
- InProcess Hosting in ASP.NET Core
- Limits on ThreadPool.SetMinThreads and SetMaxThreads
- Controlling DateTime Format in JSON Output with JsonSerializerOptions