NetEscapades.AspNetCore.SecurityHeaders

repository·main·Indexed 21 days ago

https://github.com/andrewlock/netescapades.aspnetcore.securityheaders

A lightweight ASP.NET Core package to simplify the addition and customization of security-related HTTP response headers. It provides middleware for applying default security headers, support for Content-Security-Policy (CSP) and Permissions-Policy, and the ability to define named policies for specific endpoints. Optional TagHelpers are available for implementing nonces and auto-generated SHA256 hashes in Razor views.

Tokens
5.2K
Snippets
15
Records
25
Agent score
25%

What's inside NetEscapades.AspNetCore.SecurityHeaders

  1. Customize security headers using HeaderPolicyCollection

    main

    To customize headers, instantiate a HeaderPolicyCollection and use its helper methods to add or modify policies. You can also add arbitrary headers using AddCustomHeader.

    Common patterns include:

    • Using AddDefaultSecurityHeaders() as a base and then overriding specific headers.
    • Using the UseSecurityHeaders(Action<HeaderPolicyCollection> ...) overload for a more concise syntax.
    • Using RemoveServerHeader() to strip the Server header.
    public void Configure(IApplicationBuilder app)
    {
        // Option 1: Explicitly creating the collection
        var policyCollection = new HeaderPolicyCollection()
            .AddDefaultSecurityHeaders()
            .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 63072000);
    
        app.UseSecurityHeaders(policyCollection);
    
        // Option 2: Using the Action overload (more terse)
        app.UseSecurityHeaders(policies =>
            policies
                .AddDefaultSecurityHeaders()
                .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 63072000)
        );
    }
  2. Remove the Server header from responses

    main

    The RemoveServerHeader() method in the HeaderPolicyCollection may not be sufficient to remove the Server header because Kestrel often adds it late in the middleware pipeline.

    To reliably prevent Kestrel from adding the Server header, you should configure KestrelServerOptions in your Program.cs when constructing your WebHostBuilder.

    var host = new WebHostBuilder()
        .UseKestrel(options => options.AddServerHeader = false)
        //...
  3. Use default security headers in ASP.NET Core

    main

    To apply a set of safe, minimum default security headers to all responses, add the UseSecurityHeaders() middleware to your application pipeline.

    Important: The order of middleware matters. To ensure headers are applied to all requests, configure this middleware as early as possible in your Startup pipeline or WebApplication configuration.

    public void Configure(IApplicationBuilder app)
    {
        app.UseSecurityHeaders();
    
        // other middleware e.g. static files, MVC etc  
    }
  4. Apply default security headers for JSON API endpoints

    main

    If your API returns only JSON, you may not need the full set of security headers provided by AddDefaultSecurityHeaders(). You can use AddDefaultApiSecurityHeaders() to apply a more focused subset of headers recommended by OWASP.

    This method sets:

    • X-Content-Type-Options: nosniff
    • Strict-Transport-Security: max-age=31536000; (HTTPS only)
    • X-Frame-Options: Deny
    • Content-Security-Policy: default-src: none; frame-ancestors 'none'
    • Referrer-Policy: no-referrer
    • Permissions-Policy: A comprehensive list of disabled features (e.g., camera=(), geolocation=(), etc.)
    • Cross-Origin-Opener-Policy: same-origin
    • Cross-Origin-Embedder-Policy: require-corp
    • Cross-Origin-Resource-Policy: same-site
    public void Configure(IApplicationBuilder app)
    {
        var policyCollection = new HeaderPolicyCollection()
            .AddDefaultApiSecurityHeaders();
    
        app.UseSecurityHeaders(policyCollection);
    }
  5. Verify NuGet provenance attestations

    main

    To verify the provenance of NetEscapades NuGet packages, you must first remove the .signature.p7s file added by nuget.org to reconstruct the original package.

    1. Remove the signature file (Linux/macOS):

    file="path/to/NetEscapades.AspNetCore.SecurityHeaders.1.3.1.nupkg"
    zip -d $file .signature.p7s

    2. Remove the signature file (PowerShell):

    $file="path/to/NetEscapades.AspNetCore.SecurityHeaders.1.3.1.nupkg"
    [Reflection.Assembly]::LoadWithPartialName('System.IO.Compression')
    $stream = New-Object IO.FileStream($file, [IO.FileMode]::Open)
    $zip    = New-Object IO.Compression.ZipArchive($stream, [IO.Compression.ZipArchiveMode]::Update)
    $zip.Entries | ? { $_.Name -eq ".signature.p7s" } | % { $_.Delete() }
    $zip.Dispose();

    3. Verify with GitHub CLI:

    gh attestation verify --owner andrewlock "NetEscapades.AspNetCore.SecurityHeaders.1.3.1.nupkg"

    4. Verify SBOM attestations:

    gh attestation verify --owner andrewlock --predicate-type https://cyclonedx.org/bom "NetEscapades.AspNetCore.SecurityHeaders.1.3.1.nupkg"
  6. Install NetEscapades.AspNetCore.SecurityHeaders.TagHelpers

    main

    To use nonces and auto-generated hashes with Content-Security-Policy (CSP) in Razor views, you must install the NetEscapades.AspNetCore.SecurityHeaders.TagHelpers NuGet package in addition to the core library.

    dotnet package add Install-Package NetEscapades.AspNetCore.SecurityHeaders.TagHelpers
  7. How to use auto-generated hashes in Razor views

    main

    To allow-list inline content using SHA256 hashes, use the asp-add-content-to-csp attribute on <script> or <style> tags.

    • For <script> and <style> tags, the attribute automatically calculates the hash of the element's content and adds it to the CSP header.
    • You can specify the hash algorithm using the csp-hash-type attribute (options: SHA256, SHA384, SHA512).
    <script asp-add-content-to-csp>
        var msg = document.getElementById('message');
        msg.innerText = "I'm allowed";
    </script>
    
    <style asp-add-content-to-csp csp-hash-type="SHA384">
    #message {
        color: @color;
    }
    </style>
  8. How to allow-list inline attributes using TagHelpers

    main

    Since inline styles and event handlers do not support nonces, use the AttributeHashTagHelper. Apply the asp-add-csp-for-* attribute to an element, where * is the name of the attribute you want to hash (e.g., style or onclick).

    • Example: asp-add-csp-for-style for style attributes.
    • Example: asp-add-csp-for-onclick for onclick attributes.
    • You can specify the hash type directly on the attribute (e.g., asp-add-csp-for-onclick="SHA384").
    <h3 asp-add-csp-for-style style="color: red">I will be styled red</h3>
    
    <button asp-add-csp-for-style style="color: red" asp-add-csp-for-onclick="SHA384" onclick="alert('Hello!')">Click me!</button>
  9. Customize security headers per request using SetPolicySelector

    main

    For scenarios like multi-tenancy where headers must change based on request data (e.g., a tenant ID in a header), use SetPolicySelector() on IServiceCollection.AddSecurityHeaderPolicies().

    This method accepts a Func<PolicySelectorContext, IReadOnlyHeaderPolicyCollection>. The selector is invoked for every request.

    Best Practice: Avoid creating a new HeaderPolicyCollection from scratch on every request. Instead, cache policies or use IReadOnlyHeaderPolicyCollection.Copy() to create a mutable copy of an existing policy to modify.

    builder.Services.AddSecurityHeaderPolicies()
        .AddPolicy("TenantPolicy", p => p.AddCustomHeader("X-Tenant", "Default"))
        .SetPolicySelector((PolicySelectorContext ctx) =>
        {
            // Access DI services via HttpContext
            var services = ctx.HttpContext.RequestServices;
            var selector = services.GetRequiredService<TenantHeaderPolicyCollectionSelector>();
            var tenant = services.GetRequiredService<ITenant>();
            
            return selector.GetPolicy(tenant);
        });
  10. How to use Nonces in Razor views

    main

    To use a per-request nonce, follow these steps:

    1. Add the TagHelpers to your _ViewImports.cshtml: @addTagHelper *, NetEscapades.AspNetCore.SecurityHeaders.TagHelpers
    2. Use the asp-add-nonce attribute on <script> or <style> tags. The TagHelper will automatically attach the required nonce="..." attribute to the element at runtime.
    <script asp-add-nonce>
        var body = document.getElementsByTagName('body')[0];
        var div = document.createElement('div');
        div.innerText = "I was added using the NonceHelper";
        body.appendChild(div);
    </script>