API 安全 #

概述 #

API 安全是 API 设计中最重要的方面之一。一个不安全的 API 可能导致数据泄露、服务滥用、经济损失等严重后果。

text
┌─────────────────────────────────────────────────────────────┐
│                    API 安全重要性                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  安全威胁:                                                  │
│  - 数据泄露:敏感信息被窃取                                 │
│  - 未授权访问:非法获取资源                                 │
│  - 服务滥用:恶意调用消耗资源                               │
│  - 注入攻击:SQL 注入、XSS 等                               │
│  - 中间人攻击:通信被窃听                                   │
│                                                             │
│  安全目标:                                                  │
│  - 机密性:数据不被未授权者访问                             │
│  - 完整性:数据不被篡改                                     │
│  - 可用性:服务正常运行                                     │
│  - 可追溯:操作可审计                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

认证(Authentication) #

认证方式对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    认证方式对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  方式              适用场景              优缺点             │
│  ─────────────────────────────────────────────────────────  │
│  API Key          服务间调用            简单但安全性较低    │
│  Basic Auth       简单场景              不推荐,安全性低    │
│  JWT              用户认证              推荐,无状态        │
│  OAuth 2.0        第三方授权            推荐,功能完善      │
│  Session Cookie   传统 Web 应用         有状态,不适合 API  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

API Key 认证 #

text
┌─────────────────────────────────────────────────────────────┐
│                    API Key 认证                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  使用方式:                                                  │
│                                                             │
│  方式一:请求头                                              │
│  GET /users HTTP/1.1                                        │
│  X-API-Key: your-api-key-here                               │
│                                                             │
│  方式二:查询参数                                            │
│  GET /users?api_key=your-api-key-here                       │
│                                                             │
│  方式三:Authorization Header                                │
│  Authorization: ApiKey your-api-key-here                    │
│                                                             │
│  最佳实践:                                                  │
│  ✅ 使用请求头传递                                          │
│  ✅ 每个 API Key 关联特定权限                               │
│  ✅ 设置 API Key 过期时间                                   │
│  ✅ 支持 API Key 撤销                                       │
│  ✅ 记录 API Key 使用日志                                   │
│                                                             │
│  注意事项:                                                  │
│  ❌ 不要将 API Key 嵌入前端代码                             │
│  ❌ 不要在日志中记录 API Key                                │
│  ❌ 不要使用 URL 参数传递(容易被记录)                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

JWT 认证 #

text
┌─────────────────────────────────────────────────────────────┐
│                    JWT 认证                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  认证流程:                                                  │
│                                                             │
│  1. 用户登录                                                │
│     POST /auth/login                                        │
│     { "username": "user", "password": "pass" }              │
│                                                             │
│  2. 服务端验证并返回 JWT                                    │
│     { "token": "eyJhbGciOiJIUzI1NiIs..." }                  │
│                                                             │
│  3. 后续请求携带 JWT                                        │
│     GET /users/123                                          │
│     Authorization: Bearer eyJhbGciOiJIUzI1NiIs...           │
│                                                             │
│  4. 服务端验证 JWT                                          │
│     - 验证签名                                              │
│     - 验证过期时间                                          │
│     - 解析用户信息                                          │
│                                                             │
│  最佳实践:                                                  │
│  ✅ 使用 HTTPS 传输                                         │
│  ✅ 设置合理的过期时间                                      │
│  ✅ 实现刷新 Token 机制                                     │
│  ✅ 不在 JWT 中存储敏感信息                                 │
│  ✅ 使用强密钥签名                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

OAuth 2.0 认证 #

text
┌─────────────────────────────────────────────────────────────┐
│                    OAuth 2.0 认证                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  授权码模式流程:                                            │
│                                                             │
│  1. 引导用户到授权页面                                       │
│     GET /authorize?                                         │
│         response_type=code&                                 │
│         client_id=CLIENT_ID&                                │
│         redirect_uri=REDIRECT_URI&                          │
│         scope=read write                                    │
│                                                             │
│  2. 用户授权后重定向                                         │
│     REDIRECT_URI?code=AUTHORIZATION_CODE                    │
│                                                             │
│  3. 用授权码换取 Token                                       │
│     POST /token                                             │
│     {                                                       │
│       "grant_type": "authorization_code",                   │
│       "code": "AUTHORIZATION_CODE",                         │
│       "client_id": "CLIENT_ID",                             │
│       "client_secret": "CLIENT_SECRET"                      │
│     }                                                       │
│                                                             │
│  4. 获取 Access Token                                       │
│     {                                                       │
│       "access_token": "...",                                │
│       "token_type": "Bearer",                               │
│       "expires_in": 3600,                                   │
│       "refresh_token": "..."                                │
│     }                                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

