XSS 攻防全解:反射型、存储型、DOM 型实战演示
文章目录
- 一、先建立正确心智:XSS 伤的是谁
- 二、三类 XSS:一张表建立直觉
- 三、反射型 XSS:实战演示思路
- 1. 它长什么样
- 2. 演示路径(请在靶场做)
- 3. 位置比 payload 重要
- 4. 反射型的修复直觉
- 四、存储型 XSS:实战演示思路
- 1. 它为什么更凶
- 2. 演示路径(靶场)
- 3. 富文本:存储型的重灾区
- 4. 存储型修复直觉
- 五、DOM 型 XSS:实战演示思路
- 1. 为什么说“服务端可能是清白的”
- 2. 演示路径(自建一小页即可)
- 3. 现代前端里的变体
- 4. DOM 型修复直觉
- 六、串起来的一条“实战演示”主线(适合内训)
- 七、测试方法论(比收藏 payload 重要)
- 1. 找入口
- 2. 探针优先
- 3. 看上下文再构造
- 4. 双浏览器 / 双账号
- 5. 自动化辅助,人工收口
- 八、防御全解:分层才像会打仗
- 1. 输出编码(第一原则)
- 2. 输入校验(辅助)
- 3. 富文本清理
- 4. Cookie:HttpOnly、Secure、SameSite
- 5. CSP(Content Security Policy)
- 6. 框架与危险 API 纪律
- 7. 安全头与其它
- 九、和 OWASP Top 10 的关系
- 十、常见误区
- 十一、收尾
XSS(Cross-Site Scripting,跨站脚本)听起来像某种高深的“跨站黑科技”,真落到业务里,多半是一句很土的话:
页面把用户可控的内容,当成了脚本执行了。
可能是搜索框把关键字原样写回 HTML;可能是评论区存了一段<script>,谁打开谁中招;也可能是前端用innerHTML拼了个 URL 参数,压根没经过服务端。三种常见面孔——反射型、存储型、DOM 型——本质同一类病:不可信数据进了浏览器的“代码语境”。
另外说句实在话:现在 HttpOnly、框架自动转义、CSP 越来越普及,有人宣布 XSS 已死。然后每年还能在重要系统里看见评论区、富文本、运营配置后台、老 jQuery 页面中招。死的是“无脑<script>alert(1)”,不是整个问题域。
一、先建立正确心智:XSS 伤的是谁
SQL 注入主要伤服务器和数据;XSS 主要伤正在用浏览器的人。
脚本跑在受害者浏览器里,能做的事情取决于页面所处的源(origin)和页面能力,常见包括:
- 偷当前站可用的数据(若 Cookie 非 HttpOnly,可能读到会话 Cookie;HttpOnly 则改走其它路);
- 以受害者身份发请求(改邮箱、转账、发帖),也就是挂着用户会话的 CSRF 式操作;
- 钓鱼:在真域名下画假登录框;
- 配合其它洞做跳板。
所以评估 XSS 时,别停在alert(1)。alert只是证明“我能执行脚本”。真正的问题是:在谁的浏览器里、以谁的身份、能碰到什么功能。
二、三类 XSS:一张表建立直觉
| 类型 | 脚本从哪来 | 典型触发 | 测试时盯什么 |
|---|---|---|---|
| 反射型 | 当前请求参数被立刻写进页面 | 点开带毒链接 | URL/表单参数是否进 HTML |
| 存储型 | 先存进服务器,以后每次打开都带出来 | 看帖、看消息、看后台 | 存储内容再展示的路径 |
| DOM 型 | 主要在前端 JS 里改 DOM | 改 hash/参数即可 | innerHTML、eval、危险 sink |
一句话区分:
- 反射:请求里来,响应里走,多半不进库;
- 存储:进库(或进能持久的地方),别人也会中;
- DOM:服务端可能完全干净,锅在前端。
三、反射型 XSS:实战演示思路
1. 它长什么样
最典型:搜索页。
你搜hello,页面显示“您搜索的是:hello”。若后端或模板把关键字不经编码塞进 HTML,把关键字换成可执行的 HTML/JS,浏览器就会执行。
受害者通常需要打开攻击者构造好的链接(或被钓鱼点开)。所以反射型常和社工、短链、广告投放一起出现。
2. 演示路径(请在靶场做)
环境:DVWA 反射型关卡,或自己写一个“回显参数”的页面。
步骤大致是:
- 找一个参数,它的值会出现在响应 HTML 里;
- 先提交一个无害标记,例如
xss_probe_12345,确认它原样出现在哪里(标题、属性、正文、脚本块里位置不同,手法不同); - 根据位置尝试能否形成可执行上下文——例如是否进了标签体、是否进了属性、是否进了 JavaScript 字符串;
- 用最简单的执行证明(教学上常用弹窗)证明脚本执行;
- 再讨论:若 Cookie 无 HttpOnly 会怎样(演示到“能读到什么”为止,别对外打真实账号)。
3. 位置比 payload 重要
同样是“注入字符串”,落点不同难度差很多:
- 落在 HTML 文本节点:常要形成标签;
- 落在属性值:要考虑引号闭合、事件属性;
- 落在已有
<script>字符串里:要考虑引号与编码; - 落在 URL 属性:可能变成
javascript:协议问题。
老手第一件事不是砸字典,是看回显点源码上下文。Chrome F12 比一百条盲打有用。
4. 反射型的修复直觉
模板引擎默认转义;输出到 HTML 用对应编码;能用纯文本就别用“可解析 HTML”。
业务若必须允许链接,用白名单标签的清理库,别自己写正则“过滤 script”。
四、存储型 XSS:实战演示思路
1. 它为什么更凶
脚本躺在服务器上(评论、文章、用户简介、工单回复、后台配置)。受害者只要正常浏览,就可能中招——不一定非要点奇怪链接。管理员一打开“用户反馈”,管理会话就可能交代。
所以存储型在危害评级里通常高于同类反射型:影响面是“谁会看到这条数据”。
2. 演示路径(靶场)
- 找会保存并再展示的功能:留言板最经典;
- 提交探针字符串,保存后打开详情页,看是否原样进 HTML;
- 换成可执行证明,用另一个浏览器配置/另一个账号打开,确认“存储后触发”;
- 观察触发角色:普通用户互看?还是仅管理员后台预览?角色决定危害。
3. 富文本:存储型的重灾区
产品经理常说:“评论要支持加粗和图片。”
于是上线富文本编辑器,服务端若只做半吊子过滤,攻击者就能塞事件属性、畸形标签、svg onload 之类。
这里有个残酷事实:自己写 HTML 过滤器几乎必输。用成熟的、默认安全的清理库(并保持更新),白名单标签与属性,去掉事件处理器和危险协议。
4. 存储型修复直觉
- 存可以存原文,出必须按上下文编码;或
- 存之前就清理成安全子集;
- 管理端预览也要走同一套安全输出,别“后台信任自己人”。
五、DOM 型 XSS:实战演示思路
1. 为什么说“服务端可能是清白的”
DOM XSS 的数据流主要在浏览器里:
源(source):location、hash、referrer、postMessage… ↓ 前端 JS 处理 汇(sink):innerHTML、document.write、eval、$('...').html()…服务端返回的 HTML 可能完全固定;脚本执行是前端自己用不可信数据去喂了危险 API。
传统只抓包看响应的测试,会漏掉 DOM XSS。要用浏览器调试,看参数进了哪段 JS。
2. 演示路径(自建一小页即可)
很多教程会写一个糟糕页面:从location.hash取字符串,赋给innerHTML。你改 URL 的#后面内容,页面就会执行。
练习时建议你亲自写坏再修好:
- 先复现危险写法;
- 改成
textContent或明确创建文本节点; - 若必须插 HTML,用经过清理的结果,并考虑 CSP。
3. 现代前端里的变体
- 前端路由把 query 写进页面;
postMessage没验 origin;- 从
localStorage读出以前存的脏数据再innerHTML; - 第三方小部件配置项进 DOM。
SPA 流行后,DOM XSS 的占比其实上升了——只是名字不总叫“跨站”,而叫“前端漏洞”。
4. DOM 型修复直觉
危险 sink 清单贴在团队 Wiki:禁止对不可信数据用innerHTML/document.write/ 拼on*属性。
React 默认文本转义有帮助,但dangerouslySetInnerHTML就是明示危险;Vue 的v-html同理。用之前问:数据从哪来?
六、串起来的一条“实战演示”主线(适合内训)
如果你要做一场两小时内部演示,我建议别三类各打一遍字典,而是讲一个故事:
场景:一个带搜索、评论、前端高亮关键词的小站点。
- 反射:搜索关键词回显 → 证明反射型;
- 存储:评论保存恶意内容 → 另一账号打开帖子中招;
- DOM:前端用 URL 参数高亮关键词,却
innerHTML→ 服务端已转义仍中招。
同一业务三种洞,学员一下就懂“输出编码要按场景做,前端也是战场”。
演示结束一定留 15 分钟讲修复提交:模板转义、评论清理、前端改textContent、加一层 CSP。否则演示就是表演玩火。
七、测试方法论(比收藏 payload 重要)
1. 找入口
所有用户可控且可能被展示的地方:参数、表单、上传文件名、头像 URL、回调地址、邮件模板、运营配置。
别忽略“只有管理员能看的数据”——那经常是存储型高危。
2. 探针优先
先用独一无二的字符串确认数据流,再谈执行。
乱砸 payload 会被 WAF 干扰,你也分不清是没回去还是被过滤。
3. 看上下文再构造
进 HTML、属性、JS、CSS、URL,编码方式不同。
“万能 payload”是幻觉;上下文才是地图。
4. 双浏览器 / 双账号
存储型必须验证“他人视角”。反射型验证“换个未登录/另一用户点链接”。
5. 自动化辅助,人工收口
爬虫式 XSS 扫描器能找简单反射;DOM 与业务型存储仍靠人。扫出来的东西要人工确认,减少把标签过滤误报当战果。
八、防御全解:分层才像会打仗
1. 输出编码(第一原则)
数据离开信任边界、进入 HTML/属性/JS 时,做对应上下文的编码。
在现代框架里,优先用默认转义的模板绑定,少拼原始 HTML。
2. 输入校验(辅助)
长度、格式、白名单字符——能减少垃圾,不能当唯一防线。攻击者编码花样太多。
3. 富文本清理
成熟库 + 白名单;升级跟依赖 SLA 走。
允许的标签里不要留事件属性;URL 只允许 http(s)。
4. Cookie:HttpOnly、Secure、SameSite
HttpOnly 让 JS 读不到会话 Cookie,能挡一类偷票;不挡以用户身份发请求的那类攻击,所以还要配合 CSRF 防护与敏感操作二次确认。
5. CSP(Content Security Policy)
CSP 像给浏览器下“脚本纪律”:默认禁止乱七八糟的内联脚本与陌生域脚本时,XSS 成功率会断崖下降。
落地难在兼容:要先 Report-Only 观察,再 enforce;尽量配合非ce 降低绕过。别指望一天全站完美 CSP,但关键登录域值得先做。
6. 框架与危险 API 纪律
禁止清单比鼓励清单好使:Code Review 扫dangerouslySetInnerHTML、v-html、innerHTML=。
发现一处,问数据源。
7. 安全头与其它
X-Content-Type-Options: nosniff等减少浏览器猜类型带来的意外。
完整安全头方案可另开一篇;这里只强调:XSS 不是只靠一个头解决。
九、和 OWASP Top 10 的关系
XSS 在旧版 Top 10 里单列多年,2021 起更多并入注入/其它类别的讨论,但业务上它仍独立存在。
和 A01 越权常联动:先 XSS 管理员,再改配置;和 A07 认证失败也联动:伪造登录框收口令。
修 XSS 时顺手看敏感操作有没有二次验证,性价比很高。
十、常见误区
“我们过滤了 script 关键字。”
大小写、编码、标签变形、事件属性、SVG,正则防 XSS 历史记录不太光彩。
“框架默认防 XSS,所以没事。”
直到有人用了危险 API,或把用户 HTML 当可信。
“有 WAF。”
WAF 挡一部分反射;存储与 DOM 经常绕开它的舒适区。
“HttpOnly 了,XSS 没用。”
攻击者仍可以让浏览器自己去点“删除账号”“导出数据”。
“内部系统不需要防 XSS。”
内部系统才常有高权限用户,存储型更值钱。
十一、收尾
反射型像钓鱼链接上的一次性炮弹;
存储型像埋在业务数据里的地雷;
DOM 型像前端自己挖的坑,服务端查岗都可能查不到。
攻的一方(授权测试)要会看上下文、会追数据流、会换角色验证;
防的一方要把输出编码、富文本纪律、危险 sink、CSP、Cookie 属性做成默认,而不是出了事再救火。
今晚若只做一件事:打开你们站点一个“用户输入会显示出来”的页面,在测试环境提交一个独一无二的探针字符串,然后View Source看它落在谁家里——标签体、属性,还是进了某段前端模板。
认清落点之后,你对 XSS 的理解会比背十个弹窗字符串扎实得多。
三类演示都走通以后,你会发现所谓“全解”其实不神秘:
数据在哪里变成了代码,就在哪里设防。
