Even with secure cookies, input validation, and authentication, a website may still be susceptible to cross-site scripting (XSS). One explanation is that JavaScript that an attacker is able to insert into a website may be executed by browsers.
Think about a program that shows comments left by users. An attacker may insert a script that runs in the browser of another user if the program improperly handles untrusted input. This might reveal private data or act as the impacted user, depending on the program and its security measures.
A Content Security Policy (CSP) helps reduce this risk by telling the browser which scripts and other resources it is allowed to load or execute. A nonce-based CSP takes this further by allowing specific inline scripts through a unique value generated for each HTML response.
This approach is useful for applications that need inline JavaScript but want to avoid allowing every inline script to run.
In this article, you will learn how CSP nonces work, how to configure them, how to use them in an ASP.NET Core application, and how to test the policy without breaking your website.
What Is a Content Security Policy?
Content Security Policy is a browser security mechanism that restricts the sources and types of content a page can load or execute.
A server normally sends CSP through the Content-Security-Policy HTTP response header. The browser reads the policy and applies its rules to supported resources.
For example:
Content-Security-Policy: default-src 'self'; script-src 'self'This policy uses the current origin as the default permitted source and allows scripts from that origin. It does not permit arbitrary inline scripts through the script-src directive.
That distinction matters because inline JavaScript can be a common route for XSS attacks.
Without a suitable policy, a page might contain:
<script>
alert("Inline script executed");
</script>A restrictive CSP blocks this inline script unless the policy explicitly permits it through a mechanism such as a nonce or hash, or through another applicable exception.
The exact behavior depends on the directives and browser support. CSP is a defense-in-depth control, not a replacement for safe output encoding, input handling, or secure application design.
What Is a CSP Nonce?
A nonce is a cryptographically random value generated by the server and included in both the CSP header and the HTML elements that are allowed to execute.
The term nonce means “number used once.” For CSP, the important requirements are that the value be unpredictable and newly generated for each HTML response.
Suppose the server generates this nonce:
r4nd0mExampleValueThe response header might contain:
Content-Security-Policy: script-src 'self' 'nonce-r4nd0mExampleValue'The HTML can then include an approved inline script:
<script nonce="r4nd0mExampleValue">
initializeApplication();
</script>The browser checks the script element against the policy. If the nonce matches the value in the policy, the script is permitted by that nonce source.
An inline script without the matching nonce is blocked by this policy unless another applicable rule allows it.
The example value above is illustrative only. A production nonce must be generated securely at runtime.
Why Nonces Are Better Than Allowing All Inline Scripts
A common way to make inline JavaScript work under CSP is to add 'unsafe-inline' to script-src.
For example:
Content-Security-Policy: script-src 'self' 'unsafe-inline'This weakens the protection offered by the script policy because it allows inline scripts without requiring each one to carry an approved nonce or hash. In some older browser implementations, the interaction between nonce sources and 'unsafe-inline' also differs from modern CSP behavior.
A nonce-based policy provides a more controlled option.
Approach | How it works | Security consideration |
|---|---|---|
| Permits inline scripts under the applicable policy | Broadly permits inline script execution |
Nonce | Permits scripts carrying a matching unpredictable value | Requires server-side generation and HTML integration |
Hash | Permits inline script content matching a configured cryptographic hash | Requires updating the policy when the script content changes |
External scripts only | Restricts scripts to approved sources | Requires careful source control and may not suit every application |
A nonce is not automatically safer in every implementation. If an attacker can inject arbitrary HTML and also obtain a valid nonce from the response, the protection may be undermined. The server must keep the nonce unpredictable and ensure that trusted templates, rather than untrusted user input, determine which scripts receive it.
How to Generate a Secure Nonce in ASP.NET Core
ASP.NET Core applications can generate a nonce using the cryptographic random number generator provided by .NET.
The following example creates a middleware component that generates a new nonce for each HTTP request and makes it available to downstream components.
using System.Security.Cryptography;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
var nonceBytes = RandomNumberGenerator.GetBytes(32);
var nonce = Convert.ToBase64String(nonceBytes);
context.Items["CspNonce"] = nonce;
await next();
});
app.Run();This example generates 32 random bytes and encodes them as Base64. It stores the result in HttpContext.Items, allowing downstream middleware or application code to access it during the same request.
The example does not yet send a CSP header. It only generates and stores the nonce.
In a real application, ensure that the nonce is generated before any component that needs it, and generate it for every HTML response that uses the nonce-based policy.
Avoid placing a fixed nonce in application configuration or source code. A fixed value would defeat the purpose of generating a fresh, unpredictable nonce.
Add the Nonce to the CSP Response Header
Once the nonce is available, the application can include it in the response policy.
For example, the middleware can be extended as follows:
using System.Security.Cryptography;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
var nonce = Convert.ToBase64String(
RandomNumberGenerator.GetBytes(32));
context.Items["CspNonce"] = nonce;
context.Response.OnStarting(() =>
{
context.Response.Headers["Content-Security-Policy"] =
$"default-src 'self'; " +
$"script-src 'self' 'nonce-{nonce}'; " +
$"object-src 'none'; " +
$"base-uri 'self'";
return Task.CompletedTask;
});
await next();
});
app.MapGet("/", () => Results.Content(
"<!doctype html><html><body><h1>Welcome</h1></body></html>",
"text/html"));
app.Run();This example demonstrates nonce generation and response-header construction. It does not include an inline script in the returned HTML, so there is no script element that uses the nonce yet.
The policy restricts scripts to the current origin and scripts carrying the generated nonce. It also blocks plugin-based objects and restricts the base URL used to resolve relative URLs.
The policy is intentionally small. A production website may need additional directives for stylesheets, images, fonts, API connections, frames, and other resources. Those directives should reflect the application's actual requirements rather than allowing every source by default.
Use the Nonce in Razor Views
An ASP.NET Core MVC or Razor Pages application can retrieve the nonce from HttpContext.Items and add it to trusted inline script elements.
For example, in a Razor view:
@{
var nonce = Context.Items["CspNonce"] as string;
}
<!DOCTYPE html>
<html>
<head>
<title>CSP Nonce Example</title>
</head>
<body>
<h1>Welcome</h1>
<script nonce="@nonce">
console.log("Approved inline script executed.");
</script>
</body>
</html>The Razor view reads the nonce generated by the middleware and adds it to the script element.
The response header and the HTML element must use exactly the same nonce for that response. Razor's normal HTML encoding should remain enabled; do not disable encoding to insert the value.
Also, only trusted application templates should receive the nonce. Do not automatically attach it to every script element in user-submitted HTML or other untrusted content.
For applications with many views, consider creating a reusable helper, tag helper, or view component so developers do not need to handle nonce retrieval independently in every template.
Understand the Important CSP Directives
A nonce-based policy normally works alongside other CSP directives.
Directive | Purpose | Example |
|---|---|---|
| Provides a fallback for several resource types |
|
| Controls permitted JavaScript sources and execution |
|
| Controls stylesheet sources and inline styles |
|
| Controls image sources |
|
| Controls script-initiated connections such as fetch and WebSocket connections |
|
| Controls plugin-based content |
|
| Restricts URLs used by the HTML base element |
|
| Controls which sites may embed the page |
|
These examples are starting points, not a complete policy for every application.
A directive such as default-src 'self' does not replace the need to define and test important resource-specific rules. Also, frame-ancestors must be delivered through an appropriate CSP response header; it is not supported through a CSP meta element.
Test the Policy Before Enforcing It
A strict CSP can block scripts that your application legitimately needs. Third-party analytics, payment widgets, embedded content, and frontend libraries may depend on resources that are not covered by the initial policy.
A safer rollout starts with report-only mode.
For example:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-example'; object-src 'none'; base-uri 'self'This header reports policy violations without enforcing the policy. The example nonce is a placeholder and should not be copied into a production configuration.
Review browser developer tools and any configured CSP reporting destination for violations. Update the policy based on verified application requirements, then move to an enforcing Content-Security-Policy header.
Report-only mode is useful for discovering compatibility problems, but it does not protect users from the violations being reported.
Common Problems and Their Solutions
Problem | Likely cause | What to check |
|---|---|---|
Inline script is blocked | Missing or mismatched nonce | Compare the response header and script element |
Scripts stop working after deployment | Required source or script was omitted from the policy | Review browser CSP violation messages |
Nonce appears unchanged across requests | The value is cached or generated outside the request flow | Generate a new nonce for each HTML response |
Third-party widget fails | The widget needs additional permitted resources or scripts | Identify its actual dependencies before changing the policy |
Page works in report-only mode but fails when enforced | Report-only mode does not block violations | Resolve the reported violations before enforcement |
Cached HTML contains an old nonce | HTML and its CSP header are being cached or served independently | Ensure the cached response preserves a matching header and HTML pair, or avoid shared caching of nonce-bearing HTML |
Common Mistakes to Avoid
Reusing a Nonce
A nonce should be generated securely for each HTML response. Reusing a value across requests makes it less useful as a response-specific authorization token.
Adding a Nonce to Untrusted Scripts
A nonce should identify scripts that the application trusts. Adding it automatically to attacker-controlled markup can allow injected scripts to run.
Keeping 'unsafe-inline' Without a Clear Reason
Allowing all inline scripts can undermine the intended protection. Prefer nonces, hashes, or external scripts when practical.
Applying a Policy Without Testing
A policy that is too restrictive can break login flows, payment integrations, or frontend features.
Start with report-only mode, examine the actual violations, and enforce the policy after validating the required resources.
Treating CSP as the Only XSS Defense
CSP reduces the impact of certain script-injection attacks, but it does not make unsafe HTML rendering acceptable.
Use context-aware output encoding, safe DOM APIs, appropriate input validation, and secure framework defaults as well.
Best Practices for Production Applications
Generate nonces with a cryptographically secure random number generator.
Use a fresh nonce for every HTML response that relies on it.
Construct the CSP header and HTML using the same request-specific value.
Keep nonce assignment limited to trusted scripts.
Avoid
'unsafe-inline'unless a documented compatibility requirement justifies it.Use report-only mode to evaluate a policy before enforcement.
Review browser violations after deploying policy changes.
Be careful when caching HTML that contains nonces.
Define only the resource sources that the application actually needs.
Continue using output encoding and safe rendering practices to prevent XSS.
Advantages and Limitations of CSP Nonces
Advantages | Limitations |
|---|---|
Allows selected inline scripts without permitting all inline scripts | Requires server-side nonce generation and template integration |
Helps reduce the impact of certain XSS vulnerabilities | Does not replace safe output encoding or secure DOM handling |
Works with server-rendered applications such as ASP.NET Core Razor | Shared caching can create mismatches between HTML and headers |
Supports a more restrictive script policy | Third-party integrations may require additional configuration |
Can be introduced through a staged report-only rollout | Requires ongoing testing and policy maintenance |
Summary
Content Security Policy nonces let a website authorize selected inline scripts while restricting other script execution. The server generates an unpredictable value, includes it in the CSP response header, and places the same value on trusted script elements.
In ASP.NET Core, this can be implemented with cryptographic nonce generation, middleware, and Razor views. The important part is keeping the header and HTML synchronized while ensuring that untrusted content never receives an approved nonce.
Start with a policy that reflects your application's actual resource requirements, test it in report-only mode, and enforce it after reviewing violations. Combined with output encoding and safe DOM practices, a nonce-based CSP adds a useful layer of browser-side protection against XSS.
Best ASP.NET Core 11.0 Hosting Recommendation
At HostForLIFE, customers can also experience fast ASP.NET Core 11 hosting. The company invested a lot of money to ensure the best and fastest performance of the datacenters, servers, network and other facilities. Its datacenters are equipped with the top equipments like cooling system, fire detection, high speed Internet connection, and so on. That is why HostForLIFE guarantees 99.9% uptime for ASP.NET Core. And the engineers do regular maintenance and monitoring works to assure its Orchard hosting are security and always up.



0 comments:
Post a Comment