https://www.kayssel.com/newsletter/issue-20/ Skip to main content Kayssel Home Series Author Projects About Newsletter [?]K Kayssel Home Series Author Projects About Newsletter Connect with me CSP for Pentesters: Understanding the Fundamentals 8 min read October 19, 2025 Site Updates Comments Available Drop your thoughts in the comments below! Found a bug or have feedback? Let me know. Recent Migration Migrated from Ghost to Astro. Spot any formatting issues? Report them! Email X Mastodon CSP for Pentesters: Understanding the Fundamentals Table of contents Contents Hi everyone, A few weeks ago I was knee-deep in a CTF challenge. Found an XSS vulnerability, felt good about it, crafted my payload, and... nothing. The page just sat there, mocking me. Turns out the CSP was configured in this very specific way that blocked everything I tried. Spent the next hour actually reading the policy line by line, understanding what was allowed and what wasn't. Eventually got it, but man, it made me realize how little attention I'd been paying to this header. So that's what sparked this newsletter. I want to break down how CSP actually works and, more importantly, where people screw it up. Quick side note: I'm also working on improving the website right now. Adding a white theme because apparently some of you don't live in dark mode like civilized people, plus keybindings and a bunch of other stuff. Should be ready soon What CSP Actually Is Picture this: you're running a nightclub. You don't want random people wandering in off the street, so you hire a bouncer. That bouncer has a list, and if you're not on it, you're not getting in. CSP is essentially that bouncer, but for your browser. The server sends a policy to the browser saying "hey, only execute scripts from these specific places I trust." When you try to inject malicious code from somewhere else, the browser goes "nope, not on the list" and blocks it. In theory, it's brilliant. In practice, well, that's why we're here. Here's what the flow looks like: Server: "Content-Security-Policy: script-src 'self'" You: All of these are inline. A proper CSP blocks them unless 'unsafe-inline' is present. But here's the thing: tons of legacy applications have inline scripts scattered everywhere. Refactoring all of that is a massive undertaking, so devs take the easy way out. They slap 'unsafe-inline' in there "temporarily" and call it a day. I've seen "temporary" fixes that have been in production for three years. 'unsafe-eval' is similar but for a different type of code execution. It allows functions like eval() that take strings and execute them as code: eval('alert(1)'); setTimeout('alert(1)', 0); new Function('alert(1)')(); If you can control what string gets passed to any of these functions, and 'unsafe-eval' is present, you're in. Then there are the wildcards. * means any domain. https: means any HTTPS site (which is basically everything now). data: allows data URIs, so you can embed code directly in a URL. *.example.com allows any subdomain. All of these are red flags because they're way too permissive. Where Things Break Down Let me show you the misconfigurations I see over and over again in real assessments. The most common one? 'unsafe-inline' just sitting there in the policy: Content-Security-Policy: script-src 'self' 'unsafe-inline' When you see this, your standard XSS payloads work perfectly: Next up is the missing base-uri. Check this out: Content-Security-Policy: script-src 'self' Looks pretty locked down, right? Script source is restricted to the same origin. But there's no base-uri directive. That means you can inject a tag: Now when the page loads its legitimate scripts: The browser goes "okay, base is attacker.com, so this must be https:/ /attacker.com/js/app.js" and loads your malicious script instead. You didn't even need to inject your own script tag. You just redirected theirs. Then there's the lazy wildcard approach: Content-Security-Policy: script-src 'self' https: The https: directive allows any HTTPS site. Since 99% of the internet runs on HTTPS now, this is basically worthless: Just works. Same story with data: URLs: Subdomain wildcards are another fun one: Content-Security-Policy: script-src 'self' *.example.com All you need is ONE vulnerable subdomain. Could be an old forgotten staging server, could be a user upload feature on uploads.example.com, doesn't matter. Find one weakness in any subdomain and the entire CSP falls apart: Last one: missing object-src. When this directive isn't specified, you can sometimes use or tags to bypass everything. It's browser-dependent and a bit finicky, but it works often enough that it's worth checking. Finding CSP in the Wild Most of the time you'll be using Burp Suite or another proxy to intercept traffic. Just look at the response headers in the HTTP history and search for Content-Security-Policy. That's honestly the most practical way when you're doing actual testing. Quick curl command gets you started: curl -I https://target.com | grep -i "content-security-policy" Or just pop open DevTools (F12), go to the Network tab, reload, click the main request, and look at Response Headers. Sometimes it's in a meta tag instead: One thing to remember: CSP can be in both the header and a meta tag. When that happens, the most restrictive one wins. I've seen cases where the header was solid but the meta tag had 'unsafe-inline', and guess which one applied? The restrictive one. But I've also seen the opposite, where the header was weak and the meta tag tried to lock things down, and the weak header took precedence. Point is, check both. Quick Analysis Approach When you find a CSP, here's what I do: First, I look for the obvious wins. Search for 'unsafe-inline'. If it's there, I can probably stop looking and just fire off my XSS payload. Search for 'unsafe-eval' too, because if the app uses any eval-style functions, that's another easy win. Check for wildcards: *, https:, data:. These are all way too permissive and usually mean the policy isn't doing much. Then I verify what's missing. Is base-uri there? If not, can I inject HTML? If yes to both, base tag injection might work. Is object-src there? If not, object/embed tags are worth trying. Look for subdomain wildcards. If you see *.example.com, time to enumerate subdomains and look for vulnerable ones or file upload functionality. There's a tool from Google called CSP Evaluator (https:// csp-evaluator.withgoogle.com/) that automates a lot of this analysis. Paste in the policy and it'll tell you what's weak. Super useful for quick assessments. Wrapping Up So that's the foundation. What CSP is, how it works, the directives that matter, and the misconfigurations you'll run into constantly. The reality is that most CSPs have at least one weakness, usually because getting this right is genuinely difficult. It's not that developers are bad at their jobs. It's that CSP is complex, and the tradeoffs between security and functionality are real. Thanks for reading. Hope this helps you spot these issues faster in your next assessment. Stay sharp, Ruben Other Issues Hardware Security Modules: The Fortress Guarding Blockchain's Crown Jewels Hardware Security Modules: The Fortress Guarding Blockchain's Crown Jewels Previous Issue NTLM Relay: Why Authentication in AD is Still Broken Next Issue NTLM Relay: Why Authentication in AD is Still Broken arrow_upward Comments Enjoyed the article? Stay Updated & Support Get the latest offensive security insights, hacking techniques, and cybersecurity content delivered straight to your inbox. Subscribe Newsletter Weekly security tips & insights Buy Me a Coffee Support my work [?] Follow me on social media Kayssel (c) Kayssel 2026. All rights reserved. | select ESC close Keyboard Shortcuts Search Open search Ctrl K Alternative search / Navigation Go to Home g h Go to Series g s Go to About g a Page Navigation Scroll to top t Scroll to bottom b Next chapter n Previous chapter p Utilities Copy current URL c Open RSS feed r Show this help ? Social Media Open X Shift T Open Mastodon Shift M Open LinkedIn Shift L Press Esc or click outside to close