Introduction
Content Security Policy (CSP) is the primary browser-side defence against cross-site scripting (XSS) and data-injection attacks. While a well-crafted policy can dramatically reduce the attack surface, misconfigurations and legacy allowances give attackers a rich set of bypass techniques: JSONP endpoints, unsafe inline execution, wildcard source expressions, and nonce reuse. This guide dives deep into four of the most abused vectors—JSONP, unsafe inline, wildcards, and nonce reuse—and shows how they can be combined to subvert even strict CSPs.
Understanding these bypasses is essential for security engineers, penetration testers, and developers who need to audit or harden modern web applications that rely on CSP as a security control.
Prerequisites
- Familiarity with HTTP, HTML, and JavaScript execution contexts.
- Working knowledge of CSP directives (e.g.,
script-src,style-src,object-src). - Basic experience with browser developer tools and network interception proxies (Burp, OWASP ZAP).
- Understanding of XSS payload construction and the concept of “trusted types”.
Core Concepts
Before we explore the bypasses, let’s recap the CSP model that attackers aim to defeat.
Policy Evaluation Order
The browser evaluates CSP directives in a top-down manner. If a resource matches any allowed source expression, it is permitted; otherwise, it is blocked. The default behaviour for omitted directives is to fall back to default-src.
Source Expressions
Source expressions can be:
- Scheme sources (
https:,data:) - Host sources (
example.com,*.cdn.com) - Keyword sources (
'self','unsafe-inline','nonce-…','strict-dynamic')
Understanding how browsers parse these expressions is critical because many bypasses rely on subtle parsing quirks.
JSONP Abuse
JSON with Padding (JSONP) is a legacy technique that wraps JSON data in a JavaScript function call, allowing cross-origin data retrieval via <script> tags. Because the response is executed as JavaScript, a permissive script-src that includes the JSONP endpoint effectively creates an inline script of attacker-controlled content.
Why JSONP is Dangerous Under CSP
If a policy contains script-src to permit a legitimate JSONP service, an attacker who can influence the callback parameter can inject arbitrary JavaScript. Even if 'unsafe-inline' is omitted, the JSONP response is still treated as a script fetched from an allowed source.
Exploiting Callback Injection
Consider the following endpoint:
GET /data?callback=handleResponse HTTP/1.1
Host: api.example.com
When the server responds with:
handleResponse({"user":"alice","role":"user"});
An attacker can supply a malicious callback name:
GET /data?callback=alert%281%29%3Bfoo HTTP/1.1
Host: api.example.com
The response becomes:
alert(1);foo({"user":"alice","role":"user"});
Because the script is loaded from an allowed source, CSP permits its execution, effectively bypassing the policy.
Bypass Variations
- Using
document.writein the callback to inject additional markup. - Leveraging prototype pollution payloads that execute during JSON parsing.
- Abusing “JSON Hijacking” where the JSONP endpoint returns a plain array that can be accessed via
Array.prototype.slice.call.
Unsafe Inline Execution
The 'unsafe-inline' keyword re-enables the classic inline script allowance, but modern CSP implementations provide more granular controls:
'nonce-…'– allows only scripts with a matchingnonceattribute.'strict-dynamic'– trusts scripts that are dynamically created by a script with a valid nonce.
When developers mix 'unsafe-inline' with other directives for convenience, they inadvertently open the door to classic XSS payloads.
Inline Event Handlers
Even if the policy blocks inline <script> blocks, inline event handlers (onclick=…) are still considered inline code. A common mistake is to whitelist 'unsafe-inline' only for style-src while forgetting that it also applies to script-src.
Example Policy Mistake
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline';
This policy permits any inline JavaScript, nullifying the protection offered by CSP for script execution.
Wildcard Source Expressions
Wildcards (* or *.example.com) are convenient but can be abused in several ways:
- Subdomain takeover: an attacker registers
evil.example.comand serves malicious scripts that match*.example.com. - Scheme wildcards:
http:allows both HTTP and HTTPS, potentially enabling mixed-content attacks. - Port wildcards:
example.com:*widens the attack surface to any open port on the host.
Example Bypass
Policy:
Content-Security-Policy: script-src 'self' https://*.cdn.com;
If the attacker gains control of assets.cdn.com (perhaps through an expired domain), they can host a malicious script that the browser will load without violation.
Nonce Reuse
Nonces are intended to be single-use, randomly generated per response. Reusing a nonce across multiple pages or requests effectively turns the nonce into a static secret, which attackers can harvest and replay.
Harvesting a Nonce
Using a browser extension or a simple curl request, an attacker can retrieve the nonce value from a page that includes a legitimate inline script:
curl -s https://target.com/page.html | grep -o "nonce='[^']*'"
Once known, the attacker can inject a new inline script with the same nonce via a reflected XSS or through a vulnerable JSONP endpoint, bypassing the CSP.
Real-World Misconfiguration
Some frameworks generate a nonce at the start of a request and store it in a server-side variable that persists for the entire session. This leads to the same nonce being emitted on every page, violating the “single-use” principle.
Practical Examples
Example 1: JSONP + Nonce Reuse
Target CSP:
Content-Security-Policy: default-src 'self'; script-src 'self' https://api.example.com 'nonce-abc123';
Steps:
- Harvest the nonce
abc123from any page. - Send a request to the JSONP endpoint with a malicious callback that includes an inline script using the harvested nonce:
GET /data?callback=<script%20nonce='abc123'%3Ealert('pwned')%3C/script%3E HTTP/1.1
Host: api.example.com
Because the response is executed as a script from an allowed source, and the inline script carries a valid nonce, the browser executes the payload.
Example 2: Wildcard Takeover
Assume the organization uses https://assets.cdn.com for assets and the CSP contains script-src https://*.cdn.com. The attacker registers evil.cdn.com and hosts evil.js with malicious code. The victim’s page includes:
<script src="https://evil.cdn.com/evil.js"></script>
The browser treats this as a permitted source, executing the attacker's script.
Tools & Commands
- csp-evaluator – Chrome extension that analyses CSP headers for weaknesses.
- csp-reporter – Node.js middleware to log CSP violations for tuning policies.
- burp suite – Use the Intruder module to fuzz
callbackparameters on JSONP endpoints. - jq – Parse JSON responses to extract nonce values from structured data.
Example command to extract a nonce from an HTML response:
curl -s https://target.com | grep -oP "nonce='\K[^']+"
Defense & Mitigation
- Eliminate JSONP entirely; replace with CORS-enabled
fetchcalls. - Never use
'unsafe-inline'. Prefer'nonce-…'or'strict-dynamic'with a strong hash-based CSP. - Avoid wildcards. Enumerate each required subdomain explicitly.
- Generate a fresh, cryptographically random nonce per response. Store it only in the current request context.
- Enable
report-uriorreport-toto collect violation data and adjust policies. - Implement subdomain ownership monitoring (e.g., using
sublist3rorcrt.sh) to detect takeover attempts.
Common Mistakes
- Static nonces: Reusing the same nonce across pages or sessions.
- Mixed
'unsafe-inline'withnonce: Believing the nonce overrides the unsafe keyword. - Over-permissive
script-srcwildcards: Assuming*.trusted.comis safe without verifying subdomain control. - Forgetting
style-src: Attackers can inject<style>tags that execute JavaScript via CSS expressions (e.g.,expression()in older IE). - Neglecting
report-uri: Missing valuable telemetry that could reveal attempted bypasses.
Real-World Impact
In 2023, a major e-commerce platform suffered a breach after an attacker exploited a JSONP endpoint that was whitelisted in the CSP. By injecting a malicious callback, the attacker executed a script that harvested session cookies, leading to credential theft for thousands of users. The incident highlighted how legacy APIs can undermine modern security controls.
My experience consulting for Fortune-500 firms shows that most CSP bypasses stem from “convenience” choices—developers add 'unsafe-inline' to get quick fixes, or they use wildcards to avoid updating policies for new CDNs. The cost of remediation grows exponentially when the same misconfiguration appears across multiple services.
Practice Exercises
- JSONP Injection Lab: Deploy a simple Node.js JSONP service. Write a CSP that allows the service domain. Craft a payload that executes
alert('bypass')via thecallbackparameter. - Nonce Replay Test: Generate a static nonce in a Flask app. Use Burp Intruder to inject an inline script with the same nonce into a reflected XSS vector.
- Wildcard Takeover Simulation: Register a free subdomain on a public DNS provider that matches a wildcard in a CSP. Host a malicious script and verify execution in the victim page.
- Policy Hardening: Take an existing web app with a permissive CSP. Refactor the policy to remove
'unsafe-inline', replace wildcards with explicit hosts, and implement per-request nonces. Verify that all functionality still works.
Further Reading
- W3C Recommendation: Content Security Policy Level 3
- OWASP Cheat Sheet: CSP Cheat Sheet
- Google Project Zero blog post on JSONP abuse (2022).
- Research paper: “Subdomain Takeover: A Survey of Real-World Exploits” (2021).
Summary
Bypassing CSP is rarely about a single flaw; attackers often chain multiple misconfigurations—JSONP endpoints, unsafe inline allowances, wildcard sources, and nonce reuse—to achieve code execution. Defenders must adopt a defense-in-depth approach: eliminate legacy JSONP, enforce strict nonce generation, avoid wildcards, and never rely on 'unsafe-inline'. Continuous monitoring via CSP violation reports and regular policy audits are essential to keep the attack surface in check.