☰
XSS攻击原理与防御:编码、解析与DOM破坏深度解析
2026/10/22 11:34:31 网站建设 项目流程

经常有朋友问我:XSS到底难在哪?我说你去看三个关键词就够了——编码、解析、DOM破坏。这三个词背后对应着三层完全不同的“欺骗”:骗过过滤、骗过浏览器、骗过前端逻辑。大多数人对XSS的理解停留在“插个script标签弹个窗”,但真实的攻防里,payload能不能执行,拼的是你对浏览器解析机制的理解深度,而不是你背下了多少条payload。

这篇文章我想把XSS攻击从“背exp”提升到“懂原理”这个层面。我不会罗列一堆绕过清单,而是沿着“编码—解析—DOM破坏”这条主线,把攻击者为什么这么做、浏览器为什么会中招、防御方应该在哪一层拦截讲清楚。适合安全工程师、Web开发者、以及对漏洞原理感兴趣的测试同学——尤其适合那些已经会“抄payload”但总不明白为什么有些payload换了个位置就不生效的人。

1. XSS攻击的整体攻防视角:三种形态与一条主线

1.1 反射型、存储型、DOM型:表面分类与底层共性

教科书上习惯把XSS分成三类:反射型、存储型、DOM型。这个分法本身没问题,但它强调的是“数据从哪来、存在哪里”,而不是“漏洞为什么存在”。真正决定一个XSS能不能打的,是你的输入最终进入了哪个执行上下文,以及那个上下文能不能被你控制。

举个例子:反射型XSS是把输入拼进服务端返回的HTML里,存储型是把输入先落库、下次请求再带出来,DOM型则是数据根本不出服务端,直接在浏览器端的JavaScript里被消费。表面上看三条路径完全不同,但底层都遵循同一条主线——不可信数据穿越了“数据层”进入了“代码层”。

这条主线一旦建立,很多问题就迎刃而解了。为什么有的白名单过滤能被绕过?因为它拦的是数据层,而攻击发生在解析层。为什么有些SOP(同源策略)下XSS照样能偷数据?因为DOM型XSS的执行环境根本不受服务端过滤控制。理解了这个共性问题,你就不会再去死记硬背“哪条payload能过哪套WAF”,而是会去分析你面对的输入点到底通向哪个执行上下文。

1.2 为什么编码、解析、DOM破坏是三个核心环节

顺着那条主线往下走,你会发现攻防的焦点始终围绕三件事:

第一,编码。攻击者的payload不能直接写“明文”,因为服务端大概率会过滤。于是攻击者要考虑:这个输入点支持哪些编码?HTML实体、URL编码、JavaScript十六进制转义、UTF-8的变形写法……每一种编码本质上都是在问同一个问题——过滤规则拦的是解码前还是解码后?

第二,解析。这是攻击者最关心的环节。浏览器拿到一段HTML时,会先做词法分析,把标签、属性、文本内容拆开,然后才进入JavaScript引擎执行。问题在于,浏览器在不同位置会先做不同的解码操作:在HTML标签属性里遇到&lt;会先还原成<,在JavaScript字符串里遇到\x3c会先还原成<。如果你能在“过滤完成之后、浏览器解码之前”插入你的代码,那过滤就等于白做了。

第三,DOM破坏。这一层跟前两层完全不同——它不骗过滤器,也不骗解析器,它骗的是前端JavaScript本身的逻辑。攻击者通过控制DOM结构里某些元素(比如带name或id属性的标签、锚点、表单元素),让页面原有的JS在取值时拿到的是攻击者伪造的对象,进而导致后续的赋值、跳转、innerHTML操作全部偏离预期。

这三层不是互斥的,真实的攻击链里经常混合使用。先靠编码绕过过滤,再靠DOM破坏改变取值结果,最后让payload落入一个可执行的环境——这是我在大量项目里见到的标准套路。

1.3 攻击面测定:什么样的输入点值得关注

在开始实战之前,你需要先学会判断哪些输入点值得投入精力。我常用的判断标准有三个:可控性、持久性、上下文敏感度。

