1. 为什么众多XSS靶场里,我推荐先用xss-labs入门
1.1 从DVWA、Pikachu到xss-labs,各自的定位完全不同
我见过太多初学者一上来就打开DVWA,点开XSS模块,刷完四个关卡就觉得自己会了。实际上DVWA的XSS部分设计得过于浓缩,一个模块只有简单几个关卡,而且它的核心定位是"综合漏洞演示",SQL注入、文件上传、CSRF全混在一起。你练XSS的时候很容易被其它模块分心,更别说DVWA的XSS关卡难度跨度很大,初级和中级之间直接跳档,很多人刷完中级就卡住了。
Pikachu是中文圈子里很受欢迎的靶场,它把XSS分成了反射型、存储型、DOM型几个大块,还带了在线环境的演示。但它的弱点是题目偏"演示",每个漏洞点都有非常明显的提示,做完之后你对"绕过"这件事几乎没有体感。换句话说,Pikachu适合做科普,不适合做训练。
xss-labs不一样。它是一个纯粹的XSS专项靶场,一共20个关卡,全部围绕XSS的注入与过滤绕过展开。从最基础的<script>alert(1)</script>直接执行,到后面的大小写混合、双写绕过、引号闭合、伪协议、DOM注入、资源加载,每一关都像一次小考。更关键的是,它的关卡设计是渐进的,前几关让你建立信心,中间几关开始逼你看源码,后面几关如果不熟悉浏览器解析机制根本过不去。这种"一节一节往上爬"的节奏,非常适合作为XSS入门的第一座训练场。
1.2 xss-labs的20关到底在教什么
把20关拆开看,其实可以分成三个阶段。
第一阶段是Level 1到Level 5,核心是"输出位置决定攻击方式"。这一阶段没有太多过滤,难点在于你要判断用户输入最终落在HTML的哪个位置——是直接输出在标签外部,还是输出在标签的某个属性值里。很多人第一次在value属性里注入时,直接输入<script>alert(1)</script>发现没用,就是因为没有闭合前置的引号和尖括号。这个过程训练的是对HTML解析的基本功。
第二阶段是Level 6到Level 10,核心是"黑名单过滤的绕过"。从大小写绕过、双写绕过、伪协议绕过,到最后的隐藏参数注入,每一关都在告诉你同一件事:凡是基于字符串匹配的过滤,就一定存在绕过路径。这个阶段非常锻炼思路,你会开始习惯性地尝试各种编码、各种标签、各种事件属性。
第三阶段是Level 11到Level 20,核心是"从反射型到DOM型、从常规标签到特殊加载场景"。请求头里的参数注入、隐藏input标签的利用、AngularJS指令的滥用,再到后面需要抓包改包才能完成的注入。刷完这一阶段,你对XSS的理解会从"往URL里塞payload"升级成"关注整个数据流经过的每一个节点"。
这也是我推荐xss-labs的核心原因——它不是让你背payload,而是让你被迫理解每一项技术背后的原理。
1.3 哪些人最适合用xss-labs来练手
如果你属于下面三类人之一,xss-labs就是为你准备的:
- Web安全方向的学生:需要快速建立对XSS漏洞的直觉,理解反射型、存储型、DOM型的区别,掌握常见绕过手法。
- 前端或全栈开发者:很多人写了几年页面都不清楚为什么后端要过滤输入、为什么浏览器会有XSS过滤器。刷xss-labs能让你站在攻击者视角重新审视自己写的代码。
- CTF新手:XSS是CTF Web题目的常客,xss-labs里的很多技巧(引号闭合、伪协议、编码绕过)可以直接迁移到比赛里。
当然,刷之前你至少得知道HTML标签和JavaScript的基本写法。不用精通,只要能看懂<script>、<img>、onerror这些关键词的作用就够了。如果你连这些都还不熟,建议先花一天补一下HTML基础再回来。
2. 搭建与准备:本地环境、浏览器工具、刷靶流程
2.1 本地环境搭建:PHPStudy依然是最省事的方案
xss-labs是一个基于PHP的老牌靶场,源码在GitHub上一搜就有。搭建方式非常简单,你只需要一个能跑PHP的Web环境。国内用户我一般推荐PHPStudy,装完以后把xss-labs的源码解压放到WWW目录下,启动Apache和PHP,浏览器访问http://localhost/xss-labs-master/就能看到关卡入口。
这里有一个常见坑:xss-labs的年代比较久,有些关卡依赖PHP 5.x时代的行为(比如某些超全局变量处理),在PHP 8.x上偶尔会出现警告提示,虽然不影响主流程,但有些人会被警告信息干扰到取参逻辑。我的建议是——如果报错,优先尝试把PHP版本切成7.x,5.6也完全可以,然后用Apache跑,不要用Nginx。靶场本身是按Apache的解析习惯设计的,Nginx在某些请求头处理上略有差异,虽然不影响大方向,但对新手来说少一个变数就少一次莫名其妙的卡关。
如果你习惯用Docker,也可以拉一个现成的LAMP镜像自己搭,原理一致,我就不展开说了。
2.2 浏览器工具:F12是主力,抓包工具是进阶
刷xss-labs需要准备的工具其实非常少,我见过有人只用浏览器F12就把20关全通了。
F12开发者工具是必须熟练的,重点看三个面板:
- Elements(元素面板):看你的输入最终被渲染到什么位置,这直接决定了下一步怎么闭合。
- Network(网络面板):观察请求头、请求参数,后面Level 11到Level 13要从Referer、User-Agent、Cookie里注入,这个面板必须会用。
- Console(控制台):调试JavaScript报错、验证DOM是否被修改。
HackBar这类浏览器插件可以提高效率,它能让你更方便地构造GET和POST请求。不过说实话,xss-labs前大半部分直接用URL改参数也行,HackBar不是刚需。真正刚需的是BurpSuite——从Level 11开始,很多请求头(比如Referer、User-Agent、Cookie)没法通过浏览器地址栏直接改,你需要用Burp抓包、改包、重放。
这里特别提醒一点:BurpSuite的代理配置对新手不太友好,建议先把浏览器代理设置和证书导入流程跑通再开始刷题,不要边刷题边搞工具。工具稳定了,刷题才顺畅。
2.3 一个规范的刷靶流程:先手工测,再源码审计
很多人刷靶场的方法是"试错驱动"——这个payload不行就换下一个,直到某一个生效为止。这种方法不能说没用,但效率很低,而且你根本不知道刚才那个payload为什么不行。
我的建议是每关都走四步:
- 输入探测:先随便输入一个唯一标记,比如
qwe123'"><,然后去Elements面板里看它出现在什么位置、有没有被转义、前后是什么标签结构。 - 构造闭合:根据输出位置确定闭合方式。如果输出在属性里,先闭合引号和尖括号;如果输出在HTML标签外,直接考虑注入新标签。
- 试探过滤:输入
<script>看关键字是否被过滤、被替换成什么、过滤了哪些标签和事件。 - 源码审计:xss-labs的每关都是一个独立PHP文件,直接打开源码看过滤函数。这不是作弊,这是最快的学习路径。
第四步看着像"泄题",其实是xss-labs最大的教学价值。它每一关的过滤逻辑就那么几行代码,你用猜的可能要猜半天,看一眼源码瞬间就明白了为什么某个payload有效、为什么某个payload无效。刷完之后你脑子里留下的不是一堆拼凑的payload,而是一套"过滤→绕过的对应关系图谱"。
3. 从Level 1到Level 5:反射型XSS的第一步是看"输出位置"
3.1 Level 1:最基本反射,参数直出
Level 1没有任何过滤,URL里的name参数值会被直接输出到页面的HTML里。你只需要访问:
http://127.0.0.1/xss-labs-master/level1.php?name=<script>alert(1)</script>弹窗就出来了。
如果这一关你都卡住,问题通常出在编码上——注意地址栏里的<和>可能会被浏览器自动编码成%3C和%3E,但服务端拿到之后会自动解码一次,所以最终渲染到HTML里的依然是尖括号。这一点其实值得停下来想一想:这条数据流里,浏览器编码了一次,服务端解码了一次,最后又按HTML解析了一次。XSS的本质就藏在这几次编码与解码的错位里。
3.2 Level 2到Level 3:属性值注入,引号闭合是关键
到了Level 2,页面上多了一个输入框,而你注入的内容被放进了<input>标签的value属性里。如果直接提交<script>alert(1)</script>,浏览器会把它当成value属性的纯文本值,根本不会解析成标签。
你需要先逃出属性值的边界。标准payload是:
"><script>alert(1)</script>这里双引号先闭合了value=",>再闭合<input>标签本身,后面的<script>就变成了独立的HTML标签。
Level 3的思路完全一样,只不过它用的是单引号闭合。payload改成:
'><script>alert(1)</script>这两关一旦你亲手试过,就会彻底明白"Why闭合引号"这个问题。我之前见过有人在这两关卡了很久,始终不理解为什么要加引号,结果一下子明白了——因为你的输入在别人家的"院子里",你得先打开门才能进屋。
3.3 Level 4到Level 5:双引号闭合与关键字过滤初体验
Level 4又切回双引号闭合,思路和Level 2一致。我重点想说的是Level 5,因为它是我认为整个靶场第一个真正带"过滤"的关卡——它把<script这个字符串直接替换为空。你提交<script>alert(1)</script>,渲染出来的只有>alert(1)</script>,弹窗自然不出现。
这里就引出了XSS学习中的关键概念:黑名单过滤不等于安全过滤。你以为删掉了script就能挡住XSS,但实际上可执行JavaScript的标签远不止script一个。经典替代方案是img标签配合onerror事件:
<img src=x onerror=alert(1)>src=x会找不到图片,触发onerror事件,JavaScript就执行了。
Level 5的另一个价值是让你开始积累"可用于XSS的标签清单"。script、img、svg、a、video、audio、details、iframe、object……每认识一个带着事件属性的标签,你的绕过武器库就多一把刀。
3.4 反射型XSS的本质:输出位置决定攻击方式
把前五关串起来看,你会发现它们共同指向一个核心模型:反射型XSS是"服务端取参→拼接到HTML→原样输出"的过程。攻击者真正要做的事情只有两件——判断输入落在HTML的哪个上下文(标签外、属性内、注释内、脚本内),然后构造对应的闭合和注入方式。
这个上下文意识非常重要。同样是<script>alert(1)</script>,输出在<div>标签之间就有效,输出在<input value="...">里就无效;同样是引号闭合,单引号场景和双引号场景的payload完全不同。如果你不能一眼判断出输入输出的位置关系,那你刷后面十几关一定会吃大亏。
4. Level 6到Level 10:过滤规则下的绕过实战
4.1 Level 6到Level 7:大小写绕过与双写绕过
从Level 6开始,靶场加大了过滤力度,把script、onerror等一堆关键词都纳入了黑名单。但这些过滤只做了小写精确匹配,于是大小写混淆就变成了最简单的绕过方式:
<ScRiPt>alert(1)</ScRiPt>服务端过滤规则匹配的是"script"全小写,ScRiPt不在名单里,防御直接失效。这一关的政治寓意非常明确:如果要做黑名单匹配,至少得统一转成小写再比对,否则就是形同虚设。
Level 7更巧妙一点,它会把script字符串直接替换为空字符串,但替换次数只有一次。于是出现了"双写绕过":
<scr<script>ipt>alert(1)</scr</script>ipt>过滤逻辑把里面的第一个script删掉,结果剩下的部分反倒拼成了完整的<script>标签。用字符串处理的思维来看,这就像一个贪吃蛇——你以为你在删除有害字符,实际上是在帮攻击者拼接最终payload。
我第一次见到双写绕过时觉得这招有点"脏",但它确实揭示了一个非常底层的问题:只做一次顺序替换的过滤,永远可以被构造性输入骗过。
4.2 Level 8到Level 9:javascript伪协议与"必须含http"的校验
Level 8的注入点是在<a href="...">这个链接地址里。你可能以为这里没法执行代码,但实际上href属性是支持javascript:伪协议的:
javascript:alert(1)用户点击链接时,浏览器就执行了这段JavaScript,这就是经典的"伪协议注入"。
但Level 8把javascript关键词也过滤了,所以你要用编码或者其它手法绕。常见的做法是用HTML实体编码、URL编码、大小写混淆等技巧,把javascript字符串变个形态,让过滤规则认不出来,但浏览器照样能识别。这里有个很重要的细节:不同浏览器对编码解析的宽容度不一样,xss-labs设计年代的主流浏览器对实体编码的容忍度更高,你现在用Edge或Chrome测试时,某些payload可能会受浏览器自身XSS过滤器的干扰。遇到这种问题,我的建议是换Firefox试,或者临时关掉浏览器XSS防护看下原始行为。
Level 9在Level 8的基础上加了一个条件:payload里必须包含http://字符串(所以正常javascript:alert(1)会因为不含http://而被丢弃)。绕过思路也很有意思,利用注释或者无意义的协议前缀把http://塞进payload里:
javascript:alert(1)/*http://*//* */是JavaScript注释,浏览器执行时直接忽略,但又满足了服务端对http://的字符串匹配。这就是典型的"喂给服务端看的和喂给浏览器看的不是同一个字符串"。
4.3 Level 10:隐藏输入与参数名发现
Level 10是我认为整个靶场第一个"必须看源码才能做出来"的关卡。页面上只有一个<form>,你提交的keyword参数看起来没什么用,因为真正的注入点在两个<input type="hidden">里,而且它们的name在表单里根本没出现。
如果你不看源码,光靠瞎猜参数名,理论上要试很久。这也是xss-labs逼你进步的地方——遇到页面没有明确入口的情况,第一反应就应该是看源码,而不是继续乱试。
看完源码你会发现有三个隐藏参数(一般是t_sort之类的名字),其中有一个会被渲染到value属性里。找到正确的参数名之后,构造"><script>alert(1)</script>就通关了。这关训练的是"参数发现能力",在很多真实漏洞挖掘里,隐藏参数、隐藏接口往往就是突破口。
4.4 过滤与绕过的模型总结:黑名单永远不完整
Level 6到Level 10这几关,让我把"黑名单过滤"的弱点彻底看透了。事后我总结了一个非常实用的模型:
黑名单过滤本质上是在做"字符串匹配"。既然要匹配,就有三个天然盲区:
- 匹配不完整:只过滤了小写
script,没过滤ScRiPt。 - 替换可拼接:删一次反而帮攻击者拼出了完整标签。
- 校验可欺骗:服务端校验一个字符串,浏览器执行另一个字符串。
真正安全的XSS防御从来不是靠拉黑名单维持的。正确做法是白名单——规定哪些标签、哪些属性、哪些协议允许出现,其他一律拒绝;或者直接用上下文编码,把输入当成纯文本处理。这个道理你现在可能还没有切肤之痛,但等你刷完后面几关再回头看,会理解得更深刻。
5. Level 11到Level 15:从反射型到DOM型的思维切换
5.1 Level 11到Level 13:从Referer、User-Agent、Cookie里取参
这三关放在一起说,因为它们共享同一个套路:注入点不在URL参数里,而在HTTP请求头里。
- Level 11:服务端把
Referer请求头的值取出来,拼进页面里的某个隐藏input的value中。 - Level 12:同样逻辑,但取的是
User-Agent头。 - Level 13:取的是
Cookie中的某个键值。
用户不可能通过浏览器地址栏直接修改这些值(至少常规操作下不行),所以前面说的HackBar就不够用了,必须上BurpSuite抓包改包。比如Level 12,你只需要拦截请求,把User-Agent改成:
User-Agent: "><script>alert(1)</script>放行之后就能看到弹窗。
这三关让我印象最深的是"攻击面意识的转变"。你突然发现,XSS不只是存在于URL参数里,服务端取到的每一个客户端可控输入——请求头、Cookie、甚至文件元信息——都可能被拼进HTML里。一个只过滤了GET参数的应用,完全可能栽在Referer头上。这就是为什么真实世界的XSS挖掘那么讲究"找输入点"。
5.2 Level 14到Level 15:外部资源包含与ng-include
Level 14在这一版靶场里经常被简化,有的是引导你加载一个外部图片,图片的EXIF信息会被读入页面导致XSS。如果本地环境没有配套的外部服务,这一关往往不太好复现,很多教程会直接跳过。我的建议是别被它卡住,能理解"资源文件的元数据也可以成为XSS载体"这个点就够了。
Level 15是值得好好琢磨的一关,因为它引入了AngularJS的ng-include指令。如果你在URL参数里传入一个#包含某段HTML的值,ng-include会将它作为模板包含进来。关键点在于AngularJS的指令解析是不依赖HTML标签闭合的——就算外层结构不完整,某些指令依然可能被触发。最简单的通关payload是往参数里传入一个引用,让它加载level1.php?name=<script>alert(1)</script>,借助第一关的输出把脚本"包含"到当前页面里。
这一关我第一次看的时候一头雾水——为什么前面所有关都是"想办法闭合标签",这一关却完全不按套路出牌。后来才明白,当页面使用了前端模板引擎(Angular、Vue等)时,XSS的路径就不再是"服务端拼接HTML"这么简单了,而是变成了"前端框架解析可控内容"的新模式。这也是从传统XSS向DOM型XSS过渡的典型标志。
5.3 反射型与DOM型的核心区别:服务端到底插没插手
很多初学者分不清反射型和DOM型,这里我用自己的话解释一下。
反射型XSS:用户的输入被服务端接收,服务端把输入拼进HTML,再返回给浏览器。整个"把输入变成HTML"的过程发生在服务端。
DOM型XSS:服务的返回的HTML本身是固定的,没有把输入拼进去。但页面里的JavaScript会读取客户端参数(比如location.hash、location.search、document.referrer等),然后动态修改页面DOM,这个修改过程把恶意内容插进了DOM。
换句话说,反射型的罪魁祸首是服务端代码,DOM型的罪魁祸首是前端JavaScript。这也是为什么很多扫描器很难扫出DOM型XSS——它对服务端不可见,只能靠浏览器端动态分析。
理解这个区别有什么用?用处在于防御和检测时思路完全不同。防守反射型,重点是后端过滤输入和输出编码;防守DOM型,重点是前端代码不能用不可信数据直接操作innerHTML、document.write之类的危险接口。
5.4 为什么DOM型XSS更隐蔽,以及如何在xss-labs里练出手感
DOM型XSS更隐蔽的原因在于流量层面看不出异常。你发一个带payload的请求,服务端返回的HTML完全正常,任何基于响应内容做检测的WAF都拦不住。这给安全测试带来很大挑战。
xss-labs从Level 11开始就已经在帮你培养这种"端到端数据流"视角了。你不再只在URL层面想问题,而是开始问:我提交的这份数据,最终经过哪一行代码,落到哪个DOM API里?练到后面,你看到一个页面时,第一反应就是去找innerHTML、outerHTML、document.write、eval这些危险函数,而不是先抓包改参数。
6. Level 16到Level 20:空格、换行与资源加载的收尾关卡
6.1 Level 16:空格被过滤怎么办
到了Level 16,靶场开始清洗更细粒度的字符,其中一个非常致命的是——空格被过滤掉了。<img src=x onerror=alert(1)>这种payload里到处是空格,一旦空格没了,标签属性都没法分隔。
第一个想到的方案是用/代替空格,因为<img/src=x/onerror=alert(1)>在HTML解析中也能正常工作。但这一关的过滤规则可能也会清洗某些字符,所以我常用的替代方案是换行符。在URL编码里,换行符是%0a,%0d是回车。你把payload写成:
<img%0asrc=x%0aonerror=alert(1)>浏览器解析HTML时,属性之间可以用空白字符(包括换行)分界,而过滤规则往往只删了空格忘了换行。这个"空白字符变体"的思路在后来的实战和CTF里也非常常用。
还有一个细节值得说:过滤空格的时候,很多人会顺手试Tab(%09)、换行(%0a)、回车(%0d)三种空白字符,但不会都试,结果错失正确答案。这种尝试顺序本身就是基本功——过滤一个字符的时候,你不能只想到它的直接替身,而是要把一类字符里的所有变体都过一遍。
6.2 Level 17到Level 18:embed标签与参数拼接
这两关的注入点转移到了<embed>标签。页面可能会接收多个参数,并把它们的值拼接到embed标签的src属性里,比如<embed src="参数1" type="参数2">之类。你需要在参数里闭合引号,注入自己的属性名和事件。
比如用src参数闭合之后,追加一个onmouseover事件,或者直接用onclick之类的触发点。思路在前面的关卡已经练过,没有本质变化,唯一的新知识点是embed标签本身支持的事件属性和script一样能执行代码。
这两关在整套靶场里的定位更像"巩固练习"——让你在熟悉了大小写、事件注入之后,换一个不那么常见的标签再验一遍。千万别小看这种"换标签验证",很多真实环境里script被严格过滤、img也上了黑名单,但embed、video、details这些冷门标签反而被漏掉了。
6.3 Level 19到Level 20:Flash场景与最终挑战
最后的Level 19和Level 20和Flash有关,它们的注入点在Flash加载参数里。老实说,现在主流浏览器已经不再支持Flash插件了,这两关的复现条件非常苛刻。不同版本的xss-labs对这两关的处理也不一样,有些版本直接就是"看源码学原理"的状态,有些版本则需要特定版本的浏览器加Flash插件才能弹窗。
如果你在本地刷的时候死活弹不出来,不用太纠结。我当年也是跑到这两关就停了,查了很多资料也没能在新版浏览器里完整复现。我的建议是:
- 读一下这两关的源码,理解Flash XSS的注入原理——Flash文件通过
ExternalInterface.call调用JavaScript时,如果参数可控,一样能造成XSS。 - 把精力放在前18关的复盘上,这才是这个靶场的精华。
6.4 20关通关后的payload复盘表
刷完整个靶场,我建议你按类别把用过的payload整理成一张表。这里是我当时的复盘框架,你可以直接参考:
| 关卡范围 | 核心绕过方式 | 代表payload思路 |
|---|---|---|
| Level 1-2 | 无过滤/属性闭合 | <script>alert(1)</script>、"><script>alert(1)</script> |
| Level 3-4 | 单双引号闭合 | '><script>alert(1)</script> |
| Level 5-6 | 黑名单过滤、大小写 | <img src=x onerror=alert(1)>、<ScRiPt> |
| Level 7 | 单次替换 | 双写<scr<script>ipt> |
| Level 8-9 | 伪协议+关键字校验 | javascript:编码、/*http://*/注释 |
| Level 10 | 隐藏参数发现 | 源码审计定位参数名 |
| Level 11-13 | 请求头注入 | Burp改Referer/UA/Cookie |
| Level 14-15 | 模板引擎/资源包含 | ng-include引用外部HTML |
| Level 16 | 空格过滤 | %0a换行替代空格 |
| Level 17-20 | 冷门标签/Flash | embed事件注入、Flash参数注入 |
这张表的价值不在于美观,而在于让你看到"过滤规则和绕过方式之间是一一对应的"。下次遇到新的过滤规则时,你可以快速对照这张表找到同类问题的解法。
7. 通关之后:xss-labs的正确打开方式与进阶方向
7.1 用xss-labs源码做一次代码审计复盘
20关通关之后,我强烈建议你做一件事:把每一关的源码从头到尾读一遍,重点标注它用了什么过滤函数、过滤顺序是什么、哪些字符被转义、哪些没被转义。你会发现一个很有意思的现象——很多关卡的过滤逻辑漏洞百出,原因往往是"过滤函数本身写得不够彻底",而不是"开发者水平差"。
比如有些关卡过滤了<script>却忘了过滤大小写,有些关卡替换了一次字符串却忘了处理重组后的payload,有些关卡只过滤了英文括号却忘了过滤反引号。这些细节,如果你直接看结论会觉得"过滤好弱啊",但只有亲手试错之后你才能理解:一个看起来弱智的过滤漏洞,背后往往是开发者对浏览器解析机制的理解不到位。
读源码时我还发现一个特别好的学习方法:对比相邻关卡之间的过滤差异。比如Level 5和Level 6,过滤规则几乎一模一样,只是多了几个关键词。这种微小的差异就是学习粒度——你要能说出多出的那几个关键词分别拦住了哪种payload。
7.2 从攻击视角切回防御视角:XSS正确防御的三板斧
刷靶场的目的终究是为了写更安全的代码。xss-labs通关之后,我对XSS防御的理解变得异常清晰,总结下来就是三板斧:
第一板斧:输入校验。只接受符合预期格式的数据。比如数字就只允许数字,字符串就限制长度和字符集。但输入校验不能替代输出编码,它只是减小攻击面。
第二板斧:输出编码。根据输出位置选择对应的编码方式。输出在HTML标签之间,就用HTML实体编码;输出在属性里,就用属性转义;输出在JavaScript里,就用JavaScript转义。xss-labs里大量payload之所以有效,就是因为开发者没有做输出编码,把原始输入直接拼进了HTML。
第三板斧:CSP(内容安全策略)。通过HTTP响应头限制页面能加载哪些资源、能执行哪些脚本。就算XSS注入成功了,只要CSP配置严格,恶意脚本照样跑不起来。这个现代防御手段,xss-labs里没有专门讲,但刷完靶场你应该能理解它的价值。
7.3 继续练什么:Pikachu、DVWA与PortSwigger靶场
xss-labs通关之后,我建议的进阶路线是这样的:
先刷Pikachu的XSS模块,这时候你会觉得它太简单——不要紧,目的是巩固"反射型、存储型、DOM型"三种类型在真实场景中的表现差异。尤其是存储型XSS,xss-labs里几乎没有涉及,但真实世界中的评论区、留言板、个人资料编辑都是存储型XSS的温床。
再刷DVWA的XSS模块,从low到high逐级打。DVWA的high等级会引入htmlspecialchars之类的函数以及基于浏览器的防护,你会第一次感受到"正规防御"的威力——前面学的很多payload彻底失灵了。这一步非常关键,它能打破你"绕过万能"的错觉。
然后是PortSwigger的XSS实验室,这是目前我认为最系统的在线XSS靶场。它有几十个实验,从反射型、存储型、DOM型到CSP绕过、Angular模板注入、mXSS,覆盖程度远超xss-labs。唯一的问题是它是英文环境,而且在线访问偶尔不稳定。建议等前面两个刷完再来。
最后是CTF真题。CTF里很少直接考"注入一个script能弹窗",而是会叠加各种过滤规则、编码混淆、前端框架特性。但你会发现,xss-labs里练出来的基础思路——看输出位置、判断过滤项、构造闭合——在CTF里同样适用,只是绕过的层数更多而已。
7.4 关于练习XSS的边界,说几句实在话
最后提醒一点,可能也是最重要的一点:XSS靶场练习必须在授权环境中进行。
xss-labs是本地靶场,怎么造都不违法,随便练。但当你学会这些技术之后,千万不要手痒去找真实网站测试。未经授权对线上系统做安全测试,哪怕是只发一个<script>alert(1)</script>,也属于越权行为,性质完全不一样。我在带新人的时候反复强调这个边界——技术无罪,但使用技术的场景必须合规。
如果你真的对真实环境的漏洞挖掘感兴趣,正确路径是走漏洞众测平台,那里有明确的授权范围和测试规则。你会发现在授权范围内做测试,不仅专业,而且能学到更多的防御对抗经验。
说回xss-labs本身,我个人刷完最大的收获不是会了多少payload,而是建立了一种"看到输入点就下意识分析数据流"的思维习惯。这个习惯在后来的代码审计、WAF规则写法和CTF比赛里帮了我很多。如果你是在校学生或者刚转行安全方向,把这个靶场扎扎实实刷完,绝对比刷十篇XSS入门文章管用。