安全, HTTPS·

Web 安全最佳实践

保护你的网站和用户免受常见安全威胁

Web 安全是每个开发者都必须关注的重要话题。本文介绍常见的 Web 安全威胁和防护措施。

常见安全威胁

XSS(跨站脚本攻击)

XSS 攻击通过在网页中注入恶意脚本来窃取用户数据。

<!-- 危险:直接插入用户输入 -->
<div id="user-content"></div>
<script>
  document.getElementById('user-content').innerHTML = userInput;
</script>

<!-- 安全:对用户输入进行转义 -->
<div id="user-content"></div>
<script>
  document.getElementById('user-content').textContent = userInput;
</script>

防护措施:

  1. 输入验证和转义
  2. 使用 Content Security Policy(CSP)
  3. HttpOnly Cookie
// 使用 DOMPurify 清理 HTML
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyInput);

CSRF(跨站请求伪造)

CSRF 攻击诱使用户在不知情的情况下执行非预期的操作。

防护措施:

  1. 使用 CSRF Token
  2. SameSite Cookie 属性
  3. 验证 Referer 头
// 前端:CSRF Token
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

fetch('/api/update', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': csrfToken,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
});

// 后端:验证 Token(示例)
app.use((req, res, next) => {
  const token = req.headers['x-csrf-token'];
  if (token !== req.session.csrfToken) {
    return res.status(403).send('Invalid CSRF token');
  }
  next();
});

SQL 注入

SQL 注入通过恶意 SQL 语句攻击数据库。

-- 危险
SELECT * FROM users WHERE name = '" + userInput + "';

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

HTTPS 和 SSL/TLS

为什么需要 HTTPS

  1. 加密数据传输
  2. 验证服务器身份
  3. 保护数据完整性
  4. SEO 排名提升

配置 HTTPS

# Nginx HTTPS 配置
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # 启用 HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

# HTTP 到 HTTPS 重定向
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

免费 SSL 证书

使用 Let's Encrypt 获取免费 SSL 证书:

# 安装 Certbot
sudo apt-get install certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d example.com -d www.example.com

# 自动续期
sudo certbot renew --dry-run

Content Security Policy(CSP)

CSP 限制资源加载来源,防止 XSS 攻击:

<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' 'unsafe-inline' https://trusted.cdn.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
">
// 通过 HTTP 头设置
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'

安全 Headers

重要安全 Headers

// 安全 Headers 配置
app.use((req, res, next) => {
  // X-Frame-Options: 防止点击劫持
  res.setHeader('X-Frame-Options', 'DENY');

  // X-Content-Type-Options: 防止 MIME 类型嗅探
  res.setHeader('X-Content-Type-Options', 'nosniff');

  // X-XSS-Protection: XSS 过滤
  res.setHeader('X-XSS-Protection', '1; mode=block');

  // Strict-Transport-Security: 强制 HTTPS
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

  // Content-Security-Policy: 资源加载策略
  res.setHeader('Content-Security-Policy', "default-src 'self'");

  next();
});

密码安全

密码哈希

import bcrypt from 'bcryptjs';

// 哈希密码
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(plainPassword, salt);

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

密码策略

  • 最少 8 个字符
  • 包含大小写字母、数字和特殊字符
  • 定期更换密码
  • 使用密码管理器

会话安全

// 安全的 Cookie 设置
app.use(session({
  secret: 'your-secret-key',
  cookie: {
    httpOnly: true,          // 防止 XSS 访问
    secure: true,            // 仅通过 HTTPS 传输
    sameSite: 'strict',      // CSRF 防护
    maxAge: 24 * 60 * 60 * 1000  // 1 天过期
  }
}));

数据验证

输入验证

import { z } from 'zod';

// 定义验证模式
const userSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8).regex(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/),
  age: z.number().min(18).max(120)
});

// 验证输入
try {
  const validatedUser = userSchema.parse(userInput);
} catch (error) {
  console.error('验证失败:', error.errors);
}

输出编码

// HTML 编码
function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

安全检查清单

开发阶段

  • 输入验证和过滤
  • 输出编码
  • 参数化查询
  • CSRF 保护
  • XSS 防护
  • 配置 CSP

部署阶段

  • 启用 HTTPS
  • 配置安全 Headers
  • 更新依赖包
  • 禁用不必要的功能
  • 定期备份

运维阶段

  • 监控安全事件
  • 定期安全审计
  • 及时更新补丁
  • 访问控制
  • 日志记录

学习资源

保护你的网站,保护你的用户!