可控性看的是“这个输入是否能被用户影响”,不只是URL参数,还包括POST请求体、JSON字段、Referer头、Cookie里的值,甚至上传文件的文件名和内容。持久性看的是“输入影响的是单次响应还是后续所有页面”,存储型XSS之所以危害最大,就是因为影响范围是一次写入、多次触发。上下文敏感度最关键——同样的参数,放在<title>里、放在onclick属性里、放在<script>块里的风险等级完全不同,前者可能只能构造一点文字注入,后者直接就是代码执行。

综合这三条,我在做攻击面梳理时通常会列一张表:输入点、输出位置、可能的执行上下文、潜在影响。这张表不需要很复杂,但能帮你快速区分“看起来能打”和“确实能打”。

2. 编码与解析机制:XSS绕过的底层逻辑

2.1 浏览器解析HTML的完整链路与解码顺序

要理解编码绕过,必须先清楚浏览器解析HTML的顺序。这个过程并不像很多人想的那样“拿到什么就执行什么”,而是分阶段进行的。

浏览器从网络层拿到响应后,先由HTML解析器做词法分析。这个阶段会把标签、属性名、属性值、纯文本区分开,同时识别出哪些位置需要“解码HTML实体”,比如&lt;、&quot;、&#x3c;。HTML实体解码发生在词法分析过程中,也就是说,如果你在属性值里写&lt;img src=x onerror=alert(1)&gt;,解析器会先把它还原成<img src=x onerror=alert(1)>,然后当成新的标签继续解析——即使过滤层已经检查过原始输入里没有尖括号。

接下来是URL解析器。它负责处理href、src、action这些属性里的URL值。如果URL里含有java&#x73;cript:这样的HTML实体,URL解析器会在自己的阶段再做一层解码。这就是“双重编码绕过”的基础:过滤层可能只做了一次HTML实体解码后的检查,但浏览器的多阶段解码叠加之后,你的payload在最终执行前还被还原了一次。

最后才是JavaScript引擎解析。到了这个阶段,HTML解析已经结束,脚本里的字符串、正则表达式、标识符会被JS引擎进一步处理——但JS引擎侧重的是词法分析,它会对字符串字面量里的转义序列做处理,比如\x61还原成a。所以,同样是alert(1),写在HTML属性和写在JS字符串里,可以采用的编码方式完全不同。

我在实际看代码时有个习惯:遇到一个可疑的输出点,先问自己三个问题——它输出在HTML的哪一层?这一层的解析器会做几次解码?过滤逻辑是在哪一层之前做的?这三个问题想清楚了,绕过的思路基本就出来了。

2.2 常见编码方式与典型绕过场景

  • HTML实体编码:&lt;、&gt;、&amp;、&#x3C;等。适合攻击点在HTML标签属性内部、但过滤层屏蔽了尖括号的场景。最常见的就是把<编码成&lt;,让过滤规则匹配不到原始字符,但浏览器解析时仍按<处理。
  • URL编码:%3C、%73等。适合攻击点在URL参数里、参数值会被后续JS或服务端二次解码的场景。需要注意,如果服务端只做了一次decodeURIComponent,那么双重编码的%253C会被还原成%3C而不是<,这种场景需要根据具体解码次数设计payload。
  • JavaScript转义:\x3c、\u003c、\154等。适合攻击点直接进入JS代码块或属性的事件处理器里。JS解析器对字符串字面量里的转义序列是“直接还原”的,所以即使过滤层看到了\x3c并放行,最终执行时它已经变成了<。
  • Unicode规范化与变体:有些服务端框架会做Unicode规范化(NFKC等),攻击者可以利用全角字符、分解形式的字符绕过基于ASCII的黑名单,然后在某个环节被浏览器或第三方库还原。

举一个典型场景。某后台接口把用户输入的昵称拼在<a href="...">里,前端开发知道要转义<和>,却忘了处理"。于是输入" onmouseover="alert(document.cookie)就能闭合属性、注入新的事件处理器。这里的核心不是“引号”本身有多危险,而是它触发了属性值边界闭合——浏览器解析时把你的输入当成了一个新的属性名。

这个例子说明一个规律:过滤永远是在跟解析抢时间。你转义了尖括号,用户就点引号;你转义了引号,用户就用HTML实体;你做了HTML实体解码后再过滤,用户就上JS转义。防御方要做的不是把所有字符都转义,而是根据输出上下文选择正确的转义方式。

2.3 上下文决定攻击手法:同一个payload为何时灵时不灵

很多人调payload时不生效就换一条,从不深究为什么。我建议换一个思路:不是payload的问题,是上下文的问题。

