Harden Content Security Policy with Nonces
Drop unsafe-inline, generate a per-response nonce for legitimate scripts, and report violations so a CSP actually blocks injected scripts.
Task
Make a Content Security Policy effective by replacing unsafe-inline with nonces.
When to use this
A CSP is deployed but includes unsafe-inline, so any injected script still runs and the policy provides no XSS protection.
Prerequisites
- Control over HTML rendering to inject nonces
- A report endpoint for violations
Steps
1. Generate a random nonce per response and add it to the script tags you control.
Expected output: Only your scripts carry the valid nonce.
2. Set a policy without unsafe-inline that requires the nonce for scripts.
Expected output: Injected inline scripts are blocked.
3. Add report-uri or report-to so violations are collected.
Expected output: You can see what would break before enforcing.
4. Roll out in report-only mode first, fix violations, then enforce.
curl -sI https://app.example.com | grep -i content-security-policyExpected output: Enforcement does not break the site.
Troubleshooting
Problem
Site breaks on enforcement
Solution
Run report-only first and fix inline handlers and third-party scripts before switching to enforce.
Problem
Legacy inline handlers
Solution
Replace onclick attributes with listeners; nonces do not apply to inline event handlers.
Limitations
- Nonces require per-response HTML generation and do not work with static HTML unless combined with hashes.
- A strict CSP can conflict with third-party widgets that inject their own scripts.
Context
Make a Content Security Policy effective by replacing unsafe-inline with nonces. A CSP is deployed but includes unsafe-inline, so any injected script still runs and the policy provides no XSS protection.
Steps
- Generate a random nonce per response and add it to the script tags you control. Expected: Only your scripts carry the valid nonce.
- Set a policy without unsafe-inline that requires the nonce for scripts. Expected: Injected inline scripts are blocked.
- Add report-uri or report-to so violations are collected. Expected: You can see what would break before enforcing.
- Roll out in report-only mode first, fix violations, then enforce. Expected: Enforcement does not break the site.
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy',
`default-src 'self'; script-src 'self' 'nonce-${nonce}'; object-src 'none'; base-uri 'none'`);
Verify
- Only your scripts carry the valid nonce.
- Injected inline scripts are blocked.
- You can see what would break before enforcing.
- Enforcement does not break the site.
Limits
- Nonces require per-response HTML generation and do not work with static HTML unless combined with hashes.
- A strict CSP can conflict with third-party widgets that inject their own scripts.