JavaScript安全防护:XSS与CSRF全面防御指南
JavaScript安全防护:从XSS到加密API的全面防御策略
1. XSS攻击与防御
XSS(跨站脚本攻击)是Web应用中最常见的安全威胁之一,攻击者通过注入恶意脚本到网页中,在用户浏览时执行这些脚本。
XSS类型分类
- 存储型XSS:恶意脚本永久存储在目标服务器(如评论区)
- 反射型XSS:恶意脚本来自当前HTTP请求(如搜索结果的URL参数)
- DOM型XSS:客户端JavaScript不安全的处理导致(如innerHTML操作)
防御措施
// 不安全的做法
document.getElementById('output').innerHTML = userInput;
// 安全的做法 - 使用textContent代替innerHTML
document.getElementById('output').textContent = userInput;
// 或者使用专门的库进行转义
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
实践建议:
- 对所有不可信数据进行HTML转义
- 使用现代前端框架(React/Vue/Angular),它们默认提供XSS防护
- 设置
Content-Type
头部(如application/json
)避免浏览器误解析 - 使用CSP(后面会详细介绍)作为最后防线
2. CSRF防护(SameSite/Token)
CSRF(跨站请求伪造)攻击诱使用户在不知情的情况下提交恶意请求。
防御技术对比
防御方法 | 实现方式 | 优点 | 缺点 |
---|---|---|---|
SameSite Cookie | 设置Cookie的SameSite属性 | 简单易用,浏览器原生支持 | 兼容性问题(旧浏览器) |
CSRF Token | 服务端生成并验证随机Token | 可靠性高 | 实现复杂度高 |
双重Cookie验证 | 请求中携带额外的Cookie信息 | 实现简单 | 安全性略低于Token |
SameSite Cookie实现
// Express设置SameSite Cookie示例
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.cookie('sessionID', '12345', {
httpOnly: true,
secure: true,
sameSite: 'strict' // 或 'lax'
});
next();
});
CSRF Token实现
// 服务端生成Token
const crypto = require('crypto');
function generateCSRFToken() {
return crypto.randomBytes(32).toString('hex');
}
// 前端在表单中嵌入Token
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<!-- 其他表单字段 -->
</form>
实践建议:
- 对于关键操作(如转账、修改密码)同时使用SameSite和CSRF Token
- SameSite属性设置为
Strict
或Lax
- CSRF Token应是一次性的并设置合理有效期
3. CSP内容安全策略
CSP(Content Security Policy)通过白名单机制控制资源加载,是防御XSS的终极武器。
常见指令示例
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src *;
media-src media1.com media2.com;
frame-src youtube.com;
report-uri /csp-report-endpoint;
通过meta标签设置
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-eval'">
报告模式(不强制执行)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
实践建议:
- 从
default-src 'none'
开始,逐步添加必要规则 - 避免使用
'unsafe-inline'
和'unsafe-eval'
- 在生产环境前使用
Report-Only
模式测试 - 对动态生成的JS使用nonce或hash白名单
4. 沙箱机制(Sandbox)
沙箱通过隔离执行环境限制代码权限,常用于插件系统或第三方代码执行。
iframe沙箱示例
<iframe sandbox="allow-scripts allow-same-origin"
src="https://third-party.com/widget"></iframe>
Node.js VM模块创建沙箱
const vm = require('vm');
const context = {
console,
setInterval,
setTimeout
};
vm.createContext(context); // 创建安全上下文
const script = new vm.Script(`
function fib(n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
fib(10);
`);
script.runInContext(context); // 在隔离环境中执行
实践建议:
- 明确沙箱需要的最小权限集
- 对不可信代码使用进程级隔离(如Worker线程)
- 结合CSP增强沙箱安全性
- 定期审计沙箱逃逸漏洞
5. 加密API(Web Crypto)
Web Crypto API提供了浏览器原生加密功能,替代不安全的自定义加密方案。
常见加密操作示例
// 生成随机数
const array = new Uint8Array(16);
window.crypto.getRandomValues(array);
// SHA-256哈希
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
// AES-GCM加密
async function encryptData(secretData, password) {
const salt = crypto.getRandomValues(new Uint8Array(16));
const keyMaterial = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(password),
{ name: 'PBKDF2' },
false,
['deriveKey']
);
const key = await crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
new TextEncoder().encode(secretData)
);
return { salt, iv, encrypted };
}
实践建议:
- 始终使用标准算法(如AES-GCM、PBKDF2)
- 密钥管理比加密算法更重要
- 在服务端完成敏感操作
- 定期更新加密库和算法
安全防御体系全景图
综合防御策略:
- 输入验证 + 输出编码
- 最小权限原则
- 深度防御(多层防护)
- 定期安全审计
- 保持依赖项更新
通过组合这些安全措施,可以构建坚固的JavaScript应用防御体系,有效抵御大多数Web安全威胁。
评论已关闭