API 安全设计实战:认证、授权与防护

深入理解 API 安全设计原则,掌握 JWT、OAuth 2.0、HTTPS、速率限制等关键技术,构建安全的 API 服务。

作者
安全架构师资深安全专家

🔐 API 安全威胁分析

API 是现代应用的核心,但也是主要攻击面。根据 OWASP API Security Top 10,主要威胁包括:

🎯 核心威胁

  • broken Object Level Authorization:越权访问
  • broken Authentication:认证绕过
  • Excessive Data Exposure:数据过度暴露
  • Lack of Resources & Rate Limiting:资源耗尽攻击
  • Broken Function Level Authorization:功能级越权

💡 关键数据

根据 Salt Security 2023 报告,94% 的组织在过去一年中经历了 API 安全事件,平均每次事件造成 $3.8M 的损失。

🔑 认证机制设计

认证是 API 安全的第一道防线。我们对比几种主流认证方案:

认证方案优势劣势适用场景
Session + Cookie简单、传统不适合分布式、移动端传统 Web 应用
JWT无状态、可扩展令牌无法撤销SP A、移动端
OAuth 2.0 delegated 授权复杂、实现成本高第三方登录、授权
API Key简单、快速安全性低内部 API、低风险场景

JWT 最佳实践

✅ 正确的 JWT 使用方式

// 使用 HttpOnly Cookie 存储 JWT
res.cookie('token', jwt, {
  httpOnly: true,
  secure: true,  // HTTPS
  sameSite: 'strict',
  maxAge: 360000  // 1小时
})

// 使用短有效期 + 刷新令牌
const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' })
const refreshToken = jwt.sign(payload, secret, { expiresIn: '7d' })

⚠️ 常见错误

永远不要将 JWT 存储在 localStorage 或 sessionStorage 中,这会使你容易受到 XSS 攻击。

🛡️ 授权与权限控制

认证之后是授权。我们需要确保用户只能访问他们有权访问的资源。

RBAC(基于角色的访问控制)

RBAC 实现示例

// 定义角色和权限
const roles = {
  admin: ['read:any', 'write:any', 'delete:any'],
  user: ['read:own', 'write:own'],
  guest: ['read:public']
}

// 授权中间件
function authorize(permission) {
  return (req, res, next) => {
    const userRole = req.user.role
    if (roles[userRole].includes(permission)) {
      next()
    } else {
      res.status(403).json({ error: 'Forbidden' })
    }
  }
}

// 使用
app.get('/api/users/:id', authorize('read:own'), getUser)

对象级权限控制

🎯 OWASP API1:2023 - Broken Object Level Authorization

这是最常见的 API 安全问题。攻击者通过修改请求中的 ID 来访问其他用户的数据。

❌ 错误示例

// 攻击者可以修改 :id 来访问其他用户的数据
app.get('/api/orders/:id', async (req, res) => {
  const order = await Order.findById(req.params.id)
  res.json(order)
})

✅ 正确示例

// 检查订单是否属于当前用户
app.get('/api/orders/:id', async (req, res) => {
  const order = await Order.findById(req.params.id)
  
  if (order.userId.toString() !== req.user.id) {
    return res.status(403).json({ error: 'Forbidden' })
  }
  
  res.json(order)
})

🔒 HTTPS 与数据传输安全

所有 API 通信必须使用 HTTPS。但需要正确的配置才能确保安全。

TLS 配置最佳实践

1

禁用不安全的协议

禁用 TLS 1.0 和 1.1,只支持 TLS 1.2 和 1.3。

2

使用强加密套件

优先使用 ECDHE + AES-GCM 或 ChaCha20-Poly1305。

3

启用 HSTS

强制客户端使用 HTTPS 连接。

Nginx TLS 配置示例

server {
  listen 443 ssl http2;
  server_name api.example.com;

  ssl_certificate /path/to/cert.pem;
  ssl_certificate_key /path/to/key.pem;
  
  # 只支持 TLS 1.2 和 1.3
  ssl_protocols TLSv1.2 TLSv1.3;
  
  # 强加密套件
  ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers on;
  
  # HSTS
  add_header Strict-Transport-Security "max-age=63072000" always;
  
  # ... 其他配置
}

⏱️ 速率限制与防刷

速率限制是防止 API 被滥用的关键措施。它可以防止暴力破解、DDoS、资源耗尽攻击。

速率限制策略

🎯 基于 IP

限制每个 IP 地址的请求频率。简单但容易被绕过(使用代理池)。

👤 基于用户

限制每个用户的请求频率。需要认证,更准确。

🔑 基于 API Key

限制每个 API Key 的请求频率。适合开放 API。

使用 Redis 实现速率限制

const rateLimit = require('express-rate-limit')
const RedisStore = require('rate-limit-redis')

const limiter = rateLimit({
  store: new RedisStore({
    client: redisClient,
    expiry: 60  // 1分钟
  }),
  windowMs: 60 * 1000,  // 1分钟
  max: 100,  // 最多100次请求
  message: 'Too many requests, please try again later.'
})

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

💡 进阶技巧

对于敏感接口(如登录、注册),应该使用更严格的速率限制,并结合验证码(reCAPTCHA)来防止自动化攻击。

💉 注入攻击防护

注入攻击是最常见的安全漏洞之一。我们需要防止 SQL 注入、NoSQL 注入、命令注入等。

SQL 注入防护

❌ 错误示例(拼接 SQL)

// 攻击者可以输入:' OR '1'='1
const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`
db.query(query)

✅ 正确示例(参数化查询)

// 使用参数化查询
const query = 'SELECT * FROM users WHERE username = ? AND password = ?'
db.query(query, [username, password])

// 或使用 ORM(如 Sequelize)
const user = await User.findOne({
  where: { username, password }
})

NoSQL 注入防护

❌ 错误示例(MongoDB)

// 攻击者可以输入:{"$gt": ""}
const user = await User.findOne({ username })

✅ 正确示例

// 验证输入是字符串
if (typeof username !== 'string') {
  return res.status(400).json({ error: 'Invalid input' })
}

const user = await User.findOne({ username: String(username) })

✅ API 安全最佳实践

除了上述具体措施,还有一些通用的 API 安全最佳实践:

1

输入验证

永远不要信任用户输入。使用 JSON Schema、Joi、express-validator 等工具验证所有输入。

2

输出编码

对所有输出进行编码,防止 XSS 攻击。使用 helmet 等中间件自动设置安全头。

3

错误处理

不要向客户端返回详细的错误信息(如堆栈跟踪)。使用通用错误响应。

4

日志记录

记录所有关键操作(登录、敏感数据访问等),但不要在日志中记录敏感信息(如密码、令牌)。

5

安全头

使用 Helmet 中间件设置安全头:Content-Security-Policy、X-Frame-Options、X-Content-Type-Options 等。

使用 Helmet 设置安全头

const helmet = require('helmet')
app.use(helmet())

// 或自定义配置
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"]
    }
  }
}))

📝 总结

API 安全是一个系统工程,需要从多个层面进行防护:

核心要点

  • 认证:使用 JWT + HttpOnly Cookie,短有效期 + 刷新令牌
  • 授权:实现 RBAC,检查对象级权限
  • 传输安全:强制 HTTPS,正确配置 TLS
  • 速率限制:防止滥用和 DDoS
  • 注入防护:使用参数化查询,验证所有输入
  • 安全头:使用 Helmet 设置安全头
  • 日志记录:记录关键操作,但不记录敏感信息

安全不是一次性的工作,而是持续的过程。定期审计、渗透测试、保持依赖更新,才能确保 API 的长期安全。