目录

Web安全防护最佳实践

Web 安全是每个开发者都必须重视的课题,本文涵盖常见攻击方式和防护方案。

XSS 跨站脚本攻击

攻击者注入恶意脚本到页面中:

javascript
// 危险:直接插入未转义的HTML
element.innerHTML = userInput;

// 安全:使用 textContent
element.textContent = userInput;

// 安全:使用框架自动转义(React/Vue默认转义)
<div>{userInput}</div>

// 安全:需要HTML时使用白名单过滤
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);

CSP 内容安全策略

nginx
# Nginx 配置 CSP 头
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com;";

CSRF 跨站请求伪造

javascript
// 使用 SameSite Cookie
app.use(session({
  secret: 'secret-key',
  cookie: {
    httpOnly: true,
    secure: true,        // 仅 HTTPS
    sameSite: 'strict', // 或 'lax'
  }
}));

// CSRF Token
const csrf = require('csurf');
app.use(csrf({ cookie: true }));

// 前端获取 token
fetch('/api/csrf-token')
  .then(res => res.json())
  .then(({ csrfToken }) => {
    // 后续请求带上 token
    fetch('/api/data', {
      headers: { 'X-CSRF-Token': csrfToken },
      method: 'POST'
    });
  });

SQL 注入

javascript
// 危险:字符串拼接
const query = `SELECT * FROM users WHERE name = '${name}'`;

// 安全:参数化查询
const query = 'SELECT * FROM users WHERE name = ?';
db.query(query, [name]);

// 安全:ORM
const user = await User.findOne({ where: { name } });

安全 HTTP 头

javascript
// 使用 helmet 中间件(Express)
const helmet = require('helmet');
app.use(helmet());

// 或手动设置
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  next();
});

密码安全

javascript
import bcrypt from 'bcrypt';

// 哈希密码(不要用 MD5/SHA1)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);

// 验证密码
const isValid = await bcrypt.compare(inputPassword, hashedPassword);

// 密码强度验证
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*?&])[A-Za-zd@$!%*?&]{8,}$/;
if (!passwordRegex.test(password)) {
  throw new Error('密码至少8位,包含大小写字母、数字和特殊字符');
}

JWT 安全

javascript
// 生成 JWT
const token = jwt.sign(
  { userId: user.id, role: user.role },
  process.env.JWT_SECRET,
  { 
    expiresIn: '1h',
    issuer: 'starr',
    audience: 'starr-users'
  }
);

// 安全存储:使用 httpOnly cookie 而非 localStorage
res.cookie('token', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 3600000
});

速率限制

javascript
import rateLimit from 'express-rate-limit';

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

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

// 登录接口更严格限制
const loginLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,  // 1小时
  max: 5,                     // 最多5次尝试
});

app.use('/api/login', loginLimiter);

安全检查清单

  • [ ] 所有输入进行验证和转义
  • [ ] 使用 HTTPS 并启用 HSTS
  • [ ] 设置安全 HTTP 头
  • [ ] Cookie 设置 httpOnly、secure、sameSite
  • [ ] 密码使用 bcrypt 哈希存储
  • [ ] API 启用速率限制
  • [ ] 定期更新依赖库
  • [ ] 敏感数据不在日志中记录

总结

Web 安全是系统工程,需要从输入验证、输出编码、传输加密、认证授权多个层面防护。最关键的是:永远不信任用户输入,所有输入都需验证和转义。使用 helmet、bcrypt、CSP 等工具可以快速提升安全基线。