Proposal: Implement nonce provider interface
The issue at play here is that I'm writing code with razor (blazor) components that requires nonce generation. Your library exposes a single helper extension method called "GetNonce()" on HttpContext. However, under CSP nonce values can be declared separately for both script and style inline tags and they can be different.
My proposal is to offload concerns for how nonce values are both generated as well as stored within HttpContext to a scoped provider service that implements INonceProvider. It would have a method to retrieve nonce values that can be differentiated by type (so that the scoped service can be reused throughout third party libraries).
I also utilize HTMX for partial page updates, which presents additional challenges of needing to reuse nonce values from the original document request. My approach is to generate signed nonces that can be resent back to the server (via headers in request) to be reused on partial updates that will be merged within the current document. The nonces just need to be validated on the server side and then the nonce provider can reuse them within htmx requests. This is an implementation detail of a custom INonceProvider that doesn't have to impact your library at all. Just be sure to register the INonceProvider scoped service using services.TryAddScoped() and that would allow any custom provider implementation.
The types can be a simple enum:
/// <summary>
/// Defines the different types of nonces used in Content Security Policies.
/// </summary>
public enum NonceType
{
///<summary>
/// Nonce for unspecified usage (default if nonce type not provided).
///</summary>
Unspecified,
/// <summary>
/// Nonce for inline script tags.
/// </summary>
Script,
/// <summary>
/// Nonce for inline style tags.
/// </summary>
Style
}
The provider interface would look like this:
/// <summary>
/// Service interface for providing CSP nonce values.
/// </summary>
public interface INonceProvider
{
/// <summary>
/// Retrieves (or generates) the nonce value for a given nonce type.
/// </summary>
/// <param name="nonceType">The type of nonce required.</param>
/// <returns>A string representing the nonce for the requested type.</returns>
string GetNonceFor(NonceType nonceType);
}
Then provide a base implementation of INonceProvider that can use HttpContextAccessor to get the current context and cache the generated nonce.
You can still keep the HttpContext extension method but just get the INonceProvider service from httpContext and call it's method instead. You could use an optional parameter to allow for more specificity on which nonce to retrieve (defaulting to NonceType.Unspecified).
Thoughts?
Interesting 🤔 I need to digest this further, but my first thoughts are:
- Why use separate CSPs for script/style? 🤔 I don't the security benefit to doing so?
- You say it's difficult to "reuse" nonce values... the whole point of nonce values is that you never reuse them, right? "nonce" means "number used once" 😄
- If you did want to take this approach, I wonder if you could already build it with the existing primitives (e.g. creating your own
WithNonce()implementation)... I might have a play and see.
I don't see a technical reason not to do this per se, it's more ideological/security questions I have 🙂
@damienbod, I'm always interested in your thoughts on these sorts of questions 😄
For third-party use I can inject the nonce provider wherever I have a component that is going to need a nonce and retrieve the generated nonce for either scripts or styles. This is akin to what you already have implemented with tag helpers, which aren't available inside of razor components. My proposal with an INonceProvider is an attempt to encapsulate nonce generation and caching in a consistent, reusable way. It would also offer a standardized extension point for those who need to support more advanced scenarios like signed nonces for partial updates.
Using separate CSPs for script/styles could be used in cases where you want to allow scripts from your own domain and perhaps styles from a CDN. Because you can differentiate use cases with different nonces according to CSP spec may be a solid enough reason to add support for it even if in all practicality you might be fine with a single nonce. I suppose it depends on the developer.
I understand that “nonce” implies a number used once, and in a traditional sense, nonces aren’t meant to be reused. However, in the context of HTMX partial updates, the challenge is slightly different. The scenario involves reusing the nonce from the original document for inline elements rendered during partial updates—not reusing a nonce across unrelated requests, but rather maintaining consistency within a single document’s lifecycle. The “reuse” here is about carrying forward a valid nonce so that the inline updates (via htmx only) are still compliant with the CSP, while still ensuring that the nonce is unique per full request. However, this is more an implementation detail of my own library. I'm proposing opening up generation to a scoped service with a simple interface to provide this flexibility.
Maybe a custom WithNonce() implementation could be a workable solution. If the primitives already allow for a similar pattern without introducing extra complexity, that might be a simpler route. I'm wondering if providing a dedicated nonce provider creates a better separation of concerns.
Maybe something like this, implementing the proposed solution?
public static class CustomCspDirectiveBuilderExtensions
{
public static T WithNonce<T>(this T builder, NonceType nonceType = NonceType.Unspecified)
where T : CspDirectiveBuilder
{
builder.SourceBuilders.Add(ctx =>
{
var nonceProvider = ctx.RequestServices.GetService(typeof(INonceProvider)) as INonceProvider;
if (nonceProvider is null)
{
throw new InvalidOperationException("INonceProvider service is not registered.");
}
// Retrieve or generate the nonce based on the provided nonce type.
var nonce = nonceProvider.GetNonceFor(nonceType);
return $"'nonce-{nonce}'";
},
$"{typeof(T).FullName}.{nameof(WithNonce)}");
return builder;
}
}
I understand that “nonce” implies a number used once, and in a traditional sense, nonces aren’t meant to be reused.
The point I'm trying to make is that this isn't just "traditionally they shouldn't be reused", it's "to provide any security benefit, nonces must not be reused". I was reading a bit more about how HTMX handles this and AFAICT, HTMX basically just trashes any protections nonces would provide you... Honestly, I don't even know if it's worth you trying to make that work 🤔
not reusing a nonce across unrelated requests, but rather maintaining consistency within a single document’s lifecycle
To be clear, you are reusing the nonce across requests. You can't redefine what "request" means to justify it 😄 If you're going to send the nonce in the request, you've just undermined the security, because any malicious script could use that value too.
I'm wondering if providing a dedicated nonce provider creates a better separation of concerns.
It's possible, I'm not completely against it. But doing a service-location lookup of INonceProvider in the WithNonce() code might have more of a perf implication than would be ideal, so we might want to do more of a refactoring to support it in that case.
My main pushback on this really is that the scenario you're describing (trying to "share" nonces between requests) doesn't sound like a good use case to me. I'm loathe to expose more of the internals (e.g. an INonceProvider) than is strictly necessary, as it inevitably causes support and config issues later.
The scenario of having different nonces for style and script sounds reasonable but also I can't see anyone doing this in practice. The example you suggest of the CDN needing a different nonce doesn't work, because by definition it can't use the nonce 🤔
FWIW, (even though I think you shouldn't 😉) I think you could already workaround your issue today yourself, with a custom bit of middleware, placed just before the security headers, that grabs the nonce however you want it, and sets the value for the request:
public class NonceMiddleware : IMiddleware
{
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// get the nonce from somewhere
context.Items["NETESCAPADES_NONCE"] = "some nonce";
return next(context);
}
}
Just my 2 cents, I don't really see any benefits of this, maybe I miss something, but one nonce per request for both scripts and styles seems good to me. Re-using nonces over multiple requests whether partial or not is not a good idea.
Without generating unique style or script nonces I'm fine with withdrawing this proposal. I can just use a nonce provider implementation on my end to allow compatibility with libraries like yours.