How to Use Content Security Policy Level 3
Configure a strict Content Security Policy (CSP3) with nonces and hashes to prevent XSS, data exfiltration, and code injection attacks.
Problem
Your web application is vulnerable to Cross-Site Scripting (XSS) because inline scripts and external resources can be injected by attackers. You need to implement a strict Content Security Policy (CSP Level 3) that whitelists only trusted sources.
Prerequisites
- Understanding of XSS attacks and injection vectors
- Familiarity with HTTP headers and browser security policies
- Knowledge of script nonces and subresource integrity
Steps
-
1. Start with a restrictive default policy
Begin with the most restrictive policy: default-src 'none'. This blocks everything by default. Then explicitly allow only what each resource type needs. This deny-by-default approach is more secure than allow-by-default. Use the HTML sanitizer to understand what content needs to be allowed. Start with: Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'self'
Inputdefault-src 'none'; script-src 'self'; style-src 'self'Outputall external and inline resources blocked except same-origin scripts and styles -
2. Use nonces or hashes for inline scripts
CSP3 supports nonces (number used once) for inline scripts. Generate a unique random nonce per request, add it to the CSP header (script-src 'self' 'nonce-<random>'), and add the same nonce attribute to each inline script tag: <script nonce='<random>'. This is more secure than 'unsafe-inline'. Alternatively, use hashes: compute SHA256 of the inline script content and add 'sha256-<hash>' to script-src. Use the hash and base64 tools to compute script hashes.
Inputinline script: console.log('hello')Outputnonce: rAnd0mN0nc3 | CSP: script-src 'self' 'nonce-rAnd0mN0nc3' -
3. Add strict-dynamic and reporting
CSP3 'strict-dynamic' allows scripts loaded by trusted (nonced or hashed) scripts to also execute, simplifying third-party library support. Add a reporting directive: report-uri /csp-report or report-to endpoint. The browser sends violation reports to this endpoint without blocking, useful for monitoring before enforcing. Use Report-Only mode first (Content-Security-Policy-Report-Only) to identify violations without breaking the page.
Inputscript-src 'nonce-rAnd0mN0nc3' 'strict-dynamic'Outputtrusted scripts can load dependencies | violations reported to /csp-report -
4. Enforce and test the policy
Switch from Report-Only to enforcing mode once all violations are resolved. Test with XSS payloads to confirm they are blocked. Add frame-ancestors 'none' to prevent clickjacking (equivalent to X-Frame-Options: DENY). Add upgrade-insecure-requests to force HTTPS. Add require-trusted-types-for 'script' (CSP3 Trusted Types) to prevent DOM XSS via dangerous sinks. Use the HTML escape tool to verify proper output encoding complements CSP.
InputXSS payload: <script>alert(1)</script>Outputblocked by CSP | report: violated script-src 'nonce-...' directive
Expected result
A strict CSP Level 3 policy that prevents XSS, clickjacking, mixed content, and data exfiltration, with violation monitoring.
Common errors
- Using 'unsafe-inline' for scripts
- 'unsafe-inline' defeats CSP's XSS protection. Use nonces or hashes (CSP3) instead. If using nonces, 'unsafe-inline' is ignored by browsers anyway.
- Using 'unsafe-eval' for dynamic code
- 'unsafe-eval' allows eval() and new Function(), reintroducing XSS risk. Refactor to avoid dynamic evaluation, or use CSP3 'strict-dynamic' with trusted scripts.
- Deploying CSP in enforce mode without testing
- Start with Content-Security-Policy-Report-Only, collect violation reports, fix all violations, then switch to enforcing mode. Enforcing immediately can break the application.
Troubleshooting
- If third-party scripts are blocked, either add their domains to script-src or use 'strict-dynamic' with a nonced loader script that loads them.
- If inline styles break, use nonces for style-src as well, or move styles to external stylesheets.
- For single-page applications, ensure the CSP applies to all routes and that dynamic imports are covered by the script-src policy.
Security limits
- CSP is defense-in-depth — it does not replace proper output encoding and input validation. Both are needed.
- CSP does not protect against CSS-based data exfiltration in all cases — use 'style-src' restrictions carefully.
- All policy generation and nonce generation happen server-side; the browser enforces the policy locally.
Related tools
Related glossary
Related tasks
Ready to run it?