高安全架构 学习笔记(第 13 部分)
6.3 XSS 防护
javascript
// XSS 防护
class XSSProtection {
constructor() {
// XSS 攻击模式
this.patterns = [
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
/javascript:/gi,
/on\w+\s*=/gi, // onclick=, onload= 等
/<iframe/gi,
/<object/gi,
/<embed/gi,
/expression\(.*?\)/gi
]
}
// 检测 XSS
detect(input) {
for (const pattern of this.patterns) {
if (pattern.test(input)) {
return {
safe: false,
pattern: pattern.toString(),
matched: input.match(pattern)
}
}
}
return { safe: true }
}
// 转义 HTML
escapeHtml(input) {
const htmlEntities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
}
return String(input).replace(/[&<>"'\/]/g, char => htmlEntities[char])
}
// 移除危险标签
stripTags(input) {
return input
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
.replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi, '')
.replace(/<embed\b[^>]*>/gi, '')
.replace(/on\w+\s*=\s*["'][^"']*["']/gi, '') // 移除事件处理属性
}
// CSP (内容安全策略) 设置
setCSP(res) {
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.example.com",
"style-src 'self' 'unsafe-inline' https://cdn.example.com",
"img-src 'self' data: https:",
"font-src 'self' https://cdn.example.com",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; '))
}
// XSS 防护中间件
middleware() {
return (req, res, next) => {
// 设置安全头
this.setCSP(res)
res.setHeader('X-XSS-Protection', '1; mode=block')
res.setHeader('X-Content-Type-Options', 'nosniff')
// 检查和清理输入
const sanitizeInput = (obj) => {
for (const key in obj) {
if (typeof obj[key] === 'string') {
const result = this.detect(obj[key])
if (!result.safe) {
console.warn(`XSS 检测: 字段 ${key}, 模式: ${result.pattern}`)
}
// 清理输入
obj[key] = this.escapeHtml(obj[key])
}
}
}
sanitizeInput(req.body)
sanitizeInput(req.query)
next()
}
}
}
// 使用示例
const xssProtection = new XSSProtection()
app.use(xssProtection.middleware())
// 在输出时再次转义
app.get('/user/:id', (req, res) => {
const user = getUser(req.params.id)
// 转义输出
const safeUser = {
name: xssProtection.escapeHtml(user.name),
bio: xssProtection.escapeHtml(user.bio)
}
res.json(safeUser)
})