Skip to content

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Schutzmechanismen Glossary

CSP - Content Security Policy

Content Security Policy (CSP) is an HTTP header that instructs the browser which sources are allowed for scripts, stylesheets, images, and other resources. CSP prevents XSS attacks by blocking inline scripts and unknown external sources. Common misconfigurations: unsafe-inline, unsafe-eval, wildcard hosts, missing default-src directive. Recommended: Nonce-based CSP or Strict-Dynamic.

Content Security Policy (CSP) is a browser security mechanism controlled via the HTTP response header Content-Security-Policy. It allows website operators to precisely define which resources (scripts, stylesheets, images, frames) may be loaded from which sources. When implemented correctly, CSP is the most effective defense-in-depth measure against cross-site scripting (XSS).

CSP Basic Structure and Directives

Content-Security-Policy: <direktive> <quellen>; <direktive> <quellen>; ...

Important Directives

DirectiveFunction
default-srcFallback for all resource types
script-srcJavaScript sources
style-srcCSS sources
img-srcImage sources
connect-srcFetch/XHR/WebSocket connections
font-srcWeb font sources
frame-srciframe sources (also: frame-ancestors against clickjacking)
object-srcPlugin sources (Flash, Java) - best: &#x27;none&#x27;
media-srcAudio/video sources
base-uriAllowed base-href values (base tag injection!)
form-actionAllowed form targets

Source Specifications

Source SpecificationMeaning
&#x27;none&#x27;Nothing allowed
&#x27;self&#x27;Same origin
https:All HTTPS sources
https://cdn.example.comSpecific domain
&#x27;nonce-<random>&#x27;Unique random value (safest method!)
`'hash--'
`Allows specific inline code via hash
&#x27;strict-dynamic&#x27;Trusts scripts loaded by trusted scripts
&#x27;unsafe-inline&#x27;All inline scripts allowed (INSECURE!)
&#x27;unsafe-eval&#x27;Dynamic code execution allowed (INSECURE!)

Common CSP misconfigurations

Misconfiguration 1 - unsafe-inline + script-src

Content-Security-Policy: script-src &#x27;self&#x27; &#x27;unsafe-inline&#x27;;
  • &#x27;unsafe-inline&#x27; renders CSP USELESS against XSS!
  • All inline scripts (<script>alert(1)</script> ) allowed
  • An attacker can execute XSS despite CSP!

> Common justification: "We need inline scripts for analytics/tracking" - Solution: Nonce-based CSP!

Misconfiguration 2 - Wildcard hosts

Content-Security-Policy: script-src &#x27;self&#x27; https: *.cdn.com;
  • https: allows ALL HTTPS sources worldwide → worthless!
  • *.cdn.com: Attacker places a file on cdn.com and bypasses CSP

Bypass example:

CSP: script-src &#x27;self&#x27; *.github.io;
→ Attacker creates: attacker.github.io/payload.js
→ Injected:<script src="https://attacker.github.io/payload.js"></script>
→ CSP allows it (github.io in wildcard)!

Misconfiguration 3 - JSONP Endpoint in Whitelist

CSP: script-src &#x27;self&#x27; https://api.google.com;
  • Public JSONP endpoint from google.com → CSP allows it!
  • Callback parameter is executed
  • Known JSONP bypass endpoints exist on major CDNs

Misconfiguration 4 - Missing Directives

Content-Security-Policy: script-src &#x27;self&#x27;;
  • Without default-src: other resources (img-src, style-src) are uncontrolled!
  • CSS injection possible: style-src not set

Misconfiguration 5 - object-src missing

Content-Security-Policy: script-src &#x27;self&#x27;; default-src &#x27;self&#x27;;
  • Without object-src &#x27;none&#x27;: Browser plugins could be loaded
  • ALWAYS explicitly set object-src &#x27;none&#x27;!

Misconfiguration 6 - base-uri missing

Content-Security-Policy: script-src &#x27;self&#x27;;
  • Without base-uri &#x27;self&#x27;: Base tag injection possible
  • <base href="https://evil.com/"> → all relative URLs point to evil.com!

How it works

  1. Server generates a random nonce per request (min. 128 bits!)
  2. Set the nonce in the CSP header and in the allowed script tag
  3. Browser executes only script tags with the correct nonce
  4. XSS payload has no nonce → blocked!

Python (Flask)

import secrets
from functools import wraps

