CSP-003 recommended general

Block inline scripts where possible

script-src avoids unsafe-inline or uses nonces/hashes, GTM strategy documented if used

Question to ask

"unsafe-inline — do you know what that actually allows?"

Verification guide

Severity: Recommended

Inline scripts (<script>alert('xss')</script>) are the primary XSS attack vector. Blocking them via CSP forces all JavaScript to come from trusted files, making XSS much harder to exploit.

Check automatically:

  1. Check script-src directive for unsafe-inline:
# Look for unsafe-inline in CSP (bad)
grep -rE "script-src[^;]*unsafe-inline" src/ app/ lib/ nginx/ *.conf 2>/dev/null

# Look for strict script-src (good)
grep -rE "script-src[^;]*'self'" src/ app/ lib/ 2>/dev/null
  1. Check for nonce-based CSP (allows specific inline scripts):
# Nonce generation
grep -rE "nonce|cspNonce|generateNonce" src/ app/ lib/ --include="*.ts" --include="*.js" 2>/dev/null

# Nonce in script tags
grep -rE "<script[^>]*nonce=" src/ app/ components/ pages/ --include="*.html" --include="*.tsx" --include="*.jsx" 2>/dev/null

# Next.js nonce support
grep -rE "nonce" next.config.* 2>/dev/null
  1. Check for hash-based CSP (allows specific inline script content):
# SHA hashes in CSP
grep -rE "'sha256-|'sha384-|'sha512-" src/ app/ lib/ nginx/ 2>/dev/null
  1. Check for inline scripts in HTML:
# Inline script tags (potential issues if CSP blocks inline)
grep -rE "<script>[^<]+" src/ app/ pages/ components/ --include="*.html" --include="*.tsx" --include="*.jsx" 2>/dev/null

# Inline event handlers (also blocked by strict CSP)
grep -rE "onclick=|onload=|onerror=|onmouseover=" src/ app/ components/ --include="*.html" --include="*.tsx" --include="*.jsx" 2>/dev/null
  1. Check for strict-dynamic (modern approach):
# strict-dynamic allows nonce-loaded scripts to load other scripts
grep -rE "'strict-dynamic'" src/ app/ lib/ nginx/ 2>/dev/null
  1. Check style-src for unsafe-inline:
# style-src with unsafe-inline (common for CSS-in-JS)
grep -rE "style-src[^;]*unsafe-inline" src/ app/ lib/ nginx/ 2>/dev/null
  1. Check for Google Tag Manager:
# GTM script tags
grep -rE "googletagmanager|GTM-" src/ app/ pages/ components/ 2>/dev/null

# GTM in CSP whitelist
grep -rE "googletagmanager\.com|tagmanager\.google\.com" src/ app/ lib/ nginx/ 2>/dev/null

Ask user:

  • "Do you use inline scripts or event handlers in your HTML?"
  • "Is your build pipeline configured to add nonces to script tags?"
  • "Do you use Google Tag Manager? What scripts does it load?"
  • "Do you use CSS-in-JS (styled-components, emotion, Tailwind)?"

Cross-reference with:

  • CSP-001 (full CSP headers - script-src is a key directive)
  • CSP-002 (report-only mode - shows what inline scripts would break)
  • API-005 (XSS prevention - this is the CSP enforcement layer)

Pass criteria:

  • script-src does NOT include 'unsafe-inline', OR
  • 'unsafe-inline' is present but with nonce/hash (nonce takes precedence)
  • Inline scripts use nonces or are moved to external files
  • No inline event handlers (onclick, onerror, etc.)
  • GTM strategy documented if GTM is used

Fail criteria:

  • script-src 'unsafe-inline' without nonce/hash fallback
  • Many inline scripts with no plan to externalize or add nonces
  • Inline event handlers throughout codebase
  • GTM in use with no CSP strategy

Notes on style-src 'unsafe-inline':

Often necessary for CSS-in-JS libraries (styled-components, emotion, MUI). Less dangerous than script-src unsafe-inline since CSS can't execute arbitrary code. Acceptable if:

  • Using CSS-in-JS that requires it
  • Consider nonce-based styles for stricter security

Google Tag Manager strategy:

GTM dynamically injects scripts, which conflicts with strict CSP. Options:

  1. Nonce propagation: Pass nonce to GTM, use Custom Templates that support nonces
  2. Server-side GTM: Run GTM server-side to control what scripts load
  3. Whitelist approach: Add all GTM-loaded domains to script-src (less secure, more brittle)
  4. Hash known scripts: If GTM scripts are static, hash them

Recommend: Start with report-only to see what GTM loads, then whitelist specific domains or move to server-side GTM for tighter control.

Evidence to capture:

  • Current script-src directive value
  • Current style-src directive value
  • Whether nonces or hashes are used
  • Count of inline scripts/event handlers found
  • GTM usage and current strategy
  • CSS-in-JS library in use (if any)

Section

32. Content Security Policy

API & Security