EXERCISE 3 — Why CSP is defence-in-depth, not the primary fix ============================================================== WHY CSP SHOULD BE DEFENCE-IN-DEPTH, NOT PRIMARY: - CSP does not PREVENT injection; it limits what injected content can DO. The bug (untrusted input treated as code) still exists -- CSP just tries to stop the browser acting on it. If the policy has any gap, the underlying XSS is fully exploitable again. - It is easy to get wrong, has partial/older-browser support, and a too-strict policy that breaks the site pressures teams to weaken it. So it is a BACKSTOP for when encoding/sanitization fail -- not a replacement for them. Encoding/sanitization stop the injection; CSP reduces blast radius. TWO WAYS A MISCONFIGURED OR BYPASSED CSP STILL LEAVES XSS EXPLOITABLE: 1. 'unsafe-inline' (or 'unsafe-eval') in script-src. - Re-allows injected inline scripts / eval-class sinks, so the payload runs despite "having a CSP." A policy can LOOK protective while providing no XSS protection. (The most common real-world failure.) 2. The payload doesn't need to run an inline SCRIPT at all. - Data exfiltration via ALLOWED sources: if img-src/connect-src permit broad destinations, an injected or an allowed fetch can leak data without executing a blocked script. - Overly broad allowlists: script-src listing a CDN/host that has an open redirect, a JSONP endpoint, or a vulnerable library lets the attacker load executable script "from an allowed source." (Why nonce + 'strict-dynamic' is preferred over domain allowlists.) - DOM-only effects: DOM clobbering, base-tag/anchor injection, or defacement/phishing markup that needs no script execution. - (Also: older browsers ignoring CSP entirely.) - In each case the ORIGINAL XSS is still there; CSP just didn't cover that path. That's why you must FIX the injection (encode/sanitize) regardless. THE REPORT-ONLY ROLLOUT STRATEGY: - Deploy the policy first as Content-Security-Policy-Report-Only: instead of the enforcing header. In report-only mode the browser DOES NOT BLOCK anything; it only SENDS a violation report (to report-uri/report-to) for each thing the policy WOULD have blocked. - Steps: 1. Ship the intended strict policy in Report-Only. 2. Collect real violation reports from actual traffic (inline scripts, third-party widgets, handlers you forgot). 3. Fix those: move inline scripts to external files or add nonces/hashes, remove/allow legitimate third parties narrowly. 4. Iterate until reports are clean (no legitimate violations). 5. THEN switch to the enforcing Content-Security-Policy header. WHY REPORT-ONLY PREVENTS THE "STRICT CSP BROKE THE SITE" FAILURE MODE: - Turning on a strict ENFORCING policy blind on a complex site almost always breaks something (a needed inline handler, an analytics widget). The typical panic response is to slap on 'unsafe-inline' to "make it work" -- which guts the protection (see failure #1). - Report-Only lets you discover every breakage WITHOUT impacting users, because nothing is blocked during the learning phase. You fix issues calmly, verify with clean reports, and enforce only a policy you KNOW works. No prod breakage, no pressure to weaken the policy. You arrive at a strict, nonce-based policy that doesn't need 'unsafe-inline'. SUMMARY: Keep encoding/sanitization as the PRIMARY fix; add a strict, nonce-based CSP as a backstop; roll it out via Report-Only so you never have to weaken it to keep the site working.