同一个<script>alert(1)</script>,放在下面四个位置会有完全不同的结局。放在HTML标签的纯文本区里,直接执行;放在<textarea>内部,会被当作文本内容显示;放在href属性的引号内部,尖括号不会触发标签解析;放在JS字符串变量里,中间的</script>甚至会提前闭合外层脚本块,导致语法错误。

再举一个实际调过的例子。后端有个接口返回JSON数据,前端用JSON.stringify之后把内容插进了<script>标签里,比如:

var userInfo = {"name": "<script>alert(1)</script>"};

很多人第一反应是“插不进,因为引号会破坏JSON结构”。但如果攻击者的输入里带着</script><script>alert(2)</script>,情况就完全不同了——外层脚本块被关闭,新开的脚本块里的代码就是攻击者可控的。服务端转义了<和>,却没有考虑到<script>标签闭合发生在JSON解析之前。

这就是上下文敏感度的意义。你在纯文本里防住了尖括号,在JS字符串里就要防</script>闭合;你在事件属性里防住了引号,在URL属性里就要防javascript:伪协议。没有一个全球通用的“XSS防护函数”,只有针对每个输出位置的精确转义策略。

3. DOM破坏(DOM Clobbering)深入拆解

3.1 什么是DOM破坏:破坏的到底是什么

DOM Clobbering,翻译过来叫DOM污染,也常被称作DOM破坏。它跟我前面讲的编码解析类XSS有一个根本区别:前者是在“欺骗语言解析器”,它是在“欺骗JavaScript运行时本身”。

原理说起来其实很简单:浏览器对HTML文档里的元素,会根据它的id或name属性,在window对象上自动创建一个对应的全局变量。比如页面里有一个<div id="user"></div>,那window.user就直接指向这个DOM元素。这本来是浏览器给开发者提供的便捷途径——不用写getElementById,直接按名字访问。但如果页面里可被用户控制的内容恰好能影响某个元素的id或name,问题就出现了。

攻击者可以通过注入一个带id="..."或name="..."的标签,覆盖掉原本页面上某个变量的值,从而改变前端JS的判断逻辑。比如页面原本有:

