XSS防护
XSS攻击的基本原理
XSS(跨站脚本攻击)是一种常见的Web安全漏洞,攻击者通过在网页中注入恶意脚本,当其他用户访问该页面时,这些脚本会在用户的浏览器中执行。XSS攻击主要分为三种类型:
- 反射型XSS:恶意脚本作为请求的一部分发送到服务器,然后服务器将脚本"反射"回响应中
- 存储型XSS:恶意脚本被永久存储在目标服务器上(如数据库)
- DOM型XSS:漏洞存在于客户端代码中,不涉及服务器响应
// 一个简单的反射型XSS示例
// 假设URL为:http://example.com/search?query=<script>alert('XSS')</script>
app.get('/search', (req, res) => {
const query = req.query.query;
res.send(`<p>搜索结果:${query}</p>`); // 危险!直接输出用户输入
});
Node.js中的XSS防护措施
输入验证和过滤
对所有用户输入进行严格的验证是防护XSS的第一道防线。验证应包括:
- 数据类型检查
- 长度限制
- 格式验证(如邮箱、电话号码等)
- 特殊字符过滤
// 使用validator库进行输入验证
const validator = require('validator');
function sanitizeInput(input) {
if (typeof input !== 'string') return '';
// 移除HTML标签
let clean = validator.stripLow(input);
clean = validator.escape(clean);
return clean;
}
app.post('/comment', (req, res) => {
const comment = sanitizeInput(req.body.comment);
// 存储处理后的评论
});
输出编码
在将数据输出到HTML页面时,必须进行适当的编码:
- HTML实体编码:将特殊字符转换为HTML实体
- JavaScript编码:在动态生成JavaScript代码时使用
- URL编码:在URL参数中使用
// 使用he库进行HTML实体编码
const he = require('he');
function safeOutput(text) {
return he.encode(text, {
useNamedReferences: true,
strict: true
});
}
app.get('/profile', (req, res) => {
const username = req.query.username || '匿名';
res.send(`<h1>欢迎,${safeOutput(username)}</h1>`);
});
使用安全模板引擎
现代模板引擎通常内置了XSS防护功能:
- EJS:默认转义输出
- Pug/Jade:使用=输出时会自动转义
- Handlebars:使用{{}}输出时会自动转义
// 使用EJS模板引擎的示例
const ejs = require('ejs');
app.set('view engine', 'ejs');
app.get('/product/:id', (req, res) => {
const product = {
id: req.params.id,
name: req.query.name || '默认产品'
};
res.render('product', { product });
});
// product.ejs
<h2><%= product.name %></h2> <!-- 自动转义 -->
设置HTTP安全头
通过设置HTTP响应头可以增强XSS防护:
// 使用helmet中间件设置安全头
const helmet = require('helmet');
app.use(helmet());
// 等同于
app.use(helmet.xssFilter()); // 设置X-XSS-Protection
app.use(helmet.frameguard()); // 防止点击劫持
app.use(helmet.noSniff()); // 防止MIME类型嗅探
app.use(helmet.hsts()); // HTTP严格传输安全
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"]
}
}));
CSP(内容安全策略)的实施
内容安全策略是现代Web应用中防护XSS的最有效手段之一。它通过白名单机制控制哪些资源可以被加载和执行。
// 自定义CSP策略
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' https://trusted.cdn.com 'unsafe-inline'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data:; " +
"font-src 'self'; " +
"connect-src 'self'; " +
"frame-src 'none'; " +
"object-src 'none'"
);
next();
});
处理富文本内容的XSS防护
对于需要保留HTML格式的用户输入(如评论系统),需要更精细的过滤:
// 使用xss库处理富文本
const xss = require('xss');
const myxss = new xss.FilterXSS({
whiteList: {
a: ['href', 'title', 'target'],
p: [],
br: [],
strong: [],
em: []
},
stripIgnoreTag: true,
onTagAttr: (tag, name, value) => {
// 只允许http/https链接
if (tag === 'a' && name === 'href') {
if (/^https?:\/\//.test(value)) {
return name + '="' + xss.escapeAttrValue(value) + '"';
}
return '';
}
}
});
app.post('/rich-comment', (req, res) => {
const cleanHtml = myxss.process(req.body.content);
// 存储处理后的HTML
});
Cookie安全设置
防止通过XSS攻击窃取Cookie:
// 安全设置Cookie
app.use(session({
secret: 'your-secret-key',
cookie: {
httpOnly: true, // 防止JavaScript访问
secure: true, // 仅HTTPS
sameSite: 'strict', // 防止CSRF
maxAge: 24 * 60 * 60 * 1000
}
}));
实时XSS检测
在开发阶段可以使用工具进行XSS漏洞检测:
// 使用xss-filters库进行实时检测
const xssFilters = require('xss-filters');
app.post('/api/data', (req, res) => {
const userInput = req.body.input;
try {
// 如果检测到XSS尝试会抛出异常
xssFilters.inHTMLData(userInput);
// 安全处理
res.json({ status: 'success' });
} catch (err) {
// 记录XSS尝试
console.warn('XSS attempt detected:', userInput);
res.status(400).json({ error: 'Invalid input' });
}
});
前端框架的XSS防护
现代前端框架如React、Vue和Angular都内置了XSS防护机制:
// React示例 - 默认转义所有变量
function UserProfile({ username }) {
// 安全,username会被自动转义
return <div>Hello, {username}</div>;
}
// 危险!使用dangerouslySetInnerHTML时需要特别小心
function RichText({ content }) {
const cleanHtml = sanitize(content); // 必须先净化
return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;
}
日志记录和监控
记录所有潜在的XSS攻击尝试对于安全审计至关重要:
// 使用winston记录安全事件
const winston = require('winston');
const { Loggly } = require('winston-loggly-bulk');
const logger = winston.createLogger({
transports: [
new winston.transports.File({ filename: 'security.log' }),
new Loggly({
token: 'your-loggly-token',
subdomain: 'your-subdomain',
tags: ['security', 'xss'],
json: true
})
]
});
app.use((req, res, next) => {
// 检查请求中是否包含可疑脚本
if (JSON.stringify(req.body).includes('<script>')) {
logger.warn('Potential XSS attempt', {
url: req.url,
ip: req.ip,
payload: req.body
});
}
next();
});
定期安全审计
定期进行安全审计和代码审查是保持应用安全的重要环节:
- 使用自动化工具扫描漏洞(如OWASP ZAP、Burp Suite)
- 进行手动代码审查,特别关注所有用户输入处理点
- 保持依赖库更新,及时修补已知漏洞
- 进行渗透测试,模拟真实攻击场景
// 使用npm audit检查依赖漏洞
// package.json
{
"scripts": {
"audit": "npm audit --production",
"audit:fix": "npm audit fix --production"
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn