跨站脚本攻击(Cross-Site Scripting,简称 XSS)是 Web 安全领域中最常见、最持久的漏洞类型之一。尽管现代框架和浏览器不断引入新的防护机制,XSS 依然在 OWASP Top 10 中占据重要位置。本文将从漏洞原理出发,系统梳理反射型、存储型和 DOM 型三种 XSS 的挖掘方法与防御策略。
一、XSS 漏洞的本质
XSS 的核心在于:攻击者能够将恶意脚本注入到网页中,并使其在其他用户的浏览器中执行。这打破了同源策略的边界——原本只应运行受信任代码的页面,却执行了攻击者可控的 JavaScript,从而导致 Cookie 窃取、会话劫持、钓鱼攻击、键盘记录等一系列严重后果。
从数据流的角度看,一个 XSS 漏洞的产生需要满足三个条件:
- 入口:攻击者能够向 Web 应用输入可控数据;
- 出口:这些数据未经充分处理就被输出到 HTML 页面中;
- 执行:浏览器将输出内容解析为可执行的脚本代码。
二、反射型 XSS
2.1 原理
反射型 XSS(Reflected XSS)是最常见的形式。攻击者构造一个带有恶意脚本的 URL,诱导受害者点击。服务器将 URL 中的参数直接“反射”回响应页面中,浏览器随即执行其中的脚本。
例如,一个搜索页面将关键词直接输出到结果页:
https://example.com/search?q=<script>alert(document.cookie)</script>
如果服务端未做转义,页面就会执行这段脚本。
2.2 挖掘方法
- 参数遍历:对 URL 中所有 GET/POST 参数逐一注入测试 payload,观察是否被原样输出。
- 编码绕过:尝试 URL 编码、HTML 实体编码、双写等方式绕过简单过滤。
- 上下文判断:确认输出点位于 HTML 标签之间、属性值内还是 JavaScript 代码块中,不同上下文需要不同的闭合方式。
- 工具辅助:Burp Suite 的 Intruder 模块、XSStrike 等可自动化检测。
2.3 特点
反射型 XSS 不具有持久性,每次攻击都需要诱导用户点击特定链接。因此,它常被用于钓鱼邮件或社交工程攻击中。
三、存储型 XSS
3.1 原理
存储型 XSS(Stored XSS)的危害性最大。攻击者将恶意脚本提交到服务器并持久化存储(如数据库、文件系统),当其他用户访问包含该数据的页面时,脚本自动执行。
典型场景包括:评论区、用户昵称、论坛帖子、日志展示页面等。
3.2 挖掘方法
- 寻找持久化输入点:任何会被保存并展示给其他用户的功能都值得关注。
- 测试输出位置:提交 payload 后,检查它在哪些页面、以何种方式被渲染。
- 关注富文本编辑器:富文本场景下过滤规则往往更复杂,容易出现遗漏。
- 二次注入:某些数据可能先存储、后在管理后台或邮件模板中被渲染,形成“存储 + 反射”的组合。
3.3 特点
存储型 XSS 无需诱导点击,只要用户访问受影响的页面就会中招,且影响范围可能覆盖大量用户,甚至形成蠕虫式传播(如经典的 Samy 蠕虫)。
四、DOM 型 XSS
4.1 原理
DOM 型 XSS(DOM-based XSS)的独特之处在于:恶意数据不经过服务器端处理,完全在客户端 JavaScript 中完成“源(source)→ 汇(sink)”的流动。
常见的 source 包括:location.href、location.hash、document.referrer、window.name 等。
危险的 sink 包括:innerHTML、outerHTML、document.write、eval、setTimeout 等。
例如:
var name = location.hash.substring(1);
document.getElementById("greeting").innerHTML = "Hello, " + name;
访问 https://example.com/#<img src=x onerror=alert(1)> 即可触发。
4.2 挖掘方法
- 审计 JavaScript 代码:重点查找 source 到 sink 的赋值链路。
- 使用浏览器开发者工具:在 Sources 面板中设置断点,追踪数据流。
- 动态测试:在 hash、referrer 等位置注入 payload,观察 DOM 变化。
- 工具:DOM Invader(Burp Suite 内置)、Chrome DevTools 的 DOM 断点功能。
4.3 特点
DOM 型 XSS 难以被服务端 WAF 检测,因为恶意 payload 从不发送到服务器。服务端日志中也看不到攻击痕迹,给检测和取证带来挑战。
五、三种类型的对比
| 类型 | 数据是否经过服务器 | 持久性 | 典型触发方式 | 检测难度 |
|---|---|---|---|---|
| 反射型 | 是 | 否 | 诱导点击恶意链接 | 中 |
| 存储型 | 是 | 是 | 访问被污染页面 | 低(但危害大) |
| DOM 型 | 否 | 否 | 诱导点击特殊 URL | 高 |
六、防御策略
6.1 输出编码
根据输出上下文(HTML、属性、JavaScript、CSS、URL)采用对应的编码方式。例如,HTML 上下文使用 HTML 实体编码,JavaScript 上下文使用 \x 转义。这是最根本的防御手段。
6.2 输入验证与白名单
对用户输入进行格式校验,尽可能采用白名单策略。例如,邮箱字段只允许合法邮箱格式,年龄字段只允许数字。
6.3 内容安全策略(CSP)
通过 HTTP 响应头 Content-Security-Policy 限制脚本来源,禁止内联脚本执行。例如:
Content-Security-Policy: default-src 'self'; script-src 'self'
CSP 能有效缓解 XSS 的影响,但配置不当也可能被绕过。
6.4 使用安全 API
避免使用 innerHTML、document.write 等危险 API,改用 textContent、setAttribute 等安全方法。现代前端框架(React、Vue)默认对插值进行转义,但 dangerouslySetInnerHTML、v-html 等仍需谨慎。
6.5 HttpOnly Cookie
为敏感 Cookie 设置 HttpOnly 属性,使 JavaScript 无法读取,从而降低会话劫持风险。
6.6 其他措施
- 设置
X-XSS-Protection(虽已逐渐被 CSP 取代); - 对富文本使用成熟的白名单过滤库(如 DOMPurify);
- 定期进行安全测试与代码审计。
七、结语
XSS 漏洞看似简单,实则变化多端。反射型、存储型和 DOM 型各有其独特的挖掘思路与防御重点。理解数据从输入到输出的完整链路,是发现和修复 XSS 的关键。在实际工作中,应将输出编码作为默认习惯,辅以 CSP、安全 API 和持续的安全测试,构建多层次的防御体系。唯有如此,才能在这个“永远在线”的时代,守住用户浏览器的最后一道防线。
未经允许不得转载:任鹏个人博客 » XSS 漏洞全解析:反射型、存储型与 DOM 型跨站脚本攻击的挖掘与防御

