XSS
XSS,跨站脚本攻击,即 Cross Site Scripting,为了和 CSS 不重名,所以取名为 XSS。
原理
攻击者通过畸形的输入,将恶意的 JS 代码插入到了页面中。当其他用户浏览该网页时,恶意代码会被执行,从而达到攻击的目的。
XSS 攻击发生的根本原因是对用户输入的信任。
分类
- 反射型 XSS
- 存储型 XSS
- DOM-XSS
反射型 XSS
用户的输入直接传到后台(不保存),后台未经过滤,直接将输入数据返回给前台。
http://example.com/search?param=<script>alert('XSS')</script>
攻击者将恶意 URL 通过邮件、聊天工具发送给受害者,诱导其点击。
存储型 XSS
最危险的一类,攻击过程:
- 攻击者在评论区提交恶意脚本
- 后台未经处理直接存储到数据库
- 其他用户请求该页面,恶意脚本随数据返回
- 用户打开页面,XSS 代码自动执行
DOM-XSS
纯粹发生在客户端的 XSS 攻击,不经过服务器。恶意代码通过修改页面 DOM 来执行。
// 漏洞代码:从 URL hash 获取内容直接写入 DOM
const hash = window.location.hash.substring(1);
document.getElementById('greeting').innerHTML = '你好, ' + hash;
// 攻击者构造:
// http://example.com/page#<img src=x onerror=alert(document.cookie)>
另一个常见场景:
// URL 参数未校验直接用于跳转
const url = new URLSearchParams(window.location.search).get('redirect');
// 攻击者构造: ?redirect=javascript:alert(document.cookie)
window.location.href = url;
防范 XSS
1. 输出编码
function escapeHtml(str) {
const map = {
'&': '&', '<': '<', '>': '>',
'"': '"', "'": ''', '/': '/'
};
return str.replace(/[&<>"'/]/g, char => map[char]);
}
// 更安全的做法:用 textContent 代替 innerHTML
element.textContent = userInput;
2. HttpOnly Cookie
JavaScript 无法读取设置了 HttpOnly 的 Cookie,即使 XSS 攻击成功也无法窃取:
// Node.js Express
res.cookie('sessionId', 'abc123', {
httpOnly: true,
secure: true,
sameSite: 'Strict'
});
3. Content Security Policy (CSP)
通过白名单机制限制页面可以加载和执行的资源:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src *;
CSP 部署后的效果:
- 内联
<script>标签中的代码不会执行 onclick等内联事件处理器失效eval()、new Function()被禁止- 非白名单域的外部脚本不会加载
CSRF
CSRF,跨站请求伪造,即 Cross Site Request Forgery。
原理
在受害者毫不知情的情况下以受害者名义发送请求。原理是借助受害者的 Cookie 来骗取服务器的信任。
攻击流程
1. 用户 T 登录银行网站 bank.com,获取 Cookie
2. 用户 T 未登出,访问了恶意网站 evil.com
3. evil.com 页面包含隐藏表单,自动向 bank.com 发起转账请求
4. 浏览器自动带上 bank.com 的 Cookie
5. bank.com 验证 Cookie 有效,执行转账
6. 用户 T 的钱被转走,全程无感知
恶意页面代码:
<form action="https://bank.com/transfer" method="POST" id="hack">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.getElementById('hack').submit();</script>
防范 CSRF
1. CSRF Token 验证
服务器生成随机 Token 嵌入表单,提交时验证 Token 有效性:
const crypto = require('crypto');
// 生成 Token
function generateCsrfToken(session) {
const token = crypto.randomBytes(32).toString('hex');
session.csrfToken = token;
return token;
}
// 验证中间件
function verifyCsrfToken(req, res, next) {
const token = req.body._csrf || req.headers['x-csrf-token'];
if (token !== req.session.csrfToken) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
next();
}
2. SameSite Cookie
限制第三方网站发起请求时是否携带 Cookie:
Set-Cookie: sessionId=abc123; SameSite=Lax; Secure; HttpOnly
Strict— 完全禁止第三方 CookieLax— 允许导航类跨站请求携带,POST 表单不携带None— 不限制(必须配合 Secure)
3. 双重 Cookie 验证
利用攻击者无法读取跨域 Cookie 的特点:
// 前端从 Cookie 中读取 Token 放到请求头
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': getCookie('csrf-token')
},
body: JSON.stringify(data)
});
// 攻击者能让浏览器发送 Cookie,但无法用 JS 读取跨域 Cookie 值
4. 检查 Referer / Origin
function checkOrigin(req, res, next) {
const origin = req.headers.origin || req.headers.referer;
if (!origin || !origin.startsWith('https://bank.com')) {
return res.status(403).json({ error: 'Invalid origin' });
}
next();
}
综合防护清单
XSS 防护:
- 所有用户输入做输出编码
- 使用
textContent代替innerHTML - 敏感 Cookie 设置
HttpOnly - 部署 CSP 策略
- URL 跳转禁止
javascript:协议
CSRF 防护:
- 关键操作添加 CSRF Token
- Cookie 设置
SameSite=Lax - 校验 Origin / Referer 头
- GET 请求不产生副作用
通用原则:
- HTTPS 全站部署
- Cookie 设置
Secure标志 - 敏感操作要求二次验证