🍃前言
👋
你好呀,我是你的人类朋友!
最近正在努力补充信息安全相关的内容,XSS攻击看到眼睛都要起茧子了,今天就来重点说说它。
XSS(Cross-Site Scripting,跨站脚本攻击。为了与CSS区分所以叫做XSS) 是Web安全中最常见也最危险的漏洞之一。
作为开发者,理解XSS的原理和防御方法至关重要!!!
注:本文的相关代码主要使用Express进行演示
🤔什么是XSS攻击?
XSS攻击是指攻击者利用网站漏洞,将恶意脚本注入到网页中,当其他用户浏览该网页时,恶意脚本会在用户浏览器中执行,从而窃取用户数据、会话令牌或进行其他恶意操作。
🤖XSS攻击的三种类型
【🐼这边的文字内容看不懂都没事,后面整理例子大家就懂了!!!】
- 反射型XSS:恶意脚本作为请求的一部分发送到服务器,然后服务器将脚本"反射"回响应中
- 存储型XSS:恶意脚本被永久存储在目标服务器上(如数据库)
- DOM型XSS:漏洞存在于客户端代码中,不涉及服务器
🌰Express中的XSS攻击场景与示例【重点!!!】
场景1:反射型XSS(最常见)
// 不安全的Express代码
const express = require('express');
const app = express();
app.get('/search', (req, res) => {
const query = req.query.q;
res.send(`<h1>搜索结果: ${query}</h1>`); // 直接输出用户输入
});
app.listen(3000);
攻击方式: 攻击者可以构造一个恶意链接发送给用户:
http://yoursite.com/search?q=<script>alert('XSS')</script>
当用户点击这个链接时,脚本会在用户浏览器中执行。
🤔这会导致什么?
攻击者可以替换为更恶意的脚本,例如窃取用户的Cookie、重定向到钓鱼网站、或窃取敏感信息。由于是反射型XSS,攻击通常需要诱骗用户点击特定链接才能触发。
所以未知的链接不能乱点啊,同志们。
场景2:存储型XSS(这位更是重量级,更加危险!!)
// 不安全的留言板功能
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
let comments = [];
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/comment', (req, res) => {
comments.push(req.body.comment); // 存储用户输入
res.redirect('/');
});
app.get('/', (req, res) => {
res.send(`
<h1>留言板</h1>
${comments.map(c => `<p>${c}</p>`).join('')}
<form action="/comment" method="POST">
<input type="text" name="comment">
<button type="submit">提交</button>
</form>
`);
});
app.listen(3000);
攻击方式: 攻击者提交一个包含恶意脚本的留言:
<script>console.log("我来偷你的cookie了!!")</script>
之后所有访问留言板的用户都会执行这个脚本,这就挺恐怖的了。
场景3:DOM型XSS
// 不安全的客户端代码(假设Exprses返回的HTML中包含)
app.get('/dashboard', (req, res) => {
res.send(`
<script>
const token = new URLSearchParams(window.location.search).get('token');
document.write('你的令牌是: ' + token); // 直接写入DOM
</script>
`);
});
攻击方式: 攻击者构造链接:
http://localhost:3000/dashboard?token=<img src=x onerror=alert('XSS')>
当用户访问时,恶意代码会被执行。
XSS防御措施
前面介绍了坏人是如何攻击的,现在来说说我们作为程序员要如何进行防御
1. 输入验证与过滤
// 使用xss库过滤用户输入
const xss = require('xss');
app.get('/search', (req, res) => {
const query = xss(req.query.q); // 过滤XSS代码
res.send(`<h1>搜索结果: ${query}</h1>`);
});
2. 输出编码
// 使用模板引擎自动转义(如EJS)
const ejs = require('ejs');
app.set('view engine', 'ejs');
app.get('/search', (req, res) => {
res.render('search', { query: req.query.q }); // EJS会自动转义
});
3. 设置HTTP安全头
// 使用helmet中间件
const helmet = require('helmet');
app.use(helmet());
Helmet设置了多个安全头,包括:
Content-Security-Policy:限制资源加载来源X-XSS-Protection:启用浏览器XSS过滤器X-Content-Type-Options:防止MIME类型嗅探
4. 内容安全策略(CSP)
// 自定义CSP策略
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "trusted.cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"]
}
}));
5. Cookie安全设置
// 安全设置cookie
app.use(session({
secret: 'your-secret',
cookie: {
httpOnly: true, // 防止JavaScript访问
secure: true, // 仅HTTPS
sameSite: 'strict' // 防止CSRF
}
}));
【相对完善的🌰】完整的安全Express示例
const express = require('express');
const helmet = require('helmet');
const xss = require('xss');
const ejs = require('ejs');
const session = require('express-session');
const app = express();
// 安全中间件
app.use(helmet());
app.use(express.urlencoded({ extended: false }));
// 模板引擎设置
app.set('view engine', 'ejs');
// 会话设置
app.use(session({
secret: 'complex-secret-key',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict'
}
}));
// 路由示例 - 安全搜索
app.get('/search', (req, res) => {
res.render('search', { query: xss(req.query.q) });
});
// 路由示例 - 安全留言板
let comments = [];
app.post('/comment', (req, res) => {
comments.push(xss(req.body.comment));
res.redirect('/');
});
app.get('/', (req, res) => {
res.render('comments', { comments });
});
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('发生错误!');
});
app.listen(3000, () => {
console.log('安全服务器运行在 http://localhost:3000');
});
总结
XSS攻击之所以危险,是因为它利用了用户对网站的信任。
作为开发者,我们应该:
- 永远不要信任用户输入:所有输入都应视为潜在恶意
- 使用专业的安全库:如
helmet、xss等 - 实施深度防御:结合输入验证、输出编码、CSP等多层防护
- 保持依赖更新:定期更新安全相关依赖
- 进行安全测试:使用工具如OWASP ZAP进行漏洞扫描
以上就是文章的全部内容了
下次见!