def with_csp(f):
  @wraps(f)
  def wrapper(*args, **kwargs):
    nonce = secrets.token_urlsafe(32)  # 256-bit nonce!
    g.csp_nonce = nonce
    response = f(*args, **kwargs)
    response.headers[&#x27;Content-Security-Policy&#x27;] = (
      f&quot;script-src &#x27;nonce-{nonce}&#x27; &#x27;strict-dynamic&#x27;; &quot;
      &quot;object-src &#x27;none&#x27;; base-uri &#x27;self&#x27;;&quot;
    )
    return response
  return wrapper

Node.js (Express)

const crypto = require(&#x27;crypto&#x27;);
app.use((req, res, next) =&gt; {
  res.locals.nonce = crypto.randomBytes(32).toString(&#x27;base64&#x27;);
  res.setHeader(&#x27;Content-Security-Policy&#x27;,
    `script-src &#x27;nonce-${res.locals.nonce}&#x27; &#x27;strict-dynamic&#x27;; ` +
    `object-src &#x27;none&#x27;; base-uri &#x27;self&#x27;;`);
  next();
});

Nonce Security Rules

  • NEVER reuse a nonce! (Regenerate one for each request)
  • DO NOT hardcode the nonce in URLs, logs, or source code
  • Nonce min. 128 bits (22+ Base64 characters) – brute-force-safe
  • REMOVE &#x27;unsafe-inline&#x27; if nonce is active (nonce makes it obsolete)
  • &#x27;strict-dynamic&#x27; + nonce: dynamically loaded scripts inherit trust

CSP Strict-Dynamic Mode

Purpose

&#x27;strict-dynamic&#x27; allows dynamically loaded scripts from trusted scripts.

Without strict-dynamic: Trusted script dynamically loads widget.js → widget.js is not in the whitelist → BLOCKED! Many frontend frameworks fail with standard CSP!

With &#x27;strict-dynamic&#x27;:

Content-Security-Policy: script-src &#x27;nonce-ABC&#x27; &#x27;strict-dynamic&#x27;;
  • Nonce script may dynamically load additional scripts
  • Loaded scripts inherit the trust of the parent script
  • No more need to whitelist external hosts!
Content-Security-Policy:
  default-src &#x27;none&#x27;;
  script-src &#x27;nonce-{RANDOM}&#x27; &#x27;strict-dynamic&#x27;;
  style-src &#x27;self&#x27; &#x27;nonce-{RANDOM}&#x27;;
  img-src &#x27;self&#x27; data: https:;
  font-src &#x27;self&#x27;;
  connect-src &#x27;self&#x27; https://api.example.com;
  frame-ancestors &#x27;none&#x27;;
  base-uri &#x27;self&#x27;;
  form-action &#x27;self&#x27;;
  object-src &#x27;none&#x27;;
  upgrade-insecure-requests;
  • frame-ancestors &#x27;none&#x27;: prevents clickjacking!
  • upgrade-insecure-requests: automatically upgrades HTTP to HTTPS

CSP Testing and Deployment

1. CSP Evaluator (Google)

  • csp-evaluator.withgoogle.com
  • Enter CSP header → automatic vulnerability analysis
  • Shows: which directives are missing, which insecure keywords are set

2. Report-Only Mode (Deployment Preparation!)

Content-Security-Policy-Report-Only: script-src &#x27;self&#x27; &#x27;nonce-XYZ&#x27;;
report-to: /csp-violations
  • Browser blocks NOTHING (no breaking changes!)
  • Sends violation reports for every policy violation
  • Collect + analyze what would be blocked
  • Then: enable the actual CSP header

Violation Report (JSON):

{
  &quot;csp-report&quot;: {
    &quot;document-uri&quot;: &quot;https://example.com/page&quot;,
    &quot;violated-directive&quot;: &quot;script-src&quot;,
    &quot;blocked-uri&quot;: &quot;https://evil.com/payload.js&quot;,
    &quot;original-policy&quot;: &quot;script-src &#x27;self&#x27;&quot;
  }
}

3. Burp Suite CSP Testing

  • Burp Extension: CSP Auditor
  • Checks for known bypasses: unsafe-inline, JSONP hosts, wildcards

4. Step-by-Step Migration

  1. Report-only mode without blocking (collect data for 1–2 weeks)
  2. Implement real CSP on less critical pages
  3. Analysis + adjustment
  4. Rollout to all pages

CSP and Single-Page Applications (SPA)

  • React: Configure webpack nonce injection
  • Next.js: next.config.js CSP header + script nonce in _document.tsx
  • Vue: Nonce injected into index.html via server variable
  • Angular: angular.json security-policy or nonce via server header