授权(Authorization) #

RBAC 权限模型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    RBAC 权限模型                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  用户(User)→ 角色(Role)→ 权限(Permission)              │
│                                                             │
│  示例:                                                      │
│                                                             │
│  用户          角色              权限                        │
│  ─────────────────────────────────────────────────────────  │
│  张三    →    管理员      →    用户管理、订单管理           │
│  李四    →    运营        →    订单管理、商品查看           │
│  王五    →    普通用户    →    个人信息查看                 │
│                                                             │
│  权限定义:                                                  │
│  - users:read      读取用户信息                             │
│  - users:write     创建/更新用户                            │
│  - users:delete    删除用户                                 │
│  - orders:read     读取订单                                 │
│  - orders:write    创建/更新订单                            │
│                                                             │
│  实现:                                                      │
│  // 中间件检查权限                                          │
│  function checkPermission(permission) {                     │
│    return (req, res, next) => {                             │
│      const userPermissions = req.user.permissions;          │
│      if (userPermissions.includes(permission)) {            │
│        next();                                              │
│      } else {                                               │
│        res.status(403).json({ error: 'Forbidden' });        │
│      }                                                      │
│    };                                                       │
│  }                                                          │
│                                                             │
│  // 使用                                                    │
│  app.delete('/users/:id',                                   │
│    authenticate,                                             │
│    checkPermission('users:delete'),                         │
│    deleteUser                                               │
│  );                                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

资源级权限控制 #

