Web 安全 XSS 与 CSRF 攻击防护实战教程
Web 安全是每个开发者的必修课。XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)是最常见的两类 Web 攻击,本文将从原理到实战,详细讲解这两种攻击方式及其防护策略,帮助你构建更安全的 Web 应用。
一、XSS 攻击原理与类型
XSS(Cross-Site Scripting,跨站脚本攻击)是一种代码注入攻击,攻击者通过在网页中注入恶意脚本,当其他用户浏览网页时,脚本会在用户浏览器中执行,从而窃取用户信息或执行恶意操作。
1. 反射型 XSS
反射型 XSS 是最常见的 XSS 类型。攻击者将恶意脚本作为参数嵌入 URL 中,当用户点击该链接时,服务器将参数值直接返回并渲染到页面上,导致脚本执行。
攻击场景示例:
// 搜索结果页面代码
const searchQuery = new URLSearchParams(window.location.search).get('q');
document.getElementById('result').innerHTML = `您搜索的是:${searchQuery}`;
// 攻击者构造的恶意 URL:
// https://example.com/search?q=
// 用户点击后,恶意脚本立即执行
这种攻击通常通过诱导用户点击恶意链接实施,如钓鱼邮件、社交工程等。
2. 存储型 XSS
存储型 XSS 是最危险的 XSS 类型。攻击者将恶意脚本持久化存储在目标服务器(如数据库、文件系统),当用户浏览包含该恶意脚本的页面时,脚本被执行。
攻击场景示例:
// 用户发表评论,未经过滤直接存储
const comment = req.body.comment;
db.query('INSERT INTO comments SET ?', { content: comment });
// 攻击者提交的评论内容:
//
// 其他用户查看评论页面时
comments.forEach(c => {
element.innerHTML += `${c.content}`; // 恶意脚本执行
});
这种攻击影响范围广、持续时间长,常见于论坛评论、用户资料、消息系统等功能。
3. DOM 型 XSS
DOM 型 XSS 完全发生在客户端,不经过服务器。攻击者通过修改页面的 DOM 环境来执行恶意脚本。
攻击场景示例:
// 使用 location.hash 直接操作 DOM const content = location.hash.substring(1); document.write(decodeURIComponent(content)); // 攻击 URL: // https://example.com#// 或者使用 eval 处理用户输入 const callback = location.search.match(/callback=([^&]*)/); if (callback) { eval(callback[1]); // 极度危险! }
DOM 型 XSS 的特点是不依赖服务器返回,完全由前端 JavaScript 逻辑缺陷导致。
二、XSS 防护策略
1. 输入输出编码
对所有用户输入进行 HTML 实体编码,将特殊字符转换为 HTML 实体:
// HTML 编码函数
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// 使用示例
const userInput = '';
const safeOutput = escapeHtml(userInput);
// 结果: <script>alert("XSS")</script>
2. 使用安全的 API
避免使用 innerHTML、outerHTML、document.write() 等危险 API,改用安全的替代方案:
// ❌ 危险写法
element.innerHTML = userInput;
// ✅ 安全写法
element.textContent = userInput; // 自动编码
element.innerText = userInput;
// 如果必须插入 HTML,使用 DOM API
const div = document.createElement('div');
div.textContent = userInput;
container.appendChild(div);
3. 内容安全策略(CSP)
CSP 是一个强大的安全层,通过 HTTP 响应头限制资源加载来源:
// Node.js 设置 CSP 头
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' https://trusted.cdn.com; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"connect-src 'self' https://api.example.com"
);
next();
});
// HTML meta 标签方式
//
4. HttpOnly Cookie
设置 Cookie 的 HttpOnly 属性,防止 JavaScript 读取敏感 Cookie:
// Node.js Express 设置 HttpOnly Cookie
res.cookie('sessionId', token, {
httpOnly: true, // JS 无法读取
secure: true, // 仅 HTTPS 传输
sameSite: 'strict', // 防止 CSRF
maxAge: 3600000
});
三、CSRF 攻击原理
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种利用用户已认证状态发起恶意请求的攻击。攻击者诱导已登录用户访问恶意页面,该页面自动向目标网站发送请求,由于用户已认证,请求会被服务器接受。
攻击流程
- 用户登录受信任网站 A,获得认证 Cookie
- 用户未登出网站 A,同时访问攻击者网站 B
- 网站 B 包含向网站 A 发送请求的代码
- 浏览器自动携带网站 A 的 Cookie,请求成功执行
攻击场景示例:
用户访问攻击者页面后,转账请求会自动发送,浏览器携带银行网站的认证 Cookie,银行服务器无法区分这是用户主动操作还是被伪造的请求。
四、CSRF 防护方案
1. CSRF Token
为每个会话生成唯一的 CSRF Token,嵌入表单或请求头中:
// 生成 CSRF Token
const crypto = require('crypto');
function generateCsrfToken() {
return crypto.randomBytes(32).toString('hex');
}
// 服务器端设置
app.get('/form', (req, res) => {
const csrfToken = generateCsrfToken();
req.session.csrfToken = csrfToken;
res.render('form', { csrfToken });
});
// 表单中嵌入 Token
/*
*/
// 验证 Token
app.post('/submit', (req, res) => {
const { _csrf } = req.body;
if (_csrf !== req.session.csrfToken) {
return res.status(403).send('CSRF 验证失败');
}
// 处理请求
});
2. SameSite Cookie
设置 Cookie 的 SameSite 属性,限制跨站请求携带 Cookie:
// SameSite 属性说明
res.cookie('sessionId', token, {
sameSite: 'strict' // 完全禁止跨站发送
// sameSite: 'lax' // 允许安全的跨站导航请求
// sameSite: 'none' // 允许跨站发送(需配合 secure)
});
// 完整配置示例
res.cookie('auth', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
domain: '.example.com',
path: '/',
maxAge: 86400000
});
3. 验证 Referer 头
检查请求来源是否合法:
// 中间件验证 Referer
function checkReferer(req, res, next) {
const referer = req.get('Referer');
const allowedOrigins = ['https://example.com', 'https://www.example.com'];
if (!referer) {
return res.status(403).send('缺少 Referer 头');
}
const refererUrl = new URL(referer);
if (!allowedOrigins.includes(refererUrl.origin)) {
return res.status(403).send('非法来源');
}
next();
}
app.post('/sensitive-action', checkReferer, handler);
4. 双重 Cookie 验证
将 Token 同时放在 Cookie 和请求参数中,验证两者是否一致:
// 设置双重 Cookie
app.get('/api/data', (req, res) => {
const csrfToken = generateCsrfToken();
res.cookie('csrfToken', csrfToken, { httpOnly: false }); // 前端可读
res.json({ csrfToken });
});
// 前端请求时携带 Token
fetch('/api/action', {
method: 'POST',
headers: {
'X-CSRF-Token': getCookie('csrfToken')
},
body: JSON.stringify(data)
});
// 后端验证
app.post('/api/action', (req, res) => {
const cookieToken = req.cookies.csrfToken;
const headerToken = req.headers['x-csrf-token'];
if (!cookieToken || cookieToken !== headerToken) {
return res.status(403).send('CSRF 验证失败');
}
// 处理请求
});
五、安全最佳实践
1. 纵深防御原则
不要依赖单一防护措施,应组合多种防护策略:
// 综合防护配置
const securityMiddleware = [
// 1. CSP 策略
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}),
// 2. CSRF 防护
csrf({ cookie: { httpOnly: true, secure: true, sameSite: 'strict' } }),
// 3. XSS 过滤头
helmet.xssFilter(),
// 4. 禁止 MIME 类型嗅探
helmet.noSniff(),
// 5. 引用策略
helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' })
];
2. 安全编码规范
- 永不信任用户输入:所有输入都应被视为恶意数据
- 最小权限原则:只授予必要的权限
- 安全默认:默认配置应为最安全选项
- 失败安全:验证失败时应拒绝而非放行
3. 安全测试清单
// 自动化安全测试示例
const securityTests = {
// XSS 测试用例
xssPayloads: [
'',
'
',
'">',
"javascript:alert('XSS')",
'
4. 监控与应急响应
建立安全事件监控机制,及时发现问题:
// 安全日志记录
const securityLogger = {
logSuspiciousActivity: (req, type, details) => {
console.warn({
timestamp: new Date().toISOString(),
type: type, // 'XSS_ATTEMPT', 'CSRF_FAILURE', etc.
ip: req.ip,
userAgent: req.get('User-Agent'),
path: req.path,
details: details
});
},
// 集成告警系统
alertTeam: (severity, message) => {
if (severity === 'HIGH') {
// 发送邮件/短信告警
sendAlert(message);
}
}
};
六、总结
XSS 和 CSRF 是 Web 安全领域的两大经典威胁,理解它们的攻击原理是有效防护的基础。XSS 攻击通过注入恶意脚本窃取用户数据,防护核心在于输入编码、输出转义、内容安全策略;CSRF 攻击利用用户认证状态伪造请求,防护核心在于CSRF Token、SameSite Cookie、来源验证。
在实际开发中,应当:
- 建立安全意识,将安全融入开发全流程
- 采用纵深防御策略,多重防护叠加
- 定期进行安全审计和渗透测试
- 保持框架和依赖库及时更新
- 建立安全应急响应机制
安全是一个持续的过程,而非一次性的任务。只有不断学习新的攻击手段和防护技术,才能构建真正安全的 Web 应用。
记住:永远不要信任来自用户的任何数据!
本文链接:https://www.kkkliao.cn/?id=958 转载需授权!
版权声明:本文由廖万里的博客发布,如需转载请注明出处。



手机流量卡
免费领卡
号卡合伙人
产品服务
关于本站
