名称: xss-预防 描述: 通过输入净化、输出编码、内容安全策略等方法预防跨站脚本攻击。适用于用户生成内容、富文本编辑器、Web应用安全,或遇到存储型XSS、反射型XSS、DOM操作、脚本注入错误时使用。
关键词: 净化, HTML编码, DOMPurify, CSP, 内容安全策略, 富文本编辑器, 用户输入, 转义, innerHTML, DOM操作, 存储型XSS, 反射型XSS, 输入验证, 输出编码, 可信类型, XSS攻击, 网络安全, 用户生成内容, 安全编码, 脚本注入, 基于DOM的XSS 许可证: MIT
XSS预防
概述
通过输入净化、输出编码、内容安全策略头和安全编码实践,实现全面的跨站脚本攻击预防。
使用时机
- 用户生成内容展示
- 富文本编辑器
- 评论系统
- 搜索功能
- 动态HTML生成
- 模板渲染场景
XSS攻击类型
| 类型 | 向量 | 防御 |
|---|---|---|
| 反射型 | URL参数 | 输出编码 |
| 存储型 | 数据库内容 | 输入净化 |
| 基于DOM型 | 客户端JS | 安全DOM API |
| 突变型 | HTML解析器特性 | 严格净化 |
输出编码 (Node.js)
function encodeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function encodeForAttribute(str) {
return str.replace(/[^\w.-]/g, char =>
`&#x${char.charCodeAt(0).toString(16)};`
);
}
// 在模板中使用
app.get('/profile', (req, res) => {
const username = encodeHTML(req.query.name);
res.send(`<h1>欢迎, ${username}</h1>`);
});
DOMPurify净化
import DOMPurify from 'dompurify';
const config = {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
ALLOWED_ATTR: ['href', 'title'],
ALLOW_DATA_ATTR: false
};
function sanitizeHTML(dirty) {
return DOMPurify.sanitize(dirty, config);
}
// React组件
function RichContent({ html }) {
return (
<div dangerouslySetInnerHTML={{ __html: sanitizeHTML(html) }} />
);
}
内容安全策略
// Express中间件
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.nonce = nonce;
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}'`,
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; '));
next();
});
安全DOM API
// 危险 - 避免使用
element.innerHTML = userInput; // XSS风险
element.outerHTML = userInput; // XSS风险
document.write(userInput); // XSS风险
eval(userInput); // 代码注入
// 安全 - 使用这些替代
element.textContent = userInput; // 自动转义
element.setAttribute('data-id', id); // 属性安全
document.createTextNode(userInput); // 创建安全文本节点
URL验证
function isSafeURL(url) {
try {
const parsed = new URL(url);
return ['http:', 'https:'].includes(parsed.protocol);
} catch {
return false;
}
}
// 用法
const href = isSafeURL(userURL) ? userURL : '#';
上下文特定编码
不同上下文需要不同的编码方法:
- HTML实体编码:用于文本内容的最安全选项
- 属性编码:用于HTML属性
- JavaScript转义:用于脚本上下文
- URL编码:用于URL参数
- CSS转义:用于样式表上下文
始终根据数据将渲染的特定上下文对输出进行编码。
其他实现
参见 references/python-sanitization.md 获取:
- Python bleach库用法
- Flask/Django模板转义
- 服务器端验证模式
参见 references/nodejs-advanced.md 获取:
- 包含所有方法的完整XSSPrevention类
- Express中间件 (xssProtection)
- React组件 (SafeText, SafeHTML, SafeLink, useSanitizedInput)
- Helmet CSP配置
最佳实践
✅ 推荐做法:
- 默认编码输出
- 使用带自动转义的模板引擎
- 实施CSP头
- 使用允许列表净化富内容
- 用协议白名单验证URL
- 使用HTTPOnly Cookie
- 进行定期安全测试
- 利用安全框架
❌ 不建议做法:
- 信任用户输入
- 使用不安全函数 (eval, innerHTML)
- 为便利性禁用安全功能
- 仅依赖客户端验证
- 使用阻止列表代替允许列表
- 跳过上下文特定编码
- 允许任意脚本执行
安全检查清单
- [ ] 根据上下文编码所有输出 (HTML, 属性, JS)
- [ ] 使用允许列表净化HTML (非阻止列表)
- [ ] 实施严格的CSP头
- [ ] 使用HTTPOnly Cookie进行会话
- [ ] 验证和净化URL
- [ ] 避免使用带用户内容的innerHTML
- [ ] 定期安全测试