Frontend Academy · 课时

XSS 防护:输出编码与 CSP

了解反射型、存储型和基于 DOM 的 XSS,转义用户控制的输出,避免将不可信数据传入 innerHTML,并添加内容安全策略响应头。

第 1 / 4 课14 个步骤

XSS 防护:输出编码与 CSP 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。

什么是 XSS

跨站脚本攻击发生在攻击者将可执行 JavaScript 注入您的页面时。浏览器会以您源的权限运行这些脚本,从而导致 Cookie 被窃取、会话被劫持、页面被篡改或用户被重定向到恶意软件。

XSS 的三种类型

存储型:恶意脚本保存在数据库中,并发送给每位访客。反射型:输入内容在响应中被原样返回(?q=<script>...)。基于 DOM:客户端 JavaScript 将不受信任的数据写入 DOM。

根本原因:使用不受信任的数据设置 innerHTML

使用 innerHTML(或 v-html、dangerouslySetInnerHTML)插入由用户控制的字符串,是最主要的 XSS 攻击途径。

// BAD: user input rendered as HTML
el.innerHTML = userComment;  // attacker submits <img onerror=alert(1)>

// Vue equivalent:
<div v-html="userComment"></div>  // same bug

// React equivalent:
<div dangerouslySetInnerHTML={{ __html: userComment }} />  // same bug

输出编码(安全默认值)

请将不受信任的字符串视为文本,而不是 HTML。React、Vue、Angular 和 Svelte 在 {value} 插值中默认都会这样处理。

// SAFE: text content, not HTML
el.textContent = userComment;  // <script> shows as literal text

// React (auto-escaped):
<div>{userComment}</div>

// Vue (auto-escaped):
<div>{{ userComment }}</div>

必须渲染 HTML 时进行清理

如果确实需要渲染用户提供的 HTML(例如富文本编辑器中的内容),请使用 DOMPurify 进行清理——切勿自行编写正则表达式清理器。

import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(userHtml);
el.innerHTML = clean; // strips <script>, on* handlers, dangerous URLs

URL 验证

href 或 src 中由用户控制的 URL 可能会通过 javascript: 协议触发 XSS。

// BAD:
<a href={userUrl}>Click</a>
// attacker submits: javascript:alert(1)

// GOOD: whitelist protocols:
function safeUrl(url) {
  if (!/^https?:\/\//.test(url) && !url.startsWith('/')) return '#';
  return url;
}

内容安全策略(CSP)

CSP 是一个 HTTP 响应标头,用于告知浏览器允许哪些脚本、样式、图像等资源来源。即使攻击者注入了脚本,浏览器也会拒绝执行它。

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.trusted.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:

使用随机数的严格 CSP

对于由您控制的内联脚本,请为每个请求使用一个 nonce。攻击者注入的脚本不会拥有匹配的 nonce。

// Server generates a random nonce per request:
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', `script-src 'nonce-${nonce}' 'strict-dynamic'`);

// Inject into the HTML:
<script nonce="abc123">/* trusted inline code */</script>

HttpOnly + 安全 Cookie

请使用 HttpOnly 设置会话 Cookie,使 JavaScript 无法读取它们——即使发生 XSS,攻击者也无法窃取会话令牌。

Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Strict

子资源完整性(SRI)

通过 CDN 加载第三方脚本时,请使用 SRI 验证内容是否与哈希值匹配。即使 CDN 已遭入侵,攻击者也无法在不破坏哈希检查的情况下提供恶意脚本。

<script
  src="https://cdn.example.com/lib.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"
></script>

Trusted Types(实验性功能)

Trusted Types 是一项浏览器功能(基于 Chromium),它会强制危险接收点(innerHTML)接收 Trusted Type 对象,从而完全阻止字符串被插入。

测试 XSS

尝试将 <img src=x onerror=alert(1)> 注入每个会被重新渲染的文本输入框。如果看到弹窗,就说明存在 XSS 漏洞。请使用 OWASP ZAP 等工具执行自动化扫描。

快速检查

为什么 React 的 {userInput} 默认是安全的,而 dangerouslySetInnerHTML 却不安全?

回顾:XSS 防御

默认使用文本渲染(React 的 {x}、Vue 的 {{ x }} 是安全的)。渲染 HTML 时,请使用 DOMPurify 进行清理。为 URL 协议设置允许列表。CSP 标头(配合随机数或 'strict-dynamic')可以阻止注入的脚本。HttpOnly Cookie 可以防止 JS 窃取令牌。为 CDN 脚本使用 SRI 哈希。使用经典攻击载荷测试输入。

免费开始

用 AI 导师学习 HTML — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
41
课程
163

常见问题解答

「XSS 防护:输出编码与 CSP」课时是免费的吗?

是的 — 「XSS 防护:输出编码与 CSP」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。

「XSS 防护:输出编码与 CSP」这节课中我会学到什么?

了解反射型、存储型和基于 DOM 的 XSS,转义用户控制的输出,避免将不可信数据传入 innerHTML,并添加内容安全策略响应头。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Frontend Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「XSS 防护:输出编码与 CSP」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Frontend Academy 课中编写并运行代码吗?

能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. XSS 防护:输出编码与 CSP
  2. CSRF:SameSite Cookie 与令牌
  3. 内容安全策略:随机数与哈希
  4. 前端 OAuth 流程
← 返回 Frontend Academy