前端防御 全景解析
从 XSS 到 CSRF,从 CSP 到零信任——用场景化的方式理解每一种攻击向量与防御策略, 让安全不再是后端的专属话题。
⚡ 三大威胁全景图
XSS
跨站脚本攻击
攻击者注入恶意脚本到网页,在用户浏览器中执行
CSRF
跨站请求伪造
利用用户已登录的身份,伪造请求执行非预期操作
CSP
内容安全策略
浏览器安全机制,限制页面可加载的资源来源
XSS 攻击
CROSS-SITE SCRIPTING
场景:评论区陷阱
攻击者发帖
在评论区提交包含恶意脚本的内容
服务端存储
恶意内容未经转义直接存入数据库(存储型 XSS)
用户浏览
正常用户访问页面,脚本在浏览器中自动执行
数据泄露
Cookie、Token 被发送到攻击者服务器
存储型 XSS
STORED🎯 攻击者将恶意脚本存入服务器(评论/帖子/个人资料)
💥 所有访问该页面的用户都会中招
🔴 严重反射型 XSS
REFLECTED🎯 恶意代码在 URL 参数中,服务端"反射"到页面响应
💥 需要诱骗用户点击恶意链接,典型: "?search=<script>"
🟡 中等DOM 型 XSS
DOM-BASED🎯 前端 JS 直接操作 DOM 时未过滤用户输入
💥 不经过服务器,纯前端漏洞,难以被 WAF 检测
🟠 较高✅ XSS 防御代码实战
function escapeHtml(str: string): string {
const map: Record<string, string> = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return str.replace(
/[&<>"']/g,
(c) => map[c]
);
}// ✅ React 默认安全 — 自动转义
<p>{userInput}</p>
// ❌ 危险!直接插入 HTML
<div dangerouslySetInnerHTML={{
__html: userInput // 💀 XSS!
}} />
// ✅ 必须先用 DOMPurify 消毒
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(userInput)
}} />输出编码
必须
输入验证
必须
HttpOnly Cookie
推荐
CSP 策略
推荐
CSRF 攻击
CROSS-SITE REQUEST FORGERY
场景:银行转账陷阱
受害者
已登录银行网站
恶意网站
藏有伪造请求
银行服务器
无法区分请求来源
<!-- 恶意网站中的隐藏表单 -->
<form action="https://bank.com/transfer"
method="POST"
id="csrf-form">
<input type="hidden"
name="to" value="attacker" />
<input type="hidden"
name="amount" value="99999" />
</form>
<!-- 页面加载后自动提交 -->
<script>
document.getElementById('csrf-form')
.submit();
</script>为什么能成功?
浏览器会自动携带目标域的 Cookie!银行服务器收到请求后,看到有合法的 Session Cookie, 就认为这是用户本人的操作。Cookie 无法区分"用户意愿"还是"自动提交"。
关键前提条件
- • 用户已在目标网站登录(有有效 Session)
- • 目标网站仅依赖 Cookie 进行身份验证
- • 操作可通过简单的 HTTP 请求完成
✅ CSRF 防御方案
CSRF Token
最常用服务端生成随机 Token,嵌入表单/请求头。攻击者无法获取该 Token。
<input type="hidden" name="_csrf" value="a8b3c9d..." />
SameSite Cookie
推荐设置 Cookie 的 SameSite 属性,阻止跨站携带 Cookie。
Set-Cookie: session=abc; SameSite=Strict; Secure; HttpOnly
双重验证
高安全敏感操作(转账/改密码)要求二次确认,如短信验证码、密码确认。
// 前端拦截 → 弹窗确认 const ok = await showConfirmDialog();
CSP 策略
CONTENT SECURITY POLICY
什么是 CSP?
Content Security Policy 是一个 HTTP 响应头,告诉浏览器只允许加载和执行哪些来源的资源。 它是 XSS 攻击的最后一道防线——即使攻击者注入了脚本,浏览器也会拒绝执行。
🛡️ CSP 如何阻止 XSS
// Next.js middleware / server headers
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{random}';
style-src 'self' 'unsafe-inline';
img-src 'self' https://cdn.example.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';📋 常用指令速查
script-src控制 JS 来源style-src控制 CSS 来源img-src控制图片来源connect-src控制 fetch/XHRfont-src控制字体来源frame-ancestors控制 iframe 嵌入base-uri限制 <base> 标签form-action限制表单提交目标🔑 关键字说明
'self'同源资源'none'禁止任何资源'unsafe-inline'允许内联代码'unsafe-eval'允许 eval()'nonce-{val}'白名单随机数'sha256-{hash}'哈希白名单更多防御策略
ADDITIONAL SECURITY LAYERS
Cookie 安全属性
HttpOnlyJS 无法读取,防 XSS 窃取Secure仅通过 HTTPS 传输SameSite限制跨站携带,防 CSRF__Host-必须 Secure + 同源HTTP 安全响应头
X-Content-Type-Optionsnosniff — 禁止 MIME 嗅探
X-Frame-OptionsDENY — 禁止 iframe 嵌入
X-XSS-Protection0 — 禁用旧版 XSS 过滤
Referrer-Policystrict-origin — 限制 Referer 信息
Permissions-Policycamera=() — 禁用浏览器 API
SRI & CORS
🔐 子资源完整性 (SRI)
通过哈希校验外部资源是否被篡改
<script src="..." integrity="sha384-..." crossorigin>🌐 CORS 跨域策略
严格限制 Access-Control-Allow-Origin,避免使用 * 通配符
Access-Control-Allow-Origin: https://myapp.com📊 防御策略速查对比
⚛️ Next.js 项目安全实践
// middleware.ts
import { NextResponse } from 'next/server';
export function middleware(request) {
const nonce = crypto.randomUUID();
const response = NextResponse.next();
response.headers.set(
'Content-Security-Policy',
`default-src 'self';
script-src 'self' 'nonce-${nonce}';
style-src 'self' 'unsafe-inline';
img-src 'self' https://cdn.example.com;
connect-src 'self' https://api.myapp.com;
frame-ancestors 'none';`
);
response.headers.set(
'X-Content-Type-Options', 'nosniff'
);
response.headers.set(
'X-Frame-Options', 'DENY'
);
response.headers.set(
'Referrer-Policy', 'strict-origin'
);
return response;
}// app/actions/transfer.ts
'use server';
import { auth } from '@/lib/auth';
import { z } from 'zod';
const schema = z.object({
to: z.string().min(1).max(50),
amount: z.number().positive().max(10000),
});
export async function transfer(formData) {
// 1. 验证身份
const user = await auth();
if (!user) throw new Error('Unauthorized');
// 2. 输入验证 (防注入)
const data = schema.parse({
to: formData.get('to'),
amount: Number(formData.get('amount')),
});
// 3. 业务逻辑
// Next.js Server Actions 内置
// CSRF 保护(同源验证)
await db.transfer({
from: user.id,
to: data.to,
amount: data.amount,
});
}