Skip to content

资料库 · 注入与浏览器攻击面

第 ① 卷,条目 1–20。来源以 PortSwigger Research 为主。

这一卷是离前端开发最近的一卷。里面绝大多数问题的根源都是同一件事:浏览器对同一段内容的解析方式,和你写代码时假设的不一样。HTML 解析器、CSS 解析器、JS 引擎、URL 解析器各有各的容错规则,攻击面就长在这些规则的缝隙里。

所有条目的"我的补充"都是防御视角。


XSS 与内容注入

1. New XSS vectors

来源:PortSwigger Research,Gareth Heyes。

读它解决什么:想知道 XSS 的向量集合在不断扩大,以及为什么黑名单式过滤注定失败。

要点

  • 公布新发现的 XSS 向量
  • PortSwigger 维护着一份持续更新的 XSS cheat sheet,这类研究是它的来源

我的补充:这条最重要的启示是**"新向量"这个词本身**——只要还有人在找,向量集合就会继续变大。所以任何基于"禁止这些标签/属性"的过滤方案都是在追一个跑得比你快的目标。正确的防御方向只有两个:输出编码(按上下文选择 HTML/属性/JS/URL 编码,这是根治手段)和 CSP(纵深防御,假设编码有疏漏时限制损失)。我在这个博客的 Pages Functions 里对任何回显到页面的外部输入都走编码,不做黑名单判断——判断"这个字符串危不危险"是一场必输的比赛。

阅读原文 →

2. Exploiting XSS in hidden inputs and meta tags

来源:PortSwigger Research,2023 年。

读它解决什么:以为 <input type="hidden"><meta> 里的值不会被执行,这条说明不一定。

要点

  • 隐藏输入框与 meta 标签中的 XSS 利用
  • 这两类位置常被认为"不可见所以安全"

我的补充:这条打破的是一个很普遍的直觉:"用户看不见"不等于"不会执行"。隐藏字段照样在 DOM 里,属性值里的内容照样被 HTML 解析器处理。审计代码时容易漏掉这些位置,因为它们不在视觉焦点上——模板里的 <input type="hidden" value=""><meta content=""> 需要和可见输出一样严格地做属性上下文编码。搜自己的模板时,别只搜可见文本的插值点,把所有 value=content=data-* 里的插值都过一遍。

阅读原文 →

3. onwebkitplaybacktargetavailabilitychanged?! New exotic events in the XSS cheat sheet

来源:PortSwigger Research。

读它解决什么:想知道有多少冷门事件处理器能触发脚本执行。

要点

  • 新增一批冷门事件到 XSS cheat sheet,标题里那个事件名本身就是例证
  • 标题的 ?! 表达了作者对这个事件名的态度

我的补充:这个长得离谱的事件名(onwebkitplaybacktargetavailabilitychanged)是反黑名单最有说服力的单条证据。任何维护"危险属性列表"的 WAF 或过滤器,都不可能预见到浏览器厂商会加什么新事件——而每个 on* 属性都是一个潜在的执行点。这也是为什么 CSP 里的 unsafe-inline 那么关键:禁掉内联脚本之后,绝大多数事件处理器向量直接失效,不管它叫什么名字。用 nonce 或 hash 而不是 unsafe-inline,是投入产出比最高的单项加固。

阅读原文 →

4. The seventh way to call a JavaScript function without parentheses

来源:PortSwigger Research,2022 年。

读它解决什么:了解在字符受限的情况下调用函数的方式,理解字符过滤为什么不可靠。

要点

  • 不用圆括号调用 JS 函数的第七种方式
  • 序数"第七"本身说明前面已有六种

