Script Valley
Web Security Fundamentals for Developers
Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF)Lesson 3.2

Content Security Policy: how to block XSS at the browser level

CSP directives, script-src nonce, hash-based CSP, unsafe-inline risks, CSP reporting, report-uri, strict CSP, CSP level 3

Content Security Policy (CSP)

CSP enforcement diagram

CSP is a browser-enforced whitelist of sources from which scripts, styles, images, and other resources may load. A well-configured CSP blocks XSS execution even when an injection flaw exists in your code.

Building a Strict CSP

// Strict CSP using nonces — most effective approach
app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.nonce = nonce;

  res.setHeader('Content-Security-Policy',
    `default-src 'self'; ` +
    `script-src 'nonce-${nonce}' 'strict-dynamic'; ` +
    `style-src 'self'; ` +
    `img-src 'self' data:; ` +
    `object-src 'none'; ` +
    `base-uri 'none'; ` +
    `report-uri /csp-report`
  );
  next();
});

// In your HTML template:
// 

Why 'unsafe-inline' Defeats the Purpose

Adding 'unsafe-inline' to script-src allows any inline script to execute, which is exactly what XSS payloads use. A CSP with 'unsafe-inline' provides almost no XSS protection. Use nonces instead—a unique per-request token added to your own script tags.

CSP Reporting

// Catch violations without blocking (useful for initial deployment)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

// Log the violation report
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.error('CSP Violation:', req.body);
  res.sendStatus(204);
});

Deploy in report-only mode first, review violations for legitimate content, then switch to enforcement mode.

Up next

How CSRF attacks work: forging requests with victim's cookies

Sign in to track progress