text
┌─────────────────────────────────────────────────────────────┐
│                    资源级权限控制                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  场景:用户只能访问自己的订单                                │
│                                                             │
│  实现:                                                      │
│  GET /users/:userId/orders                                  │
│                                                             │
│  // 中间件检查资源所有权                                    │
│  function checkResourceOwnership(req, res, next) {          │
│    const requestedUserId = req.params.userId;               │
│    const currentUserId = req.user.id;                       │
│                                                             │
│    // 管理员可以访问所有资源                                │
│    if (req.user.role === 'admin') {                         │
│      return next();                                         │
│    }                                                        │
│                                                             │
│    // 普通用户只能访问自己的资源                            │
│    if (requestedUserId !== currentUserId) {                 │
│      return res.status(403).json({                          │
│        error: 'You can only access your own resources'      │
│      });                                                    │
│    }                                                        │
│                                                             │
│    next();                                                  │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

HTTPS 与传输安全 #

HTTPS 配置 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HTTPS 配置                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  为什么必须使用 HTTPS:                                      │
│  - 防止中间人攻击                                           │
│  - 保护敏感数据传输                                         │
│  - 防止 Token 被窃取                                        │
│  - 满足合规要求                                             │
│                                                             │
│  最佳实践:                                                  │
│  ✅ 强制使用 HTTPS                                          │
│  ✅ 使用 TLS 1.2 或更高版本                                 │
│  ✅ 配置 HSTS 头                                            │
│  ✅ 使用强加密套件                                          │
│  ✅ 定期更新证书                                            │
│                                                             │
│  HSTS 配置:                                                 │
│  Strict-Transport-Security: max-age=31536000;               │
│    includeSubDomains; preload                               │
│                                                             │
│  HTTP 重定向到 HTTPS:                                       │
│  // Express.js                                               │
│  app.use((req, res, next) => {                              │
│    if (!req.secure) {                                       │
│      return res.redirect(301, `https://${req.headers.host}${req.url}`); │
│    }                                                        │
│    next();                                                  │
│  });                                                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

速率限制 #

速率限制策略 #

text
┌─────────────────────────────────────────────────────────────┐
│                    速率限制策略                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  限制维度:                                                  │
│  - IP 地址                                                  │
│  - 用户 ID                                                  │
│  - API Key                                                  │
│  - 端点                                                     │
│                                                             │
│  限制算法:                                                  │
│                                                             │
│  1. 固定窗口                                                 │
│     - 每分钟最多 100 次请求                                 │
│     - 简单但可能有突发                                      │
│                                                             │
│  2. 滑动窗口                                                 │
│     - 任意 1 分钟内最多 100 次请求                          │
│     - 更精确但计算复杂                                      │
│                                                             │
│  3. 令牌桶                                                   │
│     - 桶容量 100,每秒补充 10 个                            │
│     - 允许一定突发                                          │
│                                                             │
│  响应头:                                                    │
│  X-RateLimit-Limit: 100                                     │
│  X-RateLimit-Remaining: 95                                  │
│  X-RateLimit-Reset: 1640995200                              │
│                                                             │
│  超限响应:                                                  │
│  HTTP/1.1 429 Too Many Requests                             │
│  Retry-After: 60                                            │
│                                                             │
│  {                                                          │
│    "error": {                                               │
│      "code": "RATE_LIMIT_EXCEEDED",                         │
│      "message": "Rate limit exceeded",                      │
│      "retryAfter": 60                                       │
│    }                                                        │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

实现示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    速率限制实现                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  // Express.js + express-rate-limit                         │
│  const rateLimit = require('express-rate-limit');           │
│                                                             │
│  // 全局限制                                                 │
│  const globalLimiter = rateLimit({                          │
│    windowMs: 15 * 60 * 1000,  // 15 分钟                    │
│    max: 100,                  // 最多 100 次请求            │
│    message: {                                               │
│      error: {                                               │
│        code: 'RATE_LIMIT_EXCEEDED',                         │
│        message: 'Too many requests'                         │
│      }                                                      │
│    }                                                        │
│  });                                                        │
│                                                             │
│  app.use(globalLimiter);                                    │
│                                                             │
│  // 端点级别限制                                             │
│  const loginLimiter = rateLimit({                           │
│    windowMs: 60 * 60 * 1000,  // 1 小时                     │
│    max: 5,                    // 最多 5 次登录尝试          │
│    message: {                                               │
│      error: {                                               │
│        code: 'TOO_MANY_LOGIN_ATTEMPTS',                     │
│        message: 'Too many login attempts'                   │
│      }                                                      │
│    }                                                        │
│  });                                                        │
│                                                             │
│  app.post('/auth/login', loginLimiter, loginHandler);       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

输入验证 #

验证原则 #

text
┌─────────────────────────────────────────────────────────────┐
│                    输入验证原则                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 永远不要信任客户端输入                                   │
│  - 所有输入都必须验证                                       │
│  - 验证在服务端进行                                         │
│                                                             │
│  2. 白名单优于黑名单                                         │
│  - 定义允许的输入格式                                       │
│  - 而非过滤不允许的内容                                     │
│                                                             │
│  3. 尽早验证,尽早失败                                       │
│  - 在处理请求前验证                                         │
│  - 返回明确的错误信息                                       │
│                                                             │
│  4. 参数化查询                                               │
│  - 防止 SQL 注入                                            │
│  - 使用 ORM 或参数化语句                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

验证示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    输入验证示例                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  // 使用 Joi 验证                                           │
│  const Joi = require('joi');                                │
│                                                             │
│  const userSchema = Joi.object({                            │
│    name: Joi.string().min(2).max(50).required(),            │
│    email: Joi.string().email().required(),                  │
│    password: Joi.string()                                   │
│      .min(8)                                                │
│      .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)            │
│      .required(),                                           │
│    age: Joi.number().integer().min(0).max(150),             │
│    role: Joi.string().valid('user', 'admin').default('user')│
│  });                                                        │
│                                                             │
│  // 中间件验证                                               │
│  function validateUser(req, res, next) {                    │
│    const { error, value } = userSchema.validate(req.body);  │
│    if (error) {                                             │
│      return res.status(400).json({                          │
│        error: {                                             │
│          code: 'VALIDATION_ERROR',                          │
│          message: 'Invalid input',                          │
│          details: error.details                             │
│        }                                                    │
│      });                                                    │
│    }                                                        │
│    req.body = value;  // 使用验证后的值                     │
│    next();                                                  │
│  }                                                          │
│                                                             │
│  // 防止 SQL 注入                                            │
│  // ❌ 错误:字符串拼接                                      │
│  const query = `SELECT * FROM users WHERE id = ${userId}`;  │
│                                                             │
│  // ✅ 正确:参数化查询                                      │
│  const query = 'SELECT * FROM users WHERE id = ?';          │
│  db.query(query, [userId]);                                 │
│                                                             │
│  // ✅ 正确:使用 ORM                                        │
│  User.findByPk(userId);                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

CORS 配置 #

CORS 配置 #

text
┌─────────────────────────────────────────────────────────────┐
│                    CORS 配置                                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  什么是 CORS:                                               │
│  Cross-Origin Resource Sharing                              │
│  跨域资源共享机制                                            │
│                                                             │
│  预检请求:                                                  │
│  OPTIONS /users HTTP/1.1                                    │
│  Origin: https://client.example.com                         │
│  Access-Control-Request-Method: POST                        │
│  Access-Control-Request-Headers: Content-Type               │
│                                                             │
│  预检响应:                                                  │
│  HTTP/1.1 200 OK                                            │
│  Access-Control-Allow-Origin: https://client.example.com    │
│  Access-Control-Allow-Methods: GET, POST, PUT, DELETE       │
│  Access-Control-Allow-Headers: Content-Type, Authorization  │
│  Access-Control-Max-Age: 86400                              │
│                                                             │
│  最佳实践:                                                  │
│  ✅ 明确指定允许的 Origin                                   │
│  ✅ 不要使用 Access-Control-Allow-Origin: *                │
│  ✅ 限制允许的方法和头部                                    │
│  ✡ 设置合理的缓存时间                                      │
│  ✅ 凭证请求需要更严格的配置                                │
│                                                             │
│  // Express.js CORS 配置                                     │
│  const cors = require('cors');                              │
│                                                             │
│  const corsOptions = {                                      │
│    origin: ['https://client.example.com'],                  │
│    methods: ['GET', 'POST', 'PUT', 'DELETE'],               │
│    allowedHeaders: ['Content-Type', 'Authorization'],       │
│    credentials: true,                                       │
│    maxAge: 86400                                            │
│  };                                                         │
│                                                             │
│  app.use(cors(corsOptions));                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

安全响应头 #

text
┌─────────────────────────────────────────────────────────────┐
│                    安全响应头                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  // Express.js 使用 helmet                                   │
│  const helmet = require('helmet');                          │
│  app.use(helmet());                                         │
│                                                             │
│  安全响应头列表:                                            │
│                                                             │
│  Strict-Transport-Security: max-age=31536000                │
│  强制使用 HTTPS                                              │
│                                                             │
│  X-Content-Type-Options: nosniff                            │
│  防止 MIME 类型嗅探                                          │
│                                                             │
│  X-Frame-Options: DENY                                      │
│  防止点击劫持                                                │
│                                                             │
│  X-XSS-Protection: 1; mode=block                            │
│  XSS 保护(已废弃,推荐 CSP)                                │
│                                                             │
│  Content-Security-Policy: default-src 'self'                │
│  内容安全策略                                                │
│                                                             │
│  Cache-Control: no-store                                    │
│  敏感数据不缓存                                              │
│                                                             │
│  X-Request-ID: uuid                                         │
│  请求追踪                                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

安全检查清单 #

text
□ 认证与授权
  □ 使用安全的认证方式(JWT/OAuth)
  □ 实现权限控制
  □ 验证资源所有权

□ 传输安全
  □ 强制使用 HTTPS
  □ 配置 HSTS
  □ 使用 TLS 1.2+

□ 速率限制
  □ 实现全局速率限制
  □ 敏感端点加强限制
  □ 返回限制信息

□ 输入验证
  □ 验证所有输入
  □ 使用参数化查询
  □ 防止注入攻击

□ CORS 配置
  □ 明确指定允许的 Origin
  □ 限制方法和头部
  □ 正确处理凭证请求

□ 安全响应头
  □ 配置安全响应头
  □ 敏感数据不缓存
  □ 实现请求追踪

□ 日志与监控
  □ 记录安全事件
  □ 监控异常请求
  □ 定期审计

下一步 #

现在你已经了解了 API 安全,接下来学习 分页与过滤,深入了解如何处理大量数据!

最后更新:2026-03-29