CSP Bypass Using HTML Injection and Browser Caching: How It Works and How to Defend
A newly discovered cybersecurity technique combines HTML injection, CSS-based nonce leakage, and browser cache manipulation to bypass Content Security Policy (CSP) protections. By extracting nonce values from meta tags and using browser cache behaviors like bfcache and disk cache, attackers can execute malicious scripts with valid CSP nonces. This attack exposes web applications to XSS risks, even when they use proper CSP configurations.
Quick answer: Researchers have described CSP bypasses where an attacker who can inject HTML, but not run script, manages to obtain a valid nonce through browser caching behaviour, then reuses it so injected script is allowed. The defence is to stop HTML injection, avoid caching pages that carry nonces, and use a strict policy.
Key takeaways
- A nonce-based CSP only works if the nonce is unpredictable and never reusable.
- HTML injection without script execution is still dangerous when a policy trusts nonces.
- Caches that store a page with its nonce can make that nonce guessable or reusable. Do not cache nonce pages.
- Fix the root cause first: encode output and sanitise HTML. CSP is a second layer, not a substitute.
- Use strict CSP, Trusted Types, base-uri and object-src restrictions together.
What is Content Security Policy?
Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of script, style, images and frames a page may use. Its main job is to limit the damage from cross-site scripting (XSS). A strict, modern policy usually relies on a nonce: a random value generated by the server for each response, sent in the header (script-src 'nonce-abc123') and copied onto every legitimate script tag. The browser runs only scripts that carry the matching nonce. The MDN CSP guide and the OWASP CSP cheat sheet explain the syntax in detail.
Why is HTML injection still a problem under CSP?
With a good CSP, an attacker who injects <script>alert(1)</script> fails, because the tag has no valid nonce. But HTML injection is a wider class of flaw than script injection. An injected tag can still change the page: add a form that posts to another site, change a link, add markup that swallows later content (so-called dangling markup) or influence what the browser stores and reuses. CSP limits scripts; it does not make HTML injection harmless.
How can caching undo a nonce?
The security of a nonce depends on one rule: a nonce must be fresh for every response and must stay unknown to the attacker. Browsers, however, cache pages. The back/forward cache (bfcache) can restore a whole page exactly as it was, and the HTTP disk cache can serve a stored copy of the HTML. If an attacker can make the victim's browser reuse an earlier copy of a page, or can read the markup of a cached copy through injected content, the nonce inside that copy can be learned and then reused in a new injection. In outline, the reported technique combines three ingredients:
- An HTML injection point on a page protected by a nonce-based CSP.
- A way to leak or recover the nonce value that the page printed, using the injection together with caching behaviour.
- A second injection that includes a script tag carrying that same nonce, which the policy now accepts because the nonce matches.
This article deliberately stays at the level of the idea. If you want to study it practically, reproduce it only in your own lab application, with your own browser profile, and read the original researcher's write-up for browser-specific details. Browser versions change behaviour quickly, so a technique that works in one release may be blocked in the next. Testing real third-party sites without written permission is illegal under the IT Act.
How do you defend against it?
| Control | What it does |
|---|---|
| Output encoding and sanitisation | Removes the injection point, which is the root cause |
Cache-Control: no-store on pages that contain nonces | Stops disk-cache reuse and reduces bfcache restoration of sensitive pages |
| Fresh nonce per response from a secure random generator | Makes a leaked value useless on the next load |
base-uri 'none' and object-src 'none' | Blocks common ways to redirect relative scripts or load plugins |
form-action 'self' | Prevents injected forms posting data elsewhere |
| Trusted Types | Forces DOM sinks to receive vetted values only |
| CSP report-only monitoring | Shows violations before and after changes |
What should developers do this week?
- List every page that prints a nonce and check its caching headers.
- Add
no-storeto authenticated or sensitive pages that carry nonces, and test that the page still behaves acceptably when users press Back. - Search for every place user input reaches HTML, including rich-text fields, comments and error pages.
- Tighten the policy with the directives in the table and turn on reporting.
Common mistakes
- Treating CSP as the XSS fix instead of a backstop.
- Re-using one static nonce for all users or all requests.
- Allowing
unsafe-inlinealongside nonces and assuming nonces still protect you (browsers ignore it only in certain combinations). - Caching full HTML at a CDN without checking whether it contains per-user nonces.
Next steps
Start with the basics of XSS and its prevention in the detailed guide to cross-site scripting. If you want to practise web security in a legal lab, see our web application penetration testing course.
Related reading
Frequently Asked Questions
What's Your Reaction?
Like
0
Dislike
0
Love
0
Funny
0
Wow
0
Sad
0
Angry
0