👾XSS攻击与防御:基于Express的深入解析

233 阅读3分钟

🍃前言

👋

你好呀,我是你的人类朋友!

最近正在努力补充信息安全相关的内容,XSS攻击看到眼睛都要起茧子了,今天就来重点说说它。

XSS(Cross-Site Scripting,跨站脚本攻击。为了与CSS区分所以叫做XSS) 是Web安全中最常见也最危险的漏洞之一。

作为开发者,理解XSS的原理和防御方法至关重要!!!

注:本文的相关代码主要使用Express进行演示

🤔什么是XSS攻击?

XSS攻击是指攻击者利用网站漏洞,将恶意脚本注入到网页中,当其他用户浏览该网页时,恶意脚本会在用户浏览器中执行,从而窃取用户数据、会话令牌或进行其他恶意操作。

🤖XSS攻击的三种类型

【🐼这边的文字内容看不懂都没事,后面整理例子大家就懂了!!!】

  1. 反射型XSS:恶意脚本作为请求的一部分发送到服务器,然后服务器将脚本"反射"回响应中
  2. 存储型XSS:恶意脚本被永久存储在目标服务器上(如数据库)
  3. 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攻击之所以危险,是因为它利用了用户对网站的信任

作为开发者,我们应该:

  1. 永远不要信任用户输入:所有输入都应视为潜在恶意
  2. 使用专业的安全库:如helmetxss
  3. 实施深度防御:结合输入验证、输出编码、CSP等多层防护
  4. 保持依赖更新:定期更新安全相关依赖
  5. 进行安全测试:使用工具如OWASP ZAP进行漏洞扫描

以上就是文章的全部内容了

下次见!