Content Security Policy (CSP): What It Is and How It Works
Content Security Policy (CSP) is a browser security mechanism that helps prevent attacks like Cross-Site Scripting (XSS) by restricting which resources (scripts, styles, images) can be loaded and executed on a web page using HTTP headers.
Content Security Policy: Protecting Web Applications from Code Injection
Content Security Policy (CSP) is a powerful security mechanism that helps prevent cross-site scripting (XSS), clickjacking, and other code injection attacks. It works by allowing website owners to declare which sources of content are trusted and which should be blocked. When implemented correctly, CSP acts as a whitelist for resources that can be loaded and executed on your website, significantly reducing the attack surface available to malicious actors.
CSP was introduced to address one of the most persistent and dangerous web vulnerabilities: cross-site scripting. Instead of relying solely on input validation and output encoding, CSP provides a defense-in-depth layer that can stop many XSS attacks even if other defenses fail. To understand CSP properly, it is helpful to be familiar with concepts like XSS prevention, HTTP security headers, web security fundamentals, and CORS.
What Is Content Security Policy
Content Security Policy is a security standard that allows website owners to specify which sources of content are trusted. It is implemented through an HTTP response header or a meta tag that defines a set of directives. The browser then enforces these directives, blocking any resources that do not match the allowed sources.
- Whitelist-Based: CSP uses a whitelist model. Anything not explicitly allowed is blocked by default.
- Defense in Depth: CSP works alongside other security measures like input validation and output encoding.
- Browser Enforced: The browser is responsible for enforcing CSP rules, making it difficult to bypass.
- Reporting: CSP can report violations to a specified endpoint, allowing monitoring of attempted attacks.
- Policy Language: Directives define allowed sources for different resource types (scripts, styles, images, etc.).
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src https: data:; report-uri /csp-report-endpoint
Why CSP Matters
Content Security Policy addresses critical security vulnerabilities that have plagued the web for decades. Its importance has grown as web applications become more complex and reliant on third-party resources.
- XSS Mitigation: CSP is the most effective defense against cross-site scripting attacks, even if other defenses fail.
- Data Injection Prevention: Blocks malicious scripts injected through user input, third-party libraries, or compromised ad networks.
- Clickjacking Protection: The frame-ancestors directive prevents your site from being embedded in malicious frames.
- Resource Integrity: Combined with subresource integrity (SRI), CSP ensures third-party scripts haven't been tampered with.
- Monitoring and Auditing: CSP violation reports provide visibility into attacks and misconfigurations.
- Compliance: Many security standards and regulations require or recommend CSP implementation.
- Browser Security Features: CSP enables other security features like disabling eval() and inline script execution.
CSP Directives Overview
CSP directives control different types of resources. Understanding each directive helps you build effective policies.
| Directive | Controls | Example |
|---|---|---|
| default-src | Fallback for other directives (if not specified) | default-src 'self' |
| script-src | JavaScript sources | script-src 'self' 'unsafe-inline' https://cdn.example.com |
| style-src | CSS sources | style-src 'self' 'unsafe-inline' |
| img-src | Image sources | img-src https: data: |
| font-src | Font sources | font-src https://fonts.googleapis.com |
| connect-src | AJAX, WebSocket, EventSource connections | connect-src 'self' https://api.example.com |
| frame-src | iframe sources (replaced by child-src) | frame-src https://youtube.com |
| frame-ancestors | What sites can embed your page (clickjacking protection) | frame-ancestors 'none' |
| report-uri | Endpoint for violation reports | report-uri /csp-violation-endpoint |
CSP Source Values
CSP source values specify which origins are allowed. Understanding these keywords is essential for crafting effective policies.
- 'self': Allows resources from the same origin (same scheme, host, and port).
- 'none': Blocks all resources of the specified type.
- 'unsafe-inline': Allows inline script or style (generally discouraged for security).
- 'unsafe-eval': Allows eval() and similar functions (strongly discouraged).
- 'strict-dynamic': Allows scripts loaded by trusted scripts to execute.
- 'nonce-{base64}': Allows specific inline scripts with matching nonce attribute.
- 'sha256-{hash}': Allows inline scripts with matching content hash.
- https://example.com: Allows resources from a specific domain.
- https: Allows resources from any HTTPS origin.
- data: Allows data: URIs (like images from data URLs).
# Self and a specific domain
script-src 'self' https://trusted-cdn.com
# HTTPS only for images
img-src https: data:
# None for frames (no iframes)
frame-src 'none'
# Nonce-based inline scripts
script-src 'nonce-abc123' 'strict-dynamic'
# Hash-based inline styles
style-src 'sha256-xyz789'
Implementing CSP: Step by Step
Implementing CSP requires careful planning and testing. A phased approach helps avoid breaking existing functionality.
- Start with Report-Only Mode: Use the Content-Security-Policy-Report-Only header to test policies without enforcement.
- Monitor Violations: Collect and analyze violation reports to identify legitimate resources being blocked.
- Gradually Tighten Policies: Start with a permissive policy and progressively restrict allowed sources.
- Avoid unsafe-inline and unsafe-eval: These keywords significantly reduce CSP effectiveness.
- Use Nonces or Hashes: For required inline scripts, use nonce or hash-based whitelisting.
- Implement in Production: Switch from report-only to enforcement once policies are verified.
- Regularly Review: CSP policies should evolve with your application to maintain security.
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report-endpoint
# Server response includes both enforcement and report-only
Content-Security-Policy: default-src 'self'
Content-Security-Policy-Report-Only: default-src 'https:'
Using Nonces for Inline Scripts
Nonces (number used once) are a secure way to allow specific inline scripts while blocking all others. Each request generates a unique nonce value that must match between the CSP header and the script tag.
// Server-side (PHP)
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce'");
// In HTML template
<script nonce="<?= $nonce ?>">
console.log('This inline script is allowed');
</script>
// Dynamically generated nonce changes with each request
// Injected scripts without the correct nonce are blocked
Using Hashes for Inline Scripts
Hashes are useful for static inline scripts that do not change. You compute the SHA-256 hash of the script content and include it in the CSP header.
// Calculate script hash
// Script content: console.log('Hello World');
// SHA-256 hash: o7LJ... (example)
// CSP header
Content-Security-Policy: script-src 'sha256-o7LJ...'
// HTML
<script>
console.log('Hello World'); // Allowed
</script>
// Any modification to the script changes the hash and blocks execution
strict-dynamic Directive
The strict-dynamic directive simplifies CSP management for modern applications that load scripts dynamically. It allows scripts loaded by trusted scripts to execute without being explicitly whitelisted.
# CSP header with strict-dynamic
Content-Security-Policy: default-src 'self'; script-src 'nonce-abc123' 'strict-dynamic' 'unsafe-inline'; object-src 'none'
# HTML with trusted script
<script nonce="abc123" src="/main.js"></script>
# main.js can dynamically load additional scripts
const script = document.createElement('script');
script.src = '/dynamic.js';
document.head.appendChild(script); // Allowed by strict-dynamic
# strict-dynamic requires either a nonce or hash to work
CSP for Different Application Types
Different application architectures require different CSP approaches. Tailoring your policy to your application type improves both security and functionality.
# Use nonces for inline scripts
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
img-src 'self' https:;
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none'
# SPA with build-time hash for main bundle
Content-Security-Policy:
default-src 'self';
script-src 'sha256-{BUNDLE_HASH}' 'strict-dynamic';
style-src 'self';
connect-src 'self' https://api.example.com;
img-src 'self' data: https:;
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none'
# Allow trusted CDNs
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com https://js.stripe.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' https://images.example.com https:;
connect-src 'self' https://api.example.com;
frame-src https://js.stripe.com;
object-src 'none'
CSP Violation Reporting
Monitoring CSP violations is essential for maintaining security and identifying attacks. Violations can be reported to a server endpoint for analysis.
# CSP header with report-uri
Content-Security-Policy: default-src 'self'; report-uri /api/csp-report
# Violation report format (JSON)
{
"csp-report": {
"document-uri": "https://example.com/page",
"referrer": "https://attacker.com",
"blocked-uri": "https://evil.com/script.js",
"violated-directive": "script-src 'self'",
"effective-directive": "script-src",
"original-policy": "default-src 'self'",
"disposition": "enforce",
"script-sample": "eval('malicious code')",
"status-code": 200
}
}
// Server-side endpoint (Node.js example)
app.post('/api/csp-report', (req, res) => {
console.log('CSP Violation:', req.body);
// Log to monitoring system, alert on suspicious patterns
res.status(204).send();
});
CSP and Subresource Integrity (SRI)
Combining CSP with Subresource Integrity provides strong protection for third-party resources. SRI ensures that fetched resources match a cryptographic hash, preventing tampering.
# CSP allows any HTTPS script (less secure)
Content-Security-Policy: script-src https:;
# HTML with SRI ensures script integrity
<script src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
# Even if CDN is compromised, SRI prevents execution
# of tampered scripts
Common CSP Mistakes to Avoid
Even experienced developers make mistakes when implementing CSP. Being aware of these common pitfalls helps you build effective policies.
- Using unsafe-inline and unsafe-eval: These keywords significantly weaken CSP. Avoid them when possible, using nonces or hashes instead.
- Overly Permissive Policies: Using 'self' everywhere or allowing 'https:' can make CSP ineffective against many attacks.
- Missing Report-Only Testing: Implementing enforcement policies without testing breaks functionality and frustrates users.
- Ignoring Violation Reports: Not monitoring CSP reports means missing both legitimate breakages and potential attacks.
- Blocking Inline Styles in Legacy Apps: Many older applications rely on inline styles. Consider using style-src 'unsafe-inline' only if absolutely necessary.
- Not Setting frame-ancestors: Without frame-ancestors, your site remains vulnerable to clickjacking attacks.
- Wildcard Domains: Using *.example.com allows all subdomains, some of which may be vulnerable.
Testing CSP Configuration
Several tools and techniques help test and validate CSP configurations before deployment.
- Google CSP Evaluator: Online tool that analyzes CSP policies for security weaknesses.
- Mozilla Observatory: Comprehensive security scanner that includes CSP testing.
- Browser Developer Tools: Console shows CSP violations and errors during development.
- Report-Only Mode: Test policies in production without enforcement to identify issues.
- Automated Testing: Include CSP validation in CI/CD pipelines.
// Using meta tag for local development (not recommended for production)
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-${nonce}'">
// Chrome DevTools console shows violations
// Example output:
// Refused to execute inline script because it violates the following
// Content Security Policy directive: "script-src 'self'".
// Firefox DevTools shows violations in Console panel with detailed information
CSP and Modern Web Frameworks
Modern web frameworks provide built-in support for CSP, making implementation easier.
// React with Helmet
import { Helmet } from 'react-helmet';
function App() {
return (
<Helmet>
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'nonce-${nonce}'" />
</Helmet>
);
}
// Express.js (Node.js)
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'nonce-{NONCE}'"],
styleSrc: ["'self'"],
imgSrc: ["'self'", "https:"],
connectSrc: ["'self'", "https://api.example.com"]
}
}));
// Django (Python)
MIDDLEWARE = [
'csp.middleware.CSPMiddleware',
]
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", "'nonce-{NONCE}'")
CSP_STYLE_SRC = ("'self'",)
CSP_IMG_SRC = ("'self'", "https:")
CSP_CONNECT_SRC = ("'self'", "https://api.example.com")
Frequently Asked Questions
- Does CSP completely prevent XSS attacks?
CSP significantly reduces XSS risk but does not eliminate it entirely. It is most effective when combined with input validation, output encoding, and other security measures. CSP provides defense in depth, making XSS exploitation much more difficult. - What is the difference between unsafe-inline and nonces?
unsafe-inline allows all inline scripts, making CSP ineffective against XSS. Nonces allow only specific inline scripts with matching nonce values, providing strong protection while allowing necessary inline code. - Can I use CSP with inline event handlers?
Inline event handlers like onclick="..." are blocked by CSP unless you use unsafe-inline or nonces. Modern best practices recommend using addEventListener instead of inline handlers for better CSP compatibility. - How do I handle dynamic script loading with CSP?
Use the strict-dynamic directive along with a nonce. This allows scripts loaded by trusted scripts to execute without being explicitly whitelisted, simplifying CSP management for dynamic applications. - What is the difference between CSP and CORS?
CSP controls which resources can be loaded and executed on your page. CORS controls which origins can access your API from other domains. Both are important security mechanisms but serve different purposes. - What should I learn next after understanding CSP?
After mastering CSP, explore security headers like HSTS, X-Frame-Options, and X-Content-Type-Options. Also study XSS prevention techniques, Subresource Integrity (SRI), and CORS for comprehensive web security.
Conclusion
Content Security Policy is one of the most effective tools available for protecting web applications against cross-site scripting and other code injection attacks. By implementing a strict CSP, you can significantly reduce your application's attack surface and add a critical layer of defense that works even when other security measures fail.
Implementing CSP requires careful planning and testing. Start with report-only mode, monitor violations, and gradually tighten policies. Avoid unsafe-inline and unsafe-eval keywords whenever possible, using nonces or hashes for necessary inline code. Combine CSP with other security headers and best practices for comprehensive protection.
The web security landscape continues to evolve, and CSP remains a cornerstone of modern web application security. As browsers add more CSP features and refine implementation, the ability to control resource loading becomes increasingly powerful. Investing time in understanding and implementing CSP pays dividends in reduced vulnerability to XSS, improved compliance posture, and greater confidence in your application's security.
To deepen your understanding, explore related topics like HTTP security headers for complete protection, XSS prevention techniques for complementary defenses, Subresource Integrity (SRI) for third-party resource protection, and CORS for controlling cross-origin access. Together, these skills form a complete foundation for building secure web applications.
