Common CSP bypass techniques exploit misconfigurations in the policy — research shows that approximately 94.7% of deployed CSPs that attempt to restrict script execution are ineffective due to configuration weaknesses. Here are the 10 most common bypass techniques and how to mitigate each effectively. safeguard
CSP Bypass Techniques and Mitigations
| Bypass Technique | CSP Misconfiguration | Example Payload | Effective Mitigation |
|---|---|---|---|
unsafe-inline |
script-src includes 'unsafe-inline' |
<script>alert(1)</script> |
Remove 'unsafe-inline'; use nonces or hashes safeguard |
unsafe-eval |
script-src includes 'unsafe-eval' |
eval('alert(1)') |
Remove 'unsafe-eval'; replace with JSON.parse() vaadata |
Wildcard script-src |
script-src contains * |
<script src='https://evil.com/payload.js'> |
Remove wildcards; use explicit origins or nonces vaadata |
Missing object-src |
No object-src or default-src |
<object data='data:text/html;base64,...'> |
Set object-src 'none' vaadata |
| JSONP endpoints | Whitelisted domain hosts JSONP API | <script src='trusted.com/api?callback=alert(1)//'> |
Avoid whitelisting JSONP domains; use nonces + strict-dynamic vaadata |
| File upload bypass | script-src 'self' + file upload feature |
Upload evil.js, then <script src='/uploads/evil.js'> |
Validate file types; serve uploads from different origin vaadata |
| CDN script gadgets | CDN hosts JS frameworks (AngularJS, Vue) | Load AngularJS from CDN, use ng-focus for RCE |
Use nonces + strict-dynamic; avoid domain whitelists safeguard |
Missing base-uri |
No base-uri directive |
<base href='https://evil.com/'> hijacks relative URLs |
Set base-uri 'self' or 'none' aszx87410.github |
data: URI scripts |
script-src allows data: scheme |
<script src='data:;base64,YWxlcnQoMSk='> |
Remove data: from script-src vaadata |
| Script gadgets (nonce bypass) | Framework code evaluates strings even with nonces | AngularJS template injection via whitelisted script | Use strict-dynamic; remove frameworks with eval patterns swag.cispa |
Detailed Breakdown of Each Bypass
1. unsafe-inline — The Most Common Bypass
Including 'unsafe-inline' in script-src allows inline <script> blocks, inline event handlers (onclick, onerror), and javascript: URIs to execute freely. If an attacker has any HTML injection point, they achieve full XSS with a simple <script>alert(1)</script> payload. vaadata
Content-Security-Policy: default-src 'none'; script-src 'self' 'unsafe-inline';
Mitigation: Replace 'unsafe-inline' with per-request nonces or integrity hashes: safeguard
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123';
Each legitimate script tag includes the nonce:
<script nonce="r4nd0m123">/* legitimate code */</script>
Injected scripts without the nonce are blocked. safeguard
2. unsafe-eval — Dynamic Code Evaluation
When 'unsafe-eval' is present, attackers can use eval(), new Function(), and setTimeout('code') to execute arbitrary JavaScript. This is particularly dangerous in applications that concatenate user input into eval’d strings. vaadata
Content-Security-Policy: default-src 'none'; script-src 'self' 'unsafe-eval';
Mitigation: Remove 'unsafe-eval'. Replace eval(JSON_string) with JSON.parse(JSON_string). Refactor new Function() constructors to use static function definitions. vaadata
3. Wildcard script-src
A wildcard (*) in script-src allows scripts to be loaded from any origin, completely negating CSP’s script execution protections. vaadata
Content-Security-Policy: default-src 'none'; script-src *;
Mitigation: Remove all wildcards. Use explicit origins or, better, nonces with strict-dynamic: safeguard
Content-Security-Policy: script-src 'nonce-r4nd0m123' 'strict-dynamic';
4. Missing object-src
Without object-src or default-src, <object>, <embed>, and <applet> elements are unrestricted. Attackers inject <object> tags that load data: URIs containing base64-encoded JavaScript. vaadata
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
Mitigation: Set object-src 'none' to disable all plugin embedding: safeguard
Content-Security-Policy: object-src 'none';
5. JSONP Endpoint Abuse
If a whitelisted domain hosts a JSONP endpoint, an attacker can use it as a script source with a malicious callback. The JSONP response is valid JavaScript that executes the attacker’s callback function. rootshell.yanivhaliwa
Content-Security-Policy: script-src 'self' https://accounts.google.com;
Payload:
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)//"></script>
The response contains alert(1)//({...}), which is valid JavaScript that executes alert(1). vaadata
Mitigation: Avoid whitelisting domains with JSONP endpoints. Use nonces with strict-dynamic instead of domain allow-lists: rootshell.yanivhaliwa
Content-Security-Policy: script-src 'nonce-r4nd0m123' 'strict-dynamic';
6. File Upload Bypass
With script-src 'self', if the application has a file upload feature, an attacker uploads a JavaScript file and references it as a script source. vaadata
<script src="/uploads/avatar_123.js"></script>
Mitigation: Validate uploaded file types strictly. Serve uploaded files from a different origin (e.g., uploads.example-cdn.com) that is not in script-src. vaadata
7. CDN Script Gadgets
CDNs like cdnjs.cloudflare.com and cdn.jsdelivr.net host JavaScript frameworks that can be used as “script gadgets” — existing code that can be coerced into executing attacker-controlled logic. AngularJS is the most notorious example. portswigger
Google’s research identified script gadgets in Angular, Vue.js, Aurelia, Polymer, Knockout, jQuery Mobile, and others that bypass CSP whitelists, nonces, unsafe-eval, and even strict-dynamic in some cases. github
AngularJS bypass example:
<!-- Load AngularJS from whitelisted CDN -->
<script src="https://cdn.jsdelivr.net/npm/angular@1.8.3/angular.min.js"></script>
<!-- Use ng-focus + orderBy to execute arbitrary JS -->
<div ng-app ng-csp>
<input autofocus ng-focus="$event.composedPath()|orderBy:'[].constructor.from( [safeguard](https://safeguard.sh/resources/blog/csp-bypass-techniques-prevention),alert)'">
</div>
The ng-focus directive is a non-standard event handler that AngularJS processes internally, bypassing CSP’s inline script restrictions. The orderBy filter traverses the array to reach the window object, and Array.from() calls alert indirectly. portswigger
Mitigation: Avoid domain-based allow-lists entirely. Use nonces with strict-dynamic, which allows only scripts loaded by already-trusted (nonced) scripts to execute: safeguard
Content-Security-Policy: script-src 'nonce-r4nd0m123' 'strict-dynamic';
8. Missing base-uri
Without a base-uri directive, an attacker who can inject HTML can add a <base> tag that changes the base URL for all relative URLs on the page. aszx87410.github
<base href="https://evil.com/">
<!-- Now <script src="/js/app.js"> loads from https://evil.com/js/app.js -->
Mitigation: Set base-uri 'self' or base-uri 'none'. Note that base-uri does not inherit from default-src, so it must be set explicitly: aszx87410.github
Content-Security-Policy: base-uri 'self';
9. data: URI Scripts
If data: is allowed in script-src, attackers can embed JavaScript directly in a data: URI, bypassing external script restrictions. vaadata
Content-Security-Policy: script-src 'self' data:;
<script src="data:;base64,YWxlcnQoMSk="></script>
Mitigation: Never allow data: in script-src. Only allow data: for img-src or font-src if needed: rootshell.yanivhaliwa
Content-Security-Policy: script-src 'self'; img-src 'self' data:;
10. Open Redirect + Trusted Domain Combination
If a whitelisted domain has an open redirect, an attacker can chain it to load scripts from an arbitrary origin. The browser sees the request going to the trusted domain, but the redirect sends it to the attacker’s server. bitsfolio
Content-Security-Policy: script-src 'self' https://trusted.com;
Payload:
<script src="https://trusted.com/redirect?url=https://evil.com/payload.js"></script>
Mitigation: Use nonces with strict-dynamic instead of domain allow-lists. If domain allow-listing is necessary, audit whitelisted domains for open redirect vulnerabilities. rootshell.yanivhaliwa
Strict CSP Template
The most robust defense against all bypasses above is a nonce-based CSP with strict-dynamic: safeguard
Content-Security-Policy:
default-src 'none';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
frame-src 'none';
frame-ancestors 'none';
connect-src 'self';
img-src 'self' data:;
style-src 'self';
font-src 'self';
form-action 'self';
require-trusted-types-for 'script';
upgrade-insecure-requests;
report-uri /csp-reports;
Key features of this template:
default-src 'none'— blocks everything not explicitly allowed'nonce-{RANDOM}'— only scripts with the per-request nonce execute'strict-dynamic'— trust propagates from nonced scripts to their dynamically loaded childrenobject-src 'none'— blocks all plugin contentbase-uri 'none'— blocks<base>tag injectionrequire-trusted-types-for 'script'— enforces Trusted Types, preventing DOM-based XSS viainnerHTML,outerHTML, and similar sinks vaadata
Testing approach: Deploy in Content-Security-Policy-Report-Only mode first, collect violation reports, and iteratively tighten the policy before enforcing: safeguard
Content-Security-Policy-Report-Only: default-src 'none'; script-src 'nonce-{RANDOM}'; report-uri /csp-reports;
Use Google’s CSP Evaluator to analyze your policy for known bypass patterns before enforcement. vaadata