跳到主要内容

XSS / CSRF 漏洞修复实战

场景

安全团队报告项目存在 XSS 和 CSRF 漏洞,需要紧急修复。

XSS 修复

1. 三种 XSS 类型与修复

类型触发方式修复方案
存储型 XSS恶意内容存入数据库后渲染输出编码 + CSP
反射型 XSSURL 参数直接插入页面输入过滤 + 输出编码
DOM 型 XSSJS 直接操作 DOM 插入用户数据避免 innerHTML

2. 修复代码

xss-prevention.ts
// ❌ 危险操作
element.innerHTML = userInput;
document.write(userInput);

// ✅ 安全操作
element.textContent = userInput;

// ✅ 需要渲染 HTML 时,使用白名单过滤(DOMPurify)
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userContent, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
ALLOWED_ATTR: ['href', 'target'],
});
csp-header.ts
// CSP 策略(后端设置 HTTP Header)
// Content-Security-Policy: default-src 'self';
// script-src 'self' 'nonce-abc123';
// style-src 'self' 'unsafe-inline';
// img-src 'self' data: https:;

// React 中防止 XSS
// React 默认对 JSX 内容进行转义
// ❌ 唯一的风险:dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }} />

3. URL 参数防护

url-param-safe.ts
// ❌ 直接使用 URL 参数
const query = new URLSearchParams(location.search).get('q');
document.getElementById('search')!.innerHTML = `搜索:${query}`;

// ✅ 使用 textContent
document.getElementById('search')!.textContent = `搜索:${query}`;

// ✅ URL 中 javascript: 协议防护
function isSafeUrl(url: string): boolean {
try {
const parsed = new URL(url, location.origin);
return ['http:', 'https:', 'mailto:'].includes(parsed.protocol);
} catch {
return false;
}
}

CSRF 修复

1. CSRF Token

csrf-protection.ts
// 后端生成 CSRF Token 放在 cookie(httpOnly: false)
// 前端从 cookie 读取并放入请求头

function getCsrfToken(): string {
const match = document.cookie.match(/csrf_token=([^;]+)/);
return match?.[1] ?? '';
}

// Axios 全局配置
import axios from 'axios';
axios.defaults.headers.common['X-CSRF-Token'] = getCsrfToken();
// 后端 Set-Cookie 配置
Set-Cookie: session=xxx; SameSite=Strict; Secure; HttpOnly
SameSite 值效果
Strict完全禁止第三方 Cookie
Lax允许顶层导航的 GET 请求携带
None允许跨站(需配合 Secure)

常见面试问题

Q1: React/Vue 能完全防 XSS 吗?

答案

基本能,但有例外:

  • React 默认对 JSX 表达式转义({userInput} 是安全的)
  • Vue 的 {{ }} 模板语法默认转义
  • 例外dangerouslySetInnerHTML(React)/ v-html(Vue)会绕过转义,需要手动用 DOMPurify 过滤

答案

推荐同时使用

  • SameSite=Lax:简单有效,主流浏览器已默认支持
  • CSRF Token:防御更严密(应对 SameSite 不支持的旧浏览器)
  • 双重校验:Token + 自定义 Header(Ajax 请求自动带,表单提交不带,天然区分)

相关链接