我的补充:这条的价值在于它量化了一个道理:过滤单个字符(比如禁掉 ()作为防御是无效的。JS 的语法灵活到几乎总有别的路(模板字符串标签、onerror 赋值、Reflect 系列等等)。有些 WAF 规则就建立在"禁掉括号就不能调函数"这种假设上。防御方该拿走的结论:把精力放在不让攻击者控制的内容进入脚本上下文,而不是放在"进入之后怎么消毒"。前者是设计问题,后者是永无止境的军备竞赛。

阅读原文 →

5. Hiding payloads in Java source code strings

来源:PortSwigger Research,2023 年。

读它解决什么:Java 源码的字符串字面量有个特殊性,会让代码审计和扫描器看漏东西。

要点

  • 在 Java 源码字符串中隐藏 payload
  • 涉及 Java 编译期对源码的处理方式

我的补充:Java 的坑在于 Unicode 转义(\uXXXX)在词法分析之前就被处理——也就是说 " 在编译器眼里就是一个真的引号,能提前闭合字符串。后果是:你在代码里看到的和编译器看到的不是同一个东西,静态扫描器如果不做这一层预处理就会看漏。防御上这意味着代码审计不能只靠肉眼和正则扫描,得看编译后的产物或用理解语言语义的工具。这类"源码表示与实际语义不一致"的问题在多个语言里都有(Trojan Source 那批双向文本攻击是同一类),值得在 CI 里加一条检查:源文件里出现非 ASCII 转义或双向控制字符时告警。

阅读原文 →

6. Bypassing character blocklists with unicode overflows

来源:PortSwigger Research。

读它解决什么:字符黑名单为什么能被 Unicode 处理逻辑绕过。

要点

  • 利用 Unicode 溢出绕过字符黑名单
  • 涉及字符编码转换过程中的数值处理

我的补充:根因是编码转换过程中的截断。当某个环节把宽字符转成窄类型(或做取模),一个"安全"的高位码点可能被折叠成一个"危险"的 ASCII 字符——过滤器看到的是前者,最终解析器看到的是后者。防御原则很明确:在整个处理链的最后一个环节做校验,而不是最前面。校验发生在规范化之前,你校验的就不是最终会被使用的那个值。这个原则适用面很广,路径穿越、SQL 注入、命令注入里的绕过大多是同一个模式:先过检查,再被某个转换环节变形。

阅读原文 →


CSP 绕过与 DOM 攻击面

7. Bypassing CSP via DOM clobbering

来源:PortSwigger Research,2023 年。

读它解决什么:配了 CSP 还是被绕过,DOM clobbering 是其中一条路径。

要点

  • 通过 DOM clobbering 绕过 CSP
  • DOM clobbering 指用 HTML 元素的 id/name 覆盖 JS 全局变量

我的补充:DOM clobbering 的机制是浏览器的一个历史遗留行为:id 的元素会自动挂到 window。于是攻击者只要能注入一个 <a id="config">,就能让你代码里的 config 变成一个 DOM 元素而不是你以为的对象。它可怕的地方在于只需要注入 HTML,不需要注入脚本——所以即使 CSP 完全禁止了内联脚本,这条路依然通。防御要点:代码里不要依赖隐式的全局变量,用 let/const 显式声明(局部声明不会被 clobber);window.foo 这种取值方式在处理不可信 HTML 的页面里尤其危险。

阅读原文 →

8. Hijacking service workers via DOM Clobbering

来源:PortSwigger Research,2022 年。

读它解决什么:DOM clobbering 能升级到劫持 Service Worker,影响面从一次访问变成持久化。

要点

  • 用 DOM clobbering 劫持 Service Worker
  • Service Worker 一旦被控制,影响范围覆盖整个作用域

我的补充:这条的严重性等级比上一条高得多,因为 Service Worker 是持久的:它能拦截作用域内所有请求、能在页面关闭后继续存在、下次访问时用户什么都不用做就已经被控制了。这个博客本身注册了 Service Worker,所以这条对我是直接相关的。要点:注册 SW 的脚本路径绝不能来自任何动态值,必须是硬编码的常量;SW 文件的响应头要设 Content-Type: application/javascript 且不能被缓存投毒(见 第二卷);SW 的作用域尽量收窄。顺带说,我之前修过这个博客 SW 的一个 bug:首次访问时 controllerchange 事件的处理逻辑不对——功能 bug 和安全问题在 SW 这块常常是同一段代码。

阅读原文 →

9. Bypassing CSP with dangling iframes

来源:PortSwigger Research,2022 年。

读它解决什么:CSP 的另一条绕过路径,涉及 iframe 的生命周期。

要点

  • 利用"悬空 iframe"绕过 CSP
  • 涉及 iframe 被移除后其上下文的残留行为

我的补充:这类问题的共同点是 CSP 是按文档实施的,而文档的生命周期比你想的复杂。iframe 被从 DOM 里移除之后,它的 JS 上下文可能还活着一段时间,而那个上下文继承的策略未必是你期望的那份。防御上:CSP 要在每个响应上都设(包括 iframe 加载的那些子文档、错误页、以及 about:blank 类型的动态文档),别只在主页面上设。另外用 <iframe sandbox> 显式限制子框架能力,比依赖 CSP 继承更可靠。

阅读原文 →

10. Using form hijacking to bypass CSP

来源:PortSwigger Research。

读它解决什么:CSP 挡住了脚本执行,但表单劫持能在不执行脚本的前提下窃取数据。

要点

  • 通过劫持表单绕过 CSP 的限制
  • 前提是能注入 HTML 但不能执行脚本

我的补充:这条揭示了 CSP 的一个覆盖盲区:CSP 主要管脚本和资源加载,对"表单提交到哪"管得有限。注入一个 <form action="//attacker"> 加上诱导点击,用户填的密码就发到别处去了,全程没有脚本执行。对应的指令是 form-action——这一条在实践中被设的比例很低,很多站点的 CSP 里有 script-src 却没有 form-action。加上 form-action 'self' 成本几乎为零,值得作为默认配置的一部分。

阅读原文 →

11. Ambushed by AngularJS: a hidden CSP bypass in Piwik PRO

来源:PortSwigger Research,2023 年。

读它解决什么:页面上存在 AngularJS 时 CSP 的保护会被削弱,这是一个真实案例。

要点

  • 在 Piwik PRO 中发现的 CSP 绕过,与 AngularJS 相关
  • AngularJS 的模板表达式求值机制是关键

我的补充:AngularJS(1.x,已 EOL)的老式模板会在 HTML 里求值表达式,这等于在 CSP 之外开了一个脚本执行通道——CSP 管的是 <script>,管不了框架自己在解析属性时执行的表达式。这类"CSP gadget"在很多老框架里都有。防御上有两层:一是清点你页面上到底加载了什么第三方脚本(分析、埋点、客服组件常常悄悄引入老框架),二是收紧 CSP 里的 script-src 白名单——如果你信任了某个 CDN 的整个域名,而那个域名上托管着 AngularJS,那个白名单基本等于没设。用 nonce 而不是域名白名单能绕开这整类问题。

阅读原文 →

12. Bypassing Firefox's HTML Sanitizer API

来源:PortSwigger Research,2022 年。

读它解决什么:浏览器原生的 Sanitizer API 也曾有绕过,了解它的可靠性边界。

要点

  • Firefox 的 HTML Sanitizer API 存在绕过
  • 该 API 的定位是浏览器提供的标准化 HTML 净化

我的补充:Sanitizer API 的方向是对的——由浏览器自己(也就是最终解析 HTML 的那个组件)来净化,从原理上比第三方库靠谱,因为不存在"净化器和解析器理解不一致"的缝隙。但这条说明它在早期实现里也有过绕过。实践建议:需要渲染富文本时优先用浏览器原生方案或 DOMPurify(长期维护、修得快),但不要把净化当唯一防线——配上 CSP,并且在服务端也存一份净化后的内容而不是只在客户端净化(客户端净化能被绕过的话,存储型 XSS 就固化在数据里了)。

阅读原文 →

13. Framing without iframes

来源:PortSwigger Research,2022 年。

读它解决什么:以为设了 X-Frame-Options 就防住了点击劫持,这条说明嵌入方式不止 iframe。

要点

  • 不用 iframe 实现页面嵌套
  • 直接影响基于 iframe 假设的防护措施

我的补充X-Frame-Options 和 CSP 的 frame-ancestors 都是针对 iframe 设计的,而 <embed><object>、以及某些浏览器特有的嵌入方式可能不受同样约束。防御上:frame-ancestors 优于 X-Frame-Options(后者已过时且各浏览器实现不一致),敏感操作不要只靠"不能被嵌套"这一层防护——加上二次确认或 CSRF token,这样即使嵌套成功也无法完成危险操作。纵深防御的意思就是任何单层被绕过都不至于失守。

阅读原文 →


原型链污染

14. Widespread prototype pollution gadgets

来源:PortSwigger Research,2022 年。

读它解决什么:原型链污染需要"gadget"才能升级为实际危害,这条讲 gadget 有多普遍。

要点

  • 系统性寻找原型链污染的 gadget
  • "widespread" 指这类 gadget 在常见库中广泛存在

我的补充:原型链污染单独看常被评为低危("只是改了个属性"),但它的实际危害取决于有没有 gadget——某段代码读取了一个未定义的配置属性,而你污染了 Object.prototype 上的同名属性,于是控制了它的行为。这条研究的价值就是说明 gadget 在流行库里到处都有,所以"没有 gadget 所以低危"这个判断通常是错的。防御要点:处理不可信 JSON 时过滤 __proto__/constructor/prototype 这三个键;用 Object.create(null)Map 存放键名来自用户的数据;Object.freeze(Object.prototype) 在某些应用里可行(会破坏一部分库,需要测)。

阅读原文 →

15. Server-side prototype pollution: Black-box detection without the DoS

来源:PortSwigger Research,2023 年。

读它解决什么:服务端原型链污染的黑盒检测方法,且不会把目标打挂。

要点

  • 服务端原型链污染的黑盒检测技术
  • 标题强调"不造成 DoS",说明此前的检测手段有副作用

我的补充:服务端的原型链污染比客户端严重得多——影响面从"一个用户的浏览器"变成"整个 Node 进程",而且污染是跨请求持续的(Object.prototype 是全局的,一次污染影响后续所有请求)。"不造成 DoS 的检测"这个限定条件对做授权测试的人很实际:能在生产环境安全地检测,才可能真的被用上。防御上除了上一条的措施,Node 侧还可以用 --disable-proto=delete 启动参数直接移除 __proto__ 这个访问器。

阅读原文 →

16. Exploiting prototype pollution in Node without the filesystem

来源:PortSwigger Research,2023 年。

读它解决什么:说明原型链污染的利用不依赖文件系统访问。

要点

  • 在 Node 中不借助文件系统利用原型链污染
  • 隐含前提是此前的利用路径常依赖文件系统

我的补充:这条对受限运行时的场景很有意义。很多人的心理防线是"我的函数跑在沙箱里,没有文件系统访问,所以污染打不出什么"。这条说明不成立。具体到这个博客:Pages Functions 跑在 Workers 运行时上,确实没有 fs,但如果代码里有把用户 JSON 深度合并进配置对象的逻辑,污染照样能改变行为。防御不变——深度合并(deep merge)是原型链污染的头号入口,用 lodash.merge 这类函数处理用户输入时必须先过滤危险键,或者换成显式的字段白名单赋值。

阅读原文 →


CSS 与非脚本外泄通道

17. CSS: the bomb inside your inbox

来源:PortSwigger Research,2026 年,本卷最新的一条。

读它解决什么:邮件客户端里的 CSS 能造成什么危害。

要点

  • 主题是邮件场景下的 CSS 攻击面
  • 2026 年发表,是这条研究线的最新一篇

我的补充:邮件客户端是个特别糟糕的环境:它们必须渲染 HTML 和一部分 CSS(否则营销邮件全废),又不能执行脚本,于是安全模型完全依赖"CSS 是无害的"这个假设。而这个假设不成立(见下面几条)。对防御方的实际意义分两种角色:如果你在做邮件客户端或任何渲染不可信 HTML 的产品,CSS 必须白名单化(禁 @import、禁属性选择器上的远程资源、禁 attr() 组合),而不是只过滤 <script>;如果你是使用者,"禁止自动加载远程图片"这个设置的价值远超防跟踪,它同时切断了整类 CSS 外泄通道。

阅读原文 →

18. Inline Style Exfiltration: leaking data with chained CSS conditionals

来源:PortSwigger Research。

读它解决什么:CSS 在完全不执行脚本的情况下如何外泄数据。

要点

  • 用链式 CSS 条件实现数据外泄
  • 依赖内联样式,不需要脚本执行

我的补充:机制是选择器匹配加远程资源请求:写一条只在某个字段以特定字符开头时才生效的规则,让它加载一个远程背景图,攻击者从服务器日志就能逐字符还原内容。这解释了为什么 CSP 里 style-srcimg-srcscript-src 同样重要——只管脚本的 CSP 挡不住这条路。而且注意 unsafe-inlinestyle-src 里被广泛允许(因为太多框架需要内联样式),这就是这条攻击的现实前提。真要收紧,style-src 也用 nonce。

阅读原文 →

19. Blind CSS Exfiltration: exfiltrate unknown web pages

来源:PortSwigger Research,2023 年。

读它解决什么:连页面结构都不知道的情况下,CSS 外泄还能不能做。

要点

  • 盲态 CSS 外泄,目标页面内容未知
  • 比需要预知结构的外泄手法适用面更广

我的补充:"盲态可行"是一个重要的严重性升级信号。安全评估里常见的减分理由是"攻击者需要预先知道页面结构/内联了什么内容",一旦盲态可行,这个减分理由就没了,可利用性大幅提高。给防御方的实践结论:评估 HTML 注入类漏洞时,不要因为"只能注入样式不能注入脚本"就降级。能注入任意 CSS 就意味着页面上的任何文本内容都可能被读走,包括 CSRF token 和一次性验证码。

阅读原文 →

20. Top 10 web hacking techniques of 2025

来源:PortSwigger Research,2026 年初。

读它解决什么:想用一篇文章覆盖 2025 全年最重要的 Web 攻击面进展。

要点

  • 2025 年度十大 Web 黑客技术评选结果
  • 由社区提名、投票产生,是年度综述性质的内容

我的补充:如果只读一篇,读这个系列。它的价值是筛选——每年发表的 Web 安全研究成千上万,这个榜单由从业者提名投票,留下的是真正改变了攻击面认知的那些。我的用法:每年榜单出来时,对着十条逐个问"这条在我们的架构里适用吗",适用的写成检查项加进安全评审清单。这比订阅一堆安全资讯要有效得多,因为它已经帮你做了信噪比过滤。本库收了 2021–2025 五届,在各卷里分布。

阅读原文 →


本卷小结

三个带走的结论:

  1. 黑名单式过滤注定失败。 第 3、4、6 条各自证明了同一件事:字符和标签的向量集合在持续扩大,你追不上。正确的方向是按上下文做输出编码,加上 CSP 作为纵深防御。
  2. CSP 不只有 script-src form-actionstyle-srcimg-srcframe-ancestors 各自挡住一整类不需要执行脚本的攻击(第 10、13、18 条)。只设 script-src 的 CSP 覆盖面比你以为的小得多。
  3. "只能注入 HTML/CSS,不能执行脚本"不是低危。 DOM clobbering 能劫持 Service Worker(第 8 条),CSS 能盲态外泄任意页面文本(第 19 条)。定级时别把"无脚本执行"当作减分项。

上一卷: ← 总览 · 下一卷: ② HTTP 协议与请求走私 →