if (window.config && window.config.isAdmin) { // 执行管理员逻辑 }

攻击者如果能往DOM里插入一个<a id="config"><a id="config" name="isAdmin" href="...">,那window.config就不再是undefined,而是一个HTMLAnchorElement对象;window.config.isAdmin取到的是不是undefined呢——如果第二个标签没有href,那就取不到;但它的name属性是“isAdmin”,浏览器会把这个属性暴露为元素对象上的同名属性,所以window.config.isAdmin会变成""(空字符串)。在很多逻辑判断里,空字符串虽然不是=== true,但它足以通过if判断绕过关卡。

3.2 从DOM破坏到XSS的典型链路

DOM破坏最终要转成XSS,通常有两条路径。

路径一是直接改变JS的流程控制。比如某个校验逻辑依赖window.redirect指向一个安全URL,攻击者通过提交一个带特定name的表单或锚点覆盖了这个变量,让后续的window.location = redirect把页面跳到钓鱼地址。严格说这不叫XSS,但因为DOM破坏控制了关键变量,效果和任意跳转差不多。

路径二是“先破坏、再注入”。这是更经典的手法。核心思路是让原本安全的代码在处理被污染的对象时,走错分支,最终把攻击者的内容插入innerHTML、document.write或eval这样的执行点。

举个例子。某论坛功能里有一个切换账户身份的下拉框,前端代码大概是:

let form = document.getElementById("switchForm"); let target = form.targetUser || "default"; document.getElementById("panel").innerHTML = "当前用户:" + target;

正常情况下,target是下拉框里选中的用户名,插入innerHTML前没有转义——但这已经足够危险。不过比较麻烦的是,攻击者要控制一个select控件的值有点难度。更省事的做法是:攻击者先在自己的输入里提交一个自闭合的多余DOM,比如<img name="targetUser" src=x onerror="alert(document.cookie)">,这个标签的name是“targetUser”,于是在form.targetUser这一行里,它返回的不是一个字符串,而是一个HTMLImageElement对象。对象被加进innerHTML结果里会变成[object HTMLImageElement],看起来没什么用——但当对象被序列化到innerHTML时,它的outerHTML属性会被直接写入,于是<img src=x onerror=...>就以片段形式进入了DOM。

这一步才是DOM破坏的精华:攻击者不直接提交带事件属性的字符串(因为这会被过滤或转义),而是提交一个正常的、不带onerror的HTML标签——但通过覆盖变量、再让它在innerHTML里被序列化,最终完整标签进入可执行位置。过滤层检查原始输入时看到的是干净的<img ...>,但执行时它已经带上了攻击者想要的副作用。

3.3 如何检测与防御DOM破坏

检测DOM破坏,我推荐的方法是用浏览器开发者工具直接跑一遍“变量覆盖侦察”。在控制台手动创建候选标签,比如document.body.innerHTML = '<a name="x" href="1"><a name="x-2" href="2">',然后检查window.x、window["x-2"]是否被覆盖成元素引用。另外一个更系统的做法是给所有涉及全局变量访问的代码段打断点,观察是否存在“被元素覆盖为对象”的情况。

防御层面,第一原则是不要用window.xxx这种方式去引用敏感变量。代码里写window.config、window.router、window.targetUser这种隐式全局变量,等于把业务逻辑的支点架在了一个能被HTML标签随意覆盖的位置上。建议显式声明:const config = {...}、let isAdmin = false,这样DOM里的元素就覆盖不了同名变量。

第二原则是让DOM ID和业务变量隔离。给前端元素ID加上不可预测的前缀或启用随机化,可以有效干扰自动创建全局变量的可能性。虽然不太好维护,但对安全要求高的系统值得做。

第三原则是对所有写入innerHTML的数据进行严格的上下文转义。DOM破坏不一定非得触发事件属性——即使最后落在innerHTML里是无害的文本,只要攻击者能控制元素的outerHTML序列化,就可能构成vector。单纯的“输入过滤”在这种场景下非常脆弱,因为你根本无法预判前端有多少处隐式赋值会把对象重新写回DOM。

4. 实战推演:从漏洞发现到修复的完整流程

4.1 搭建一个最小可复现的本地实验环境

我没有用现成的漏洞靶场去演示,因为很多现成靶场景设计得太“理想化”了——过滤规则只有一条、业务逻辑只有一行。还是自己搭一个更贴近真实项目的环境,一个小Node.js服务配合一个搜索页,完整还原“URL参数—服务端输出—前端二次消费”的链路。

服务端代码很简单,关键是它有两个输出点:

// server.js const http = require("http"); const url = require("url"); const server = http.createServer((req, res) => { const query = url.parse(req.url, true).query; const keyword = query.q || ""; let html = ` <!DOCTYPE html> <html> <head><title>搜索</title></head> <body> <!-- 输出点1:直接拼入HTML文本 --> <h1>你搜索的是:${keyword}</h1> <!-- 输出点2:写入textarea缺省值 --> <textarea id="inputBox">${keyword}</textarea> <!-- 输出点3:写入JS变量 --> <script> var lastKeyword = "${keyword}"; document.getElementById("show").innerHTML = "上次关键词:" + lastKeyword; </script> <div id="show"></div> </body> </html>`; res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"}); res.end(html); }); server.listen(3000);

注意输出点3,服务端完全没有考虑</script>闭合的问题,前端还把lastKeyword直接拼进了innerHTML。这一条路径就是个教科书级的DOM型XSS入口。

4.2 攻击链完整推演:输入、编码、解析、执行

拿这个环境复盘一条完整的攻击链。

第一步,确认注入点。访问/?q=test,确认回显位置在<h1>、<textarea>和<script>里。

第二步,初步探测。输入<img src=x onerror=alert(1)>,发现尖括号在<h1>里被原样输出了,页面直接弹窗。这说明输出点1根本没有做任何转义,这是一条最直接的反射型XSS路径。但我没有停下来,因为真实系统往往不会这么“裸奔”——它可能有WAF,可能有框架自带的转义。

第三步,绕过假想中的过滤。假设服务端加了过滤代码,把<和>替换成了空字符串,我的payload变成img src=x onerror=alert(1),标签解析失败。这时观众要理解的是:过滤发生在服务端输出之前,而<textarea>标签内部的内容在浏览器解析时,依然是“特殊元素文本”,尖括号不会被解释成标签闭合。所以直接payload失效,但可以利用实体编码绕过:&lt;img src=x onerror=alert(1)&gt;。服务端过滤看到的是实体字符,浏览器HTML解析后把它还原成<img...>,标签就能正常解析。

第四步,打输出点3即JS变量,需要另一种策略。这个输出点里外层有一对<script>标签,变量值由双引号包裹。攻击者构造"/><script>alert(1)</script>就能先闭合字符串、再闭合外层script,然后新建一个script标签弹窗。当然,服务端若把<script>过滤掉,可以换成<svg onload=...>或<img src=x onerror=...>,原理一样——HTML解析器先看到新的标签,于是新标签回到HTML上下文执行。

从这条链路可以看到,同一个服务端应用、同一个/search接口,因为三个输出点位于不同的HTML上下文,所以需要的payload完全不同:一个被黑名单直接漏放,一个靠实体编码绕过,一个靠闭合标签。这不叫“运气好”,而是解析器在每一个阶段给了攻击者不同的操作空间。

4.3 修复方案横向对比:转义、白名单、CSP三层各管什么

修复方案不能笼统地说“转义一下就好”。针对上面三个输出点,需要三种不同的处理,恰好对应防XSS的三道防线。

第一道防线是输出编码。输出到HTML文本中,转义& < > " ';输出到textarea内部,同样转义但要注意textarea内不能出现</textarea,需要把<完整转成&lt;;输出到JS字符串里,推荐使用JS转义(\x3C)而不是HTML实体,因为JS字符串里HTML实体会原样显示,只有在HTML上下文的解析阶段才能还原,JS阶段并不做HTML实体解码。这套修复一般发生在模板层,用你所在框架自带的转义函数就好。

第二道防线是输入校验与白名单。对搜索关键词这类强格式数据,可以限制字符集:只允许字母、数字、空格和少量常见符号。但这个思路只对“格式稳定”的输入有效;像用户昵称、富文本内容这类天然需要各种字符的场景,不能一刀切割,要配合富文本安全过滤库做白名单标签检查。

第三道防线是CSP(内容安全策略)。通过HTTP响应头限制页面可以执行的脚本来源:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

在攻击者的payload里,alert(1)属于内联脚本,如果CSP里没有'unsafe-inline',浏览器会直接阻止执行。CSP不能替代转义,但它是在“万一前面都漏了”的情况下最后兜底的一层。尤其是在DOM型XSS的场景里,服务端转义往往管不到前端JS执行的后续行为,CSP的作用尤为关键。

我个人的建议是“输出编码+CSP”组合。输出编码处理已知的漏洞点,CSP处理未知的和未来的漏洞点。单独依赖任何一方,在真实的攻防里都有撑不住的时候。

4.4 修复后的回归测试方法

改完代码之后一定要做回归测试。这个过程不能只是“跑一遍curl看看弹不弹窗”,而是要站在攻击者的视角重新梳理一遍所有输入点。

我的回归测试清单通常包含这些内容:原始payload直接访问;实体编码后的payload;URL编码后的payload(包括单次和双重编码);大小写混合变体,比如<ScRiPt>;空字节截断尝试(现在基本没有效果,但老系统可能有);闭合标签型payload,比如</script><script>...;DOM类payload,比如利用img name覆盖变量,观察前端JS行为是否异常。

尤其想提醒一点:修复XSS时最大的风险不是改错了,而是“改完这一个点,其他点被漏掉了”。我带项目时见过不止一次——开发把受害页面里最明显的反射点转义了,但同一个接口数据还被前端拿去拼了eval()或setTimeout等执行入口。所以回归测试时,建议直接给前端JS里所有innerHTML、document.write、eval、new Function、setTimeout字符串形式打上断点,把所有可能落执行的点过一遍。

5. 常见问题与排查技巧实录

5.1 过滤了script标签为什么还是被绕过

这是被问得最多的问题,也是很多开发转安全时的第一道坎。“明明我已经把<script>替换成空字符串了,为什么漏洞报告还是显示可弹窗?”

原因几乎都在于:你只拦了<script>这一个标签,但XSS的承载标签不止script一个。<img onerror>、<svg onload>、<body onfocus>、<details ontoggle>、<iframe srcdoc>——任何一个带事件属性或可加载外部资源的标签都可能成为执行载体。甚至不需要标签,纯CSS的expression(老版本IE)或<style>里的background:url(javascript:...)在某些环境也能执行。

更隐蔽的一种情况是过滤顺序不对。如果你的代码先做了一次全局的replace(/<script>/gi, ""),但用户提交的是<scr<script>ipt>,在过滤掉中间那段后,剩下的部分恰好拼成了<script>,这就是经典的“过滤器助燃”问题。过滤逻辑本身引入了漏洞,这在自研过滤函数里屡见不鲜。

我的建议是,除非你有极大的把握,否则不要依赖自研关键词屏蔽。用成熟的输出编码函数,同时通过CSP限制内联脚本执行,这两件事配合起来比任何自研黑名单都可靠。

5.2 明明转了码为什么DOM型XSS依然存在

这类问题往往出在“只守了服务端,没守前端”。服务端的输出编码做得非常到位,<、>、"、'全部转义,但从接口返回的数据到了浏览器端之后,前端代码还会再做一次解码或拼接,这个过程中转义失效了。

一个典型的例子是前端拿到服务端转义后的JSON,再用JSON.parse()解析获得原始文本,然后直接把原始文本插进innerHTML。那服务端转义的功夫等于白费了,因为后端把数据编码成HTML安全形式是给“显示”用的,一旦进入前端逻辑的数据流,它的编码形态就被还原成了原始内容。

遇到这种情况,重点排查前端JS里有哪些地方使用了innerHTML、outerHTML、insertAdjacentHTML、document.write。这四个API是DOM型XSS的四大常见落点。对它们的要求是:除了服务端给的数据,其他一切写入都先经过转义;即便服务端给的数据,也要带着“这个字段是否可信”的标记。

5.3 修复XSS时的两个意外坑:业务字符集受限与页面大面积报错

真正修复时我发现过两个很影响交付的坑。第一个是业务字符集被过度限制。我遇到过某个搜索系统为了防XSS,把用户昵称的输入校验改成了只允许字母和数字,结果大量用户的中文名、表情符号全部被拒。后来把校验逻辑改成“只校验不允许出现的敏感结构,不做全字符白名单”,加上输出转义,才在安全性和可用性之间找到平衡。

第二个坑是页面大面积报错。原因通常是修复某处输出点时把原本可以正常解析的数据变成了被HTML实体保护的形式,但前端JS拿到的是实体编码后的字符串,再拿去当JSON解析时直接语法报错。这说明修复XSS不是简单地“加个转义函数”就完了,必须跑一遍完整的业务链路测试——尤其是那些跨服务端和前端两个技术栈的数据流。

5.4 独家排查经验:两个值得养成的工作习惯

排查XSS几年下来,我觉得有两个习惯帮助最大。

第一个习惯是“打开浏览器的开发者工具,用控制台当自己的调试场”。不要光是看服务端返回的HTML源码,要在浏览器解析后的DOM树里看最终结果。很多XSS问题在源码里看不出来,因为DOM解析器会把看似无害的片段重新组合成危险的结构。直接用document.body.innerHTML去看浏览器实际得到的DOM,比读原始响应直观得多。

第二个习惯是“把每一次绕过做成一条可复用的时间线”。整理漏洞时把“提交了什么输入、过了哪层过滤、浏览器在哪个阶段做的解码、最终在哪一步执行的”按时间顺序写清楚。这样做有两个好处:一是下次遇到类似的绕过场景,你可以快速定位是过滤层失守还是解析层失守;二是复盘时能明显看到自己的认知盲区——很多人绕不过去的原因根本不是payload不硬,而是不知道浏览器到底在那一步做了怎样的一次解码。

写在最后

XSS攻击反馈到防御上,其实就是一句话:永远不要信任用户的输入,也永远不要认为输出编码是万能的。编码、解析、DOM破坏这三个词,串联起来就是一条完整的攻防链路——攻击者在找解析器的每一层解码空隙,防御者要做的则是把每一层出口都管住。我个人的体会有两点:一是多看真实环境里的浏览器解析行为,少背payload清单;二是严格做好CSP兜底,它能在你完全没有预料到的漏洞点上挡住最致命的一击。最后分享一个小技巧:在Chrome的开发者工具里把断点打在Element.innerHTML的setter上,能帮你第一时间发现页面里所有正在往DOM里写数据的位置,这也是我排查DOM型XSS时最常用的一个手段。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询