'Dot Net 5 CSP with a nonce - discrepancy between nonce in header and nonce in script tags

I have a .Net 5 site and would like to implement a CSP using a nonce for scripts. I register ICspNonceService in my Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...various services added
    services.AddContentSecurityPolicyNonce();
}

Then I add my headers with some middleware

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    var nonceService = app.ApplicationServices.GetService(typeof(ICspNonceService));
    app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder((ICspNonceService)nonceService).AddDefaultSecurePolicy().RemoveHeader("X-Powered-By"));

    ...various middleware
}

And in that middleware:

public SecurityHeadersBuilder AddContentSecurityPolicy()
{
    var policy = new List<string>();

    var contentSecurityPolicies = Enum.GetValues<ContentSecurityPolicy>();
    foreach (var contentSecurityPolicy in contentSecurityPolicies)
    {
        var value = contentSecurityPolicy.Value();
        if (!string.IsNullOrEmpty(value))
        {
            value = value.Replace("{nonceValue}", this._cspNonceService.GetNonce());
            policy.Add(value);
        }
    }

    this._policy.SetHeaders[ContentSecurityPolicyConstants.Header] = string.Join(';', policy.ToArray());
    return this;
}

And this produces my appropriate header:

default-src 'self'; script-src 'nonce-h5BUValX03OJFO165044o1gMNVUMsbhdYUvzpDATneU=' 'strict-dynamic'

Then I have a taghelper to add my nonce to a script tag:

[HtmlTargetElement("script", Attributes = "use-csp-nonce")]
public class NonceTagHelper : TagHelper
{
    private readonly ICspNonceService _nonceService;

    public NonceTagHelper(ICspNonceService nonceService) => this._nonceService = nonceService;


    [HtmlAttributeName("use-csp-nonce")]
    public bool UseNonce { get; set; }

    public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        if (this.UseNonce)
        {
            output.Attributes.SetAttribute("nonce", this._nonceService.GetNonce());
        }

        return base.ProcessAsync(context, output);
    }
}

Which I can use like this:

<script use-csp-nonce="true" src="/dist/js/main.min.js" asp-append-version="true" ></script>

This produces a script tag with a nonce, but it is not the same nonce as the header, so it all fails.

I have several theories:

  1. Something about the order of my services registrations
  2. The way I am injecting the ICspNonceService for the header

How do I ensure I get the same Nonce?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source