xss-labs靶场实战:20关XSS过滤绕过与防御总结

发布时间:2026/9/29 15:54:21
xss-labs靶场实战:20关XSS过滤绕过与防御总结 1. 从DVWA全家桶到xss-labs为什么我坚持把整个靶场从头打到尾1.1 xss-labs和DVWA、Pikachu的定位差异我最早练XSS是在DVWA上四个难度档位调来调去但总觉得差点意思DVWA的XSS模块每一关都太完整了做完之后你看得到答案却很难形成那种从零到一的构造直觉。后来朋友推荐了xss-labs我才发现自己之前对XSS的理解还是太浅。xss-labs不是什么大而全的漏洞平台它是一套纯粹的XSS挑战关卡特点就是一关一个过滤姿势你需要像解密一样把每一关的过滤器拆掉最终弹出alert。这套靶场最舒服的地方在于它把XSS攻击路径里最容易卡人的过滤绕过单独抽了出来。DVWA教你是这里能注入xss-labs教你是这里明明过滤了但你能通过闭合、编码、事件、伪协议绕过去。对于刚入门Web安全、想在XSS上建立肌肉记忆的人它比直接读长篇漏洞分析要高效得多。1.2 部署只要三步下载、启动、开始注入xss-labs的环境部署非常简单不需要复杂的写代码过程。它以PHP代码的形式分发通常放在Apache或Nginx PHP环境下就能跑。我自己用的是phpstudy把靶场文件丢进www目录启动服务后访问http://127.0.0.1/xss-labs/就能看到关卡列表。如果你只是本地练习不需要额外装数据库也不需要配Redis甚至连配置文件都不用改。这一点对新手特别友好。唯一要提醒的是PHP版本建议保持在7.x或8.x太老的5.x或太新的8.3在某些关卡上可能出现函数行为差异虽然不影响整体思路但没必要在环境上浪费精力。启动之后第一屏就是1到20关的列表每点进去是一个独立的漏洞页面。我的打法是先看页面源码找出输出点在哪再构造一个最基础的payload去试探过滤规则最后根据过滤规则调整绕过手法。三步走完一关再进入下一关。整个过程就像在解一套漏洞谜题。1.3 20关的核心考点分布从无脑弹窗到编码绕过xss-labs的20关并不是胡乱选的它有很明显的难度递进。我打完之后大致划分了一下前五关考察的是输出位置感知你需要知道payload是进了HTML标签内部、属性内部、还是JavaScript代码段中间五关重点考察黑名单绕过常见的手法如大小写、编码、引号闭合、事件触发都是在这部分集中出现后面的关卡则开始玩数据来源和DOM型XSSReferer、Cookie、User-Agent都能成为注入入口。这个分布非常科学。前五关建立的是空间感——注入点在哪决定了你能用什么payload中间五关建立的是对抗感——过滤器到底过滤了什么、漏了什么后段建立的是全局感——用户的每一个输入都可能不可信。如果你能把这三种感觉内化那在真实项目里看到任何XSS点基本都能在几秒钟内判断出可利用性。2. 第一关到第五关闭合、事件、伪协议三招撑起半场2.1 第一关输出点在标签内部直接送一个script第一关是老好人页面大概长这样URL上有个?name参数你传进去的字符串被直接嵌进了页面里的某个标签内。打开源码能看到类似h2你输入的name/h2的结构没有任何过滤没有任何转义。这种场景的payload是最基础的scriptalert(1)/script我到现在都记得第一次弹窗时那种成了的感觉。但我想说的不是payload本身而是你一定要在脑子建立输出上下文的概念当你的输入被放在HTML元素的内容区域时只要尖括号没被过滤就能直接闭合或新建标签来执行脚本。这一关虽然简单却是后面所有高级玩法的基础因为你得先知道输出位置在哪儿才知道该闭合什么、插入什么。2.2 第二关属性值里的逃逸先双引号再标签第二关就开始有味道了。你的输入被塞进了标签的属性里比如input value你的输入。这时候你直接在URL里输入scriptalert(2)/script是没用的因为在HTML属性值的上下文里尖括号不会被当作标签开始符浏览器会把整个valuescriptalert(2)/script当作一个属性值脚本根本不会执行。正确做法是先闭合掉属性值的双引号让payload脱离属性值的囚笼。我用的payload是scriptalert(2)/script。第一个双引号把value属性闭合掉接着的把input标签本身闭合掉然后浏览器遇到了一个新的script标签于是脚本就执行了。这一关的核心不是注入脚本而是让注入点从属性值变成标签上下文。你可以把属性值想象成一组括号先得把括号关上才能在括号外面写新代码。2.3 第三关单引号闭合和事件触发不一定要新标签第三关在很多writeup里被归结为单引号闭合但其实它真正的考点是事件触发。这一关的输入点仍然在标签属性里但属性值是用单引号包裹的而且似乎过滤了双引号或者某些符号。这时候你不能再无脑用双引号闭合得用单引号把当前属性闭合掉然后再构造一个新的事件属性。我记得当时传的是 onfocusalert(3) autofocus。拆开看就是第一个单引号闭合掉原有属性值的开头接着添加一个onfocus事件属性再加一个autofocus让元素自动获得焦点最后的单引号是为了把之后残留的引号配对闭合。浏览器解析到autofocus时会使输入框自动获取焦点触发onfocus事件于是弹窗。这一关让我意识到两件事第一不是所有XSS都必须新建script标签事件属性也是天然的代码执行入口第二闭合一定要闭合干净如果漏掉了结尾的引号整个HTML结构就会乱掉payload很可能不生效。这算是在考验你对HTML解析细节的掌控力。2.4 第四关尖括号没了就把现有标签改成自触发第四关一上手我就发现和被过滤掉了直接输入script根本不显示。既然没法引入新标签那思路就变成利用现有标签的属性。这一关的输入点还是在双引号属性内所以我构造了 onfocusalert(4) autofocus。原理和第三关类似但区别在于场景第三关你还有单引号需要闭合第四关的突破口在于即使尖括号被过滤浏览器解析HTML时仍然会把输入里的双引号视为属性分隔符。通过闭合原有属性、添加事件属性等于把一个普通的输入框变成了我的脚本触发机关。这一关告诉我过滤了尖括号并不能扼杀XSS只要属性值存在引号可逃逸事件属性就能派上用场。这里顺便说一句很多人以为XSS就是必须有尖括号其实事件注入往往比标签注入更隐蔽。在实际开发中很多后端只过滤和却放过了引号和事件名结果一条 onmouseoveralert(1)就能绕过。2.5 第五关伪协议绕过script和on事件全被堵死第五关给我的印象特别深因为它是第一次出现组合拳式过滤script被过滤on开头的关键字也被过滤。我试了大小写、嵌套、加空格全部碰壁。后面翻了源码才发现这关把script和on都做了字符串替换只把这些字面量删掉但完全没有考虑其他执行方式。既然标签被封锁、事件被封锁那剩下的常规执行方式就只剩下JavaScript伪协议了。我记得当时是在链接附近做文章最终用的payload是a hrefjavascript:alert(5)点我/a配合URL参数里的输入点让页面生成一个可点击的恶意链接。点击之后浏览器解析javascript:协议直接执行后面的代码。这一关我第一次感受到执行JavaScript不只一种方式同时也让我建立了伪协议也是XSS payload的意识。现在的浏览器对javascript:伪协议限制了很多但在靶场里它依然是理解XSS绕过路径的必修课。3. 中间关卡黑名单过滤型的常见绕过惯用手段3.1 大小写、编码、拆分绕过str_replace的常规思路从中段开始xss-labs的过滤套路变得非常直白且典型很多关卡都是直接调str_replace把危险字符串删掉。这种过滤有一个致命弱点它只做一次性、精确匹配。比如它删掉script那我传scrscriptiptalert(1)/script的时候过滤完中间的script剩下的部分重组成了script。我第一次用这种嵌套payload成功弹窗时自己都笑了——原来黑名单绕过就这么简单。除了嵌套大小写混合也是常见手法过滤器只认小写的script那我传ScRiPt某些环境下的HTML标签名解析是不区分大小写的。第三种常见手法是编码绕过例如把javascript里的某些字符换成HTML实体#106;avascript如果后端过滤时没有做解码但浏览器解析时能还原那过滤器就形同虚设。我当时做这类关卡时总结了一个顺序先试大小写再试嵌套再试实体编码最后试换行和Tab。把这些套路走一遍大部分黑名单型过滤都能找到突破口。3.2 空格被过滤时用斜杠和Tab来凑合有一关当时卡了我很久——空格被过滤了。你输入img srcx onerroralert(1)时空格全被删掉变成imgsrcxonerroralert(1)整个属性名和值粘连在一起浏览器根本没办法正确解析。这一关的解法其实在HTML解析规则里有在某些标签中斜杠和换行符可以充当属性分隔符。我用的payload是img/srcx/onerroralert(1)。去掉空格之后/依然能把属性分隔开浏览器照样能识别出src和onerror属性。还有人用Tab、换行等空白字符去替代空格也能达到同样效果。这个技巧在真实场景里已经很少见了因为现代WAF通常会正则匹配空白字符但在xss-labs里它逼着我去了解了HTML的空白字符拆分属性机制。这种细节不亲自卡一关很难记住。3.3 链接型XSS的实体编码陷阱还有一类关卡是用a href你的输入来生成超链接过滤规则会把javascript:这个前缀删掉。我一开始试javascript:alert(1)发现javascript:直接被吃掉剩下的只有alert(1)根本没法执行。折腾了一会儿我才缓过神过滤器只会删掉明文里的关键字但如果我把javascript拆开写再用HTML实体编码让浏览器在解析时还原就能绕过。比如构造jav#x61;script:alert(1)后端过滤时看到的是一串和javascript完全不同的字符不会触发抓取浏览器解析HTML时却把#x61;解码成字母a最终得到完整的javascript:伪协议。这种服务端过滤明文、浏览器解析后还原的思路是XSS绕过里非常核心的一种。后来我看很多真实漏洞报告攻击者也会把payload做多层编码来绕过WAF逻辑是一样的。4. 后半程当xss-labs开始考DOM型XSS和Referer/Cookie数据流4.1 DOM型XSS和反射型XSS的本质区别后段关卡里有一部分是DOM型XSS。它和前端的反射型XSS最大区别是反射型XSS的payload在服务端嵌入HTML返回给你服务端在响应正文里能直接看到恶意代码而DOM型XSS是服务端原样返回一段正常的JavaScript真正的问题出在这段脚本里它会读取location.search、document.referrer、localStorage等数据然后通过innerHTML或者eval之类的API把内容动态插入DOM。我在靶场上碰到一道题源码里写着类似document.write(你的输入)服务端完全没有过滤因为payload根本没经过服务端它压根不用回来直接在浏览器端被DOM API消费掉。这种攻击刷新认知的地方在于作为防御方你以为服务端过滤了就不会有XSS但DOM型漏洞告诉你服务端根本看不到payload。在后半程的练习里我刻意养成了一个习惯拿到页面先看JS代码找到DOM操作的入口再想payload能不能被document.write或者innerHTML执行。很多纯看后端代码的人会漏掉这个视角但它恰恰是XSS在客户端侧最灵活的土壤。4.2 从Referer和User-Agent里拔羊毛数据源不只有URL参数xss-labs的后段关卡里有几关很有意思输入点不在URL参数里而在请求头里。比如有一关直接读取Referer字段并嵌入页面我一开始尝试用Burp Suite修改Referer为scriptalert(1)/script结果弹窗成功。还有一关读取的是User-Agent方法类似。很多人觉得请求头是浏览器自动带的我怎么控制——但攻击者和防御者视角完全不同。只要我拦截HTTP请求任何请求头我都能改。浏览器在发请求时会带上Referer、User-Agent、Cookie如果后端把这些值直接插入HTML或JS代码无异于把权限交给用户。这一点对防御者的启发是请求头数据同样不能信任凡是能影响页面输出的部分都必须做编码或过滤。4.3 最后一个容易忽略的点位置与拼接顺序决定一切打到最后几关时我最大的收获不是某条payload而是输出位置与拼接顺序决定一切这个概念。同样是过滤script如果输出点在script代码块内部可能只需要闭合掉前面的字符串然后用自己的代码结束如果输出点在事件属性的值里又可能是另一套玩法。xss-labs里有一关是把输入放在了一段JS字符串中类似var str 你的输入;。这种场景下我用的是;alert(1);//先用单引号闭合当前字符串用分号结束当前语句再写入新语句最后用//注释掉末尾的;。整个payload就像一个精心计算的字符串操作多一个符号少一个符号都会破坏语法。这一点在实际开发中也很有意义。无论后端如何过滤只要把用户输入放入一个可执行上下文中且拼接顺序有漏洞攻击者总能通过调整边界来让代码生效。防御思路不能只盯着危险函数更要盯着你把这个值放在哪里、用什么拼接。5. 通关之后拿什么思路去做XSS防御5.1 输入过滤是助攻输出编码才是核心打完xss-labs之后我对XSS防御最大的认知转变是过滤器永远只能挡一部分攻击真正可靠的是在输出阶段做上下文编码。同样是用户输入放在HTML标签内容区和放在属性值里、放在JavaScript字符串里需要采用不同的编码方式。比如属性值输出最好用HTML实体编码把、等字符转成实体如果是JavaScript字符串输出则要对引号、换行、反斜杠做转义。很多项目里所谓XSS防护只是在入口加了一个黑名单却忽略了不同上下文需要不同规则结果就是被xss-labs里那种大小写、编码、闭合绕得干干净净。5.2 富文本场景下白名单比黑名单可靠xss-labs的20关几乎全是黑名单绕过练习这也从反面证明了一件事在富文本这类允许HTML的场景里黑名单永远比白名单危险。你需要让用户提交b、i这些标签但又想防住script、onerror如果靠黑名单去枚举总会被新玩法突破。更稳妥的是白名单解析只允许指定的标签和指定的属性其余的标签一律剥掉属性名和属性值都经过严格校验。像a href这种标签就算允许也要校验协议必须是http或https而不是javascript。防御思路从不能出现什么变成只能出现什么漏洞面会小很多。5.3 HttpOnly和CSP能兜底但别把宝全押在它们身上打穿靶场之后我顺手把真实项目里的XSS防御措施也理了一遍。首先是HttpOnly它确实能挡住JavaScript读取document.cookie但它拦不住攻击者直接用XMLHttpRequest发请求、拦截页面内容、模拟用户操作尤其遇到DOM型XSS时HttpOnly几乎起不到任何作用。其次是CSP内容安全策略它可以限制页面能加载的脚本来源让scriptalert(1)/script这类内联脚本直接被浏览器拦截。CSP是个好措施但要注意如果业务本身需要内联脚本或者外部脚本来源很多配置就会很痛苦而且一旦配置错误反而可能造成可用性问题。我的个人体会是防御必须分层。输出编码、白名单校验、HttpOnly、CSP各管一段谁也别指望单扛。就像xss-labs里的关卡一样过滤少了一环整条链可能就断了。打完靶场不是终点真正有价值的是把每一关的绕过手法反向整理成一份防御检查清单再回到自己写的代码里过一遍那些以前觉得无所谓的输出点才是真正需要警惕的地方。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询