10种XSS绕过WAF的实战技巧:从编码混淆到上下文攻击 1. 从“小宁的ping功能”说起为什么WAF是Web安全的最后一道防线最近在安全圈里有个挺火的段子叫“小宁写了个ping功能但没有写wafx老师告诉她这是非常危险的”。这个梗之所以能火起来恰恰因为它用一个最简单的场景戳中了无数开发者和安全工程师的痛点。我们来拆解一下一个ping功能用户输入一个IP地址或域名服务器去执行ping命令并返回结果。这听起来人畜无害对吧但问题就出在如果这个输入框没有经过任何过滤和防护攻击者输入的可能就不是8.8.8.8而是一段精心构造的恶意命令比如8.8.8.8 cat /etc/passwd。这就不再是ping而是命令注入Command Injection了。XSS跨站脚本攻击和这个ping功能的危险在本质上是一样的信任了不可信的输入。XSS攻击者将恶意脚本“注入”到网页中当其他用户浏览该页面时脚本就会在其浏览器中执行。后果轻则弹个窗恶作剧重则盗取用户的登录凭证Cookie、进行页面篡改、发起钓鱼攻击甚至结合其他漏洞拿下服务器。那么WAFWeb应用防火墙在这里扮演什么角色呢你可以把它想象成网站门口的“智能安检仪”和“语法纠错老师”的结合体。它部署在Web应用之前对所有进出的HTTP/HTTPS流量进行深度检测和过滤。当“小宁的ping功能”收到那段恶意输入时一个配置得当的WAF会在请求到达后端代码之前就将其拦截因为它识别出了、cat、/etc/passwd这些危险模式。对于XSSWAF的核心工作就是识别并阻断那些试图在响应中插入script、javascript:、onerror等危险字符或模式的请求。但是道高一尺魔高一丈。WAF的规则是死的基于正则表达式、语义分析、机器学习模型等而攻击者的思路是活的。于是“绕过WAF”就成了渗透测试和攻防演练中一个经典且富有挑战性的课题。这绝不是为了破坏而破坏而是通过模拟高级攻击者的思路来检验和提升我们防御体系的健壮性。一个只能防住“教科书式”攻击的WAF是远远不够的。本文的目的就是带你从零基础开始系统性地剖析10种常见的XSS绕过WAF的姿势并深入讲解每一种背后的思路。无论你是刚入门的安全爱好者还是想深化防御策略的开发者或是负责安全运营的工程师收藏这篇都能帮你构建起从攻击视角理解防御的完整知识框架。2. WAF是如何工作的理解规则才能找到缝隙在开始“绕过”之前我们必须先搞清楚我们要绕过的是什么。WAF不是黑盒子它的拦截逻辑有迹可循。主流WAF的检测引擎通常基于以下几层逻辑理解它们就等于拿到了绕过地图的钥匙。2.1 规则匹配正则表达式的攻防战这是最传统、最核心的检测手段。WAF内置了成千上万条正则表达式Regex规则用来匹配已知的攻击模式。比如一条简单的检测XSS的规则可能是这样的/(script|javascript:|on\w\s*)/i它会匹配script标签、javascript:协议以及形如onclick、onerror的事件处理器。绕过思路既然规则是固定的字符串或模式那么我们的目标就是让恶意负载“看起来”不像规则里定义的样子但浏览器解析后又能还原成恶意代码。这就引出了后续几乎所有技巧的核心混淆与变形。2.2 语义分析理解代码的“意图”高级WAF会进行简单的语义分析而不仅仅是字符串匹配。例如它可能理解HTML标签的结构知道img srcx后面应该接一个等号和属性值。它也可能解析JavaScript代码识别出eval()、document.cookie等危险函数和对象。绕过思路对抗语义分析需要更巧妙地隐藏攻击意图。比如将代码拆散、使用浏览器支持但WAF可能未识别的语法糖、或者利用上下文环境让恶意代码“延迟”或“间接”执行。3.3 协议/格式合规性校验WAF会检查HTTP请求的结构是否规范参数值是否符合预期的数据类型如数字、短字符串。它也会验证HTML/JS代码的格式是否大体正确畸形的请求可能被直接拒绝。绕过思路利用浏览器比WAF更“宽容”的解析特性。浏览器在解析HTML和JS时为了兼容性允许大量不严格符合规范的写法而WAF的解析器可能更严格或者采用了不同的解析策略。这中间的“容错差”就是可利用的缝隙。3.4 机器学习与行为分析新型WAF会采用机器学习模型基于大量正常和恶意流量进行训练以识别异常模式。它可能不依赖具体的规则而是从参数长度、字符分布、请求频率等维度进行综合判断。绕过思路这类WAF的绕过更复杂通常需要“低慢小”的攻击将恶意负载伪装成正常流量的样子或者利用对抗样本技术轻微扰动负载以欺骗模型。本文讨论的多数技巧属于规则/语义层面的绕过但理解这一层有助于明白为什么简单的字符串替换有时会失效。注意在实际测试中一个请求往往需要同时绕过多层检测。成功的绕过载荷通常是多种技巧的组合拳。3. 基础混淆绕过简单关键词过滤这是最入门级的绕过方式适合对付那些只进行了简单字符串黑名单过滤的防护措施可能还称不上完整的WAF。核心思想是对抗第一层的正则表达式匹配。3.1 大小写变换有些简单的过滤规则可能只匹配了小写关键词。利用HTML和JavaScript对大小写不敏感某些上下文的特性进行绕过。原始载荷scriptalert(1)/script绕过尝试ScRiPtalert(1)/sCrIpTSCRIPTalert(1)/SCRIPT实操要点这种方法在现代云WAF如阿里云WAF、腾讯云WAF面前几乎完全失效因为它们通常进行了规范化处理。但在一些自定义的、简单的输入过滤函数中可能仍有奇效。测试时这是一个成本最低的优先尝试项。3.2 双写与嵌套标签当WAF采用简单的“删除”策略时例如发现script就将其删除可以利用双写绕过。假设过滤逻辑payload.replace(/script/gi, ‘’)绕过载荷scrscriptiptalert(1)/script过滤后中间的script被删除剩下的字符正好拼接成新的script。思路分析这种绕过方式的关键在于猜测或探测出防护方的处理逻辑是“删除”而非“阻断请求”。你可以通过输入scscriptript并观察返回页面的源码来验证。如果页面显示的是script说明中间的script被删除了这个技巧就可能生效。3.3 插入无关字符或注释在HTML和JavaScript中某些位置插入空格、换行、制表符或者注释不会影响代码执行但可能破坏WAF的正则匹配。绕过示例1HTML注释img srcx onerror!-- 这里是个注释 --alert(1)浏览器解析HTML时会忽略注释将onerror和连接起来事件处理器依然有效。绕过示例2JS换行/空格scriptal\u0065rt(1)/script // 使用Unicode转义 scriptalert (1)/script // 在函数名和括号间加空格某些上下文可行注意事项这种方法高度依赖于WAF规则编写的严谨性。一个健壮的规则应该能在匹配前对输入进行标准化如压缩空白符、解码。在onerror这类事件处理器中插入注释是非常经典的绕过姿势因为很多规则只匹配onerror这个连续字符串。4. 编码与解码的艺术利用浏览器与WAF的解析差异这是绕过WAF的中坚力量核心原理是WAF解码层与浏览器解码层的不同步。WAF为了检测必须对编码后的输入进行一定程度的解码而浏览器在渲染页面时会进行最终的解码。如果WAF的解码深度或顺序与浏览器不一致恶意代码就可能“漏网”。4.1 HTML实体编码HTML实体编码将特殊字符转换为#十进制;或#x十六进制;的形式。原始载荷img srcx onerroralert(1)HTML实体编码后img srcx onerroralert(1)思路如果WAF没有解码或只解码一次它看到的是onerror而不是onerror可能不会触发规则。但浏览器在渲染HTML时会自动将这些实体解码回原始字符从而执行alert(1)。实操心得你可以对整个标签编码也可以只对关键部分如事件名、括号编码。测试时经常尝试对“’以及括号、分号等进行编码。一个常见的技巧是混合编码img srcx one#114;roralert(1)这里只编码了onerror中的字母r。4.2 JavaScript编码在JavaScript上下文中可以使用Unicode转义序列\uXXXX、十六进制\xXX或八进制转义。Unicode转义script\u0061\u006c\u0065\u0072\u0074(1)/scriptalert十六进制转义scripteval(‘\x61\x6c\x65\x72\x74\x28\x31\x29’)/script思路WAF的JS解析器可能没有深度解码这些转义字符或者解码规则与浏览器不同。特别是当这些编码后的字符串出现在字符串上下文被引号包裹中再通过eval()、setTimeout()等函数执行时绕过成功率会增加。典型场景在一些允许用户输入内容并最终放入script标签内执行的场景如某些JSONP回调这种编码非常有效。例如callback\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029。4.3 多重编码与混合编码这是编码绕过的进阶技巧旨在制造更深的“解析鸿沟”。示例先对alert(1)进行URL编码得到alert%281%29再将整个字符串进行HTML实体编码。最终载荷可能看起来像img srcx onerroralert%25281%2529这里%25是%的URL编码。解析过程WAF可能进行URL解码看到onerroralert%281%29认为alert和括号被%28和%29隔开可能不触发规则。请求到达后端后端代码可能再次解码或直接输出。浏览器渲染时看到onerroralert%281%29它会进行URL解码得到alert(1)并执行。关键点你需要清晰地推断出数据流的“解码链”用户输入 → WAF解码检测 → 后端应用处理可能再次解码→ 输出到HTML/JS → 浏览器解码执行。在哪个环节用哪种编码是测试的重点。5. 标签与属性变异寻找WAF的“盲点”WAF的规则库虽然庞大但很难覆盖所有浏览器支持的、晦涩的HTML标签、属性或事件。利用这些“盲点”是绕过深度过滤的有效手段。5.1 使用非常规标签或已废弃标签并非只有script、img、svg才能执行代码。很多标签都有可以触发脚本执行的属性。details标签的ontoggle事件details ontogglealert(1) open summary点击我/summary /details页面加载时open属性会使details默认展开立即触发ontoggle事件。iframe、embed、object的src属性结合javascript:协议需看浏览器兼容性。已废弃但某些浏览器仍支持的标签如isindex、xmp可能在某些解析上下文中导致异常从而引入XSS。思路分析这种绕过的前提是WAF的规则集没有将这些“冷门”的标签-事件组合纳入黑名单。你需要对HTML规范有较深的了解或者通过爬取浏览器的DOM接口清单来寻找潜在的攻击面。5.2 利用标签属性的特殊语法HTML属性值的引号使用、空格处理等存在灵活性可以被利用。无引号与多种引号onerroralert(1)、onerror’alert(1)’、onerror”alert(1)”、甚至反引号onerroralert(1)在HTML中反引号也可作为属性值引号。WAF的规则可能只匹配了其中一两种形式。属性值中的HTML在某些标签中属性值会被解析为HTML。最经典的是iframe srcdoc”scriptalert(1)/script”。srcdoc属性内的内容会作为一个独立的HTML文档被解析和执行。如果WAF只检查了外层标签的属性而忽略了对srcdoc属性值的深度检查就会被绕过。自动闭合与多余字符img/srcx onerroralert(1)在标签名和属性间加斜杠img src’x’onerroralert(1)属性值后不加空格。浏览器解析器非常宽容这些写法都能正常解析。实操要点在测试时要系统地变换属性值的包裹方式并尝试在标签名、属性名、等号、属性值之间插入各种可控字符如/、\、换行符等观察WAF的拦截反应和浏览器的最终解析结果。6. JavaScript执行上下文绕过不止于事件处理器当注入点不在HTML标签内而是在JavaScript代码块script.../script或事件处理器onerror内部时我们需要不同的绕过技巧。这里的核心是在不改变代码语义的前提下改变代码的书写形式。6.1 利用JavaScript语法糖和全局对象JavaScript非常灵活调用函数、访问属性的方式多种多样。反引号调用函数模板字符串alert1。这会被解释为调用alert(‘1’)。许多WAF的规则只匹配alert(1)这种常规调用形式。使用window、self、top等全局对象window[‘alert’](1)、self[‘al’’ert’](1)。通过字符串拼接和数组下标访问的方式动态构造函数名。利用eval的替代品Function(‘alert(1)’)()、setTimeout(‘alert(1)’,0)、setInterval(‘alert(1)’, 1000)。这些函数都能执行字符串形式的代码。利用location、document对象间接执行location’javascript:alert(1)’结合某些场景、document.body.innerHTML’img srcx onerroralert(1)’如果允许的话。思路分析这种绕过的关键在于WAF的静态分析可能无法识别出window[‘al’’ert’]最终就是alert函数。特别是当拼接的字符串来自一个复杂的表达式或用户输入的另一部分时动态性会极大地增加检测难度。6.2 代码分割与注释技巧在JS代码中某些位置插入注释或无关语句不会影响执行。利用//注释分割alert(1)//’);。如果注入点是类似var x ‘用户输入’;这样的上下文你输入’);alert(1)//就能闭合前面的字符串和括号然后执行自己的代码并用注释符//注释掉后面可能存在的其他字符。利用/* */注释/*’*/alert(1)/*’*/。这在需要平衡单引号或双引号的场景下特别有用注释块可以“吞掉”原本的引号。利用JS的自动分号插入ASI在某些情况下即使不闭合语句浏览器也会尝试“修复”语法。但这需要谨慎测试不如注释法可靠。注意事项在JS上下文中的绕过必须充分考虑注入点周围的代码环境。你需要仔细阅读页面返回的源码判断你的输入是被放在字符串里、变量赋值里、还是函数参数里。每一种上下文对应的闭合和绕过方式都不同。这就是所谓的“代码上下文感知”。7. 协议与数据格式滥用在非脚本处执行脚本有些绕过方式非常巧妙它们不直接插入脚本标签或事件而是利用浏览器对某些协议或数据格式的解析特性“诱导”浏览器执行代码。7.1javascript:协议的伪装javascript:协议不仅可以用在a href中还可以用在很多接受URL的地方。绕过对其的过滤是关键。大小写和混合Javascript:javaSCRIPT:。插入控制字符利用URL解析特性在javascript:前插入空格、制表符或其URL编码形式%09,%0a,%0d等如java%0ascript:alert(1)。某些WAF的URL解析器可能在遇到这些空白符时就停止识别协议头。利用data:协议data:协议可以承载HTML或文本内容。例如object data’data:text/html,scriptalert(1)/script’/object。如果WAF没有深度检查data:协议后面的内容就可能被绕过。7.2 SVG标签与HTML5新特性SVG可缩放矢量图形本质上是XML但它可以内嵌在HTML中并且支持脚本。SVG中的事件和脚本svg scriptalert(1)/script /svg或者利用事件svg onloadalert(1)一些WAF对svg标签内部的检查规则可能弱于对普通HTML的检查。HTML5新标签/属性如audio、video的oncanplay事件input的onfocus事件结合autofocus属性等。攻击面随着新标准的引入而不断扩大。实操心得在测试文件上传功能时如果允许上传SVG图片并且服务器没有正确验证SVG内容那么一个包含恶意脚本的SVG文件就可能造成存储型XSS。同样任何允许用户自定义HTML内容如富文本编辑器的地方都要警惕这些“非主流”的标签和属性。8. 基于上下文的绕过量身定制的攻击这是最高级的绕过方式之一因为它不依赖通用的混淆技巧而是深度结合目标应用程序的代码逻辑和输出上下文。CTF夺旗赛中的很多XSS难题都属于此类。8.1 闭合与逃逸如果用户输入被直接拼接进现有的JavaScript代码字符串中你的目标就是逃逸出这个字符串然后执行自己的代码。场景页面代码为scriptvar userInput ‘{{用户输入}}’; /script。攻击载荷’; alert(1); //结果拼接后变为scriptvar userInput ‘’; alert(1); //’; /script。你通过输入的单引号’闭合了原字符串分号;结束原语句然后插入自己的alert(1)语句最后用//注释掉后面多余的单引号和分号。思路分析成功的关键在于准确判断输出点的上下文在HTML标签内、在HTML属性值内、在JS字符串内、在JS代码块内。你需要像编译器一样去阅读前后端代码。有时应用程序会对引号进行转义\’这时你就需要想办法消除那个反斜杠或者利用其他方式如/script标签来跳出当前的JS块。8.2 DOM型XSS与WAF的盲区DOM型XSS的恶意代码完全在客户端浏览器中通过JavaScript操作DOM文档对象模型而触发。由于攻击载荷可能不经过服务器或经过服务器但未被反射传统的基于流量检测的WAF可能完全无法防御。经典场景document.write(location.hash.substring(1));这段代码将URL的锚点部分#后面的内容直接写入页面。攻击者可以构造URLhttp://victim.com/page.html#scriptalert(1)/script。WAF的困境#后面的内容锚点通常不会被发送到服务器在HTTP请求中因此部署在服务器前的WAF根本看不到这部分攻击载荷。防御完全依赖前端代码的安全性。排查技巧检测DOM型XSS不能只看服务器日志和WAF告警。必须进行人工代码审计或者使用动态分析工具如带有DOM污染跟踪功能的扫描器来查找像innerHTML、outerHTML、document.write、eval、setTimeout、location等接收用户可控数据的危险“汇点”。9. 实战问题排查与技巧实录在实际的渗透测试或代码审计中你会遇到各种各样的情况。下面记录一些常见的坑和应对技巧。9.1 如何判断WAF的存在与类型触发已知攻击提交一个最简单的scriptalert(1)/script。如果请求被阻断返回403、406等错误或者页面被替换成一个拦截页面基本可以确定有WAF。观察响应头查看HTTP响应头常见WAF会留下标识如Server: cloudflareX-Protected-By: AWS WAF 或者有WAF、Firewall等字样的自定义头。错误信息差异提交畸形请求如超长参数、特殊字符对比有WAF和无WAF如果可能时的错误信息有时WAF会返回统一的、特征明显的错误页。时间延迟某些WAF在检测到可疑请求时会引入轻微的处理延迟。9.2 绕过测试的通用流程信息收集确定注入点、输出上下文、过滤了哪些字符通过提交特殊字符测试。基础试探尝试大小写、简单编码、双写等基础方法探测过滤强度。上下文分析仔细查看页面源码明确你的输入被放在HTML的哪个位置。这是选择绕过技法的根本依据。载荷构造根据上下文选择合适的标签、属性、事件并应用编码、混淆技巧。迭代测试采用“二分法”思维。如果完整载荷被拦就拆开测试先测试标签img是否被拦再测试srcx再测试onerror最后测试alert(1)。定位到被拦截的具体部分再针对该部分进行变形。组合利用单一技巧失效时将多种技巧组合。例如对一个使用冷门标签且对属性值进行编码的载荷再进行一次URL编码。9.3 常见拦截场景与绕过思路速查表拦截场景可能原因绕过思路尝试提交任何包含script的请求都被拦基础标签黑名单1. 尝试ScRiPt2. 使用非script标签执行JS如img onerror…svg onload…3. 利用iframe srcdoc…onerror被拦截事件处理器黑名单1. 使用其他事件onloadonmouseoverontoggle等 2. 在事件名中插入注释或编码one!-- --rrorone#114;ror3. 不使用事件用script标签或javascript:协议alert(1)被拦截敏感函数名黑名单1. 使用其他函数prompt(1)confirm(1)2. 使用反引号调用alert13. 使用字符串拼接和全局对象window[‘al’’ert’](1)4. 使用eval及其替代品eval(‘al’’ert(1)’)括号()被拦截过滤了特定字符1. 使用反引号无需括号alert12. 使用throw语句绕过括号onerrorthrow 1需特定场景3. 使用HTML实体编码括号alert#40;1#41;整个请求被标记为恶意语义分析或机器学习模型1. 大幅减少攻击特征尝试“低慢小”注入。2. 将载荷拆分到多个参数或请求中如果应用逻辑允许。3. 利用更冷门、更隐晦的浏览器特性。10. 防御视角从绕过中学到的防护最佳实践作为防守方我们从这些绕过姿势中能学到什么单纯的“过滤”和“拦截”思维是脆弱的必须建立纵深防御体系。严格实施输入验证与输出编码输入验证在数据入口处根据预期的数据类型如数字、邮箱、特定格式字符串进行白名单验证拒绝一切不符合格式的输入。这是最根本的。输出编码在数据出口处根据输出上下文HTML、HTML属性、JavaScript、CSS、URL进行上下文相关的编码。将转成lt;将‘转成#x27;等。使用成熟的库如OWASP ESAPI、各种语言的标准HTML编码函数来做这件事不要自己写简单的字符串替换。使用内容安全策略CSP是防御XSS的终极利器之一。通过HTTP头Content-Security-Policy告诉浏览器只允许执行来自哪些可信源的脚本、样式等。即使攻击者成功注入了脚本如果脚本来源不在白名单内浏览器也不会执行。例如Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://trusted.cdn.com;。这能极大缓解DOM型XSS和存储/反射型XSS的危害。设置安全的Cookie属性为会话Cookie设置HttpOnly属性这样JavaScriptdocument.cookie就无法读取它即使发生XSS攻击者也难以直接窃取Cookie。同时根据情况设置Secure仅HTTPS传输和SameSite限制第三方发送属性。谨慎使用危险函数和API在开发中尽量避免直接使用innerHTML、outerHTML、document.write()、eval()、setTimeout(string)等可以将字符串当作代码执行的函数。如果必须使用务必对输入进行严格的净化。WAF的合理定位将WAF视为一道动态的、可更新的应急防线而不是唯一的、静态的解决方案。它的规则需要持续运营和更新以应对新的绕过手法。但它不能替代安全的编码实践。在“小宁的ping功能”那个例子里最根本的解决方法是使用安全的API如参数化查询/命令分离来处理用户输入而不是依赖WAF去拦截所有的恶意命令。绕过与防御是一场永无止境的博弈。攻击者在寻找WAF规则和浏览器解析之间的“缝隙”而防御者在不断弥合这些缝隙并建立更立体的防御体系。理解这10种绕过姿势及其背后的思路无论是为了更有效地进行安全测试还是为了构建更坚固的防御工事都至关重要。真正的安全始于对风险全面而深刻的认知。