API 安全设计

从 RESTful API 到 GraphQL 的安全设计:认证鉴权、速率限制、输入验证、API 网关防护、OWASP API Security Top 10,以及 API 密钥管理、OpenAPI 安全规范。

API 是现代应用的核心接口,也是攻击者的主要目标。OWASP API Security Top 10 专门针对 API 的独特风险。本文从设计到运维,系统讲解 API 安全的关键要素。


1. OWASP API Security Top 10 2023

排名风险说明示例
API1:2023对象级授权失效未验证用户对特定对象的访问权限修改 URL 查看他人订单
API2:2023认证失效弱令牌、JWT 误用、无 MFAjwt.io 修改令牌权限
API3:2023对象属性级授权失效返回过多敏感字段接口返回用户密码哈希
API4:2023不受限制的资源消耗无速率限制导致 DoS大量请求耗尽资源
API5:2023功能级授权失效普通用户调用管理员接口POST /admin/delete
API6:2023unrestricted access to business flow自动化攻击业务流程批量注册、黄牛抢票
API7:2023服务器端请求伪造API 参数控制服务端请求SSRF via URL 参数
API8:2023安全配置错误默认配置、CORS 过于宽松调试模式开启
API9:2023库存管理不当使用不安全的依赖日志库存在 RCE
API10:2023unsafe consumption of APIs调用外部 API 未验证外部 API 返回恶意数据

2. 认证与授权

2.1 API 认证方案

方案适用安全等级注意
API Key服务间调用★★需 HTTPS,定期轮换
JWT用户态 API★★★短过期,不存敏感信息
mTLS高安全微服务★★★★★双向证书认证
OAuth2第三方 API★★★★使用 PKCE
Signed RequestAWS/GCP 模式★★★★请求签名防篡改

2.2 API Key 安全

# API Key 生成
import secrets
api_key = secrets.token_urlsafe(32)  # 256-bit
# sk_live_abc123...xyz

# 存储:哈希后保存(防泄露后直接使用)
from cryptography.fernet import Fernet
# 或使用 HMAC
key_hmac = hmac.new(SECRET_KEY, api_key.encode(), hashlib.sha256).hexdigest()

# 传输:仅 HTTPS Header
headers = {'X-API-Key': api_key}

# 定期轮换:支持多 key 同时有效过渡期

3. 速率限制(Rate Limiting)

3.1 算法对比

算法原理突发处理平滑度
固定窗口每个时间窗口计数差(窗口边界突发)
滑动窗口按时间滑动统计
令牌桶匀速发放令牌好(允许突发)
漏桶匀速处理请求差(队列等待)

3.2 Redis + 令牌桶实现

@Service
public class RateLimiterService {
    @Autowired
    private StringRedisTemplate redis;
    
    // Lua 脚本保证原子性
    private static final String RATE_LIMIT_SCRIPT = """
        local key = KEYS[1]
        local capacity = tonumber(ARGV[1])
        local rate = tonumber(ARGV[2])  -- 每秒生成令牌数
        local now = tonumber(ARGV[3])
        local requested = tonumber(ARGV[4])
        
        local last_time = redis.call('hget', key, 'last_time') or now
        local tokens = redis.call('hget', key, 'tokens') or capacity
        
        local elapsed = math.max(0, now - last_time)
        tokens = math.min(capacity, tokens + elapsed * rate)
        
        local allowed = tokens >= requested
        if allowed then
            tokens = tokens - requested
        end
        
        redis.call('hset', key, 'last_time', now)
        redis.call('hset', key, 'tokens', tokens)
        redis.call('expire', key, 60)
        
        return allowed and 1 or 0
        """;
    
    public boolean tryAcquire(String key, int capacity, double rate, int requested) {
        long now = System.currentTimeMillis() / 1000;
        Long result = redis.execute(
            new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Long.class),
            List.of("ratelimit:" + key),
            String.valueOf(capacity),
            String.valueOf(rate),
            String.valueOf(now),
            String.valueOf(requested)
        );
        return result != null && result == 1;
    }
}

3.3 分层限流

# Nginx 限流(第一层)
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
limit_req zone=api burst=200 nodelay;

# 应用层限流(第二层)
# 用户级别:1000 次/小时
# 接口级别:登录 5 次/分钟
# 全局级别:10000 QPS

4. 输入验证

4.1 严格校验原则

@Validated
@RestController
@RequestMapping("/api/v1")
public class OrderController {
    
    @PostMapping("/orders")
    public Order createOrder(
        @RequestBody @Valid OrderRequest request
    ) {
        // 业务逻辑
    }
}

@Data
public class OrderRequest {
    @NotNull
    @Size(min = 1, max = 100)
    private String productId;
    
    @NotNull
    @Min(1)
    @Max(100)  // 限制批量购买数量
    private Integer quantity;
    
    @Pattern(regexp = "^[A-Z]{2}$")
    private String countryCode;
    
    @DecimalMax("999999.99")
    private BigDecimal amount;
}

4.2 自定义校验

@Constraint(validatedBy = SafeOrderValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SafeOrder {
    String message() default "订单数据异常";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class SafeOrderValidator implements ConstraintValidator<SafeOrder, OrderRequest> {
    @Override
    public boolean isValid(OrderRequest req, ConstraintValidatorContext ctx) {
        // 自定义业务校验
        if (req.getQuantity() * unitPrice != req.getAmount()) {
            return false; // 前端篡改金额
        }
        if (isBlacklistedProduct(req.getProductId())) {
            return false;
        }
        return true;
    }
}

5. API 网关安全

5.1 Kong Gateway

# kong.yml
services:
  - name: order-service
    url: http://order-svc:8080
    plugins:
      - name: rate-limiting
        config:
          minute: 100
          policy: redis
          redis_host: redis
      - name: jwt
        config:
          uri_param_names: []
          cookie_names: []
          key_claim_name: iss
          secret_is_base64: false
      - name: cors
        config:
          origins: ["https://app.example.com"]
          methods: [GET, POST, PUT, DELETE]
          max_age: 3600
      - name: request-transformer
        config:
          add:
            headers: ["X-Request-ID:$(request_id)"]

5.2 Gateway 安全功能

功能作用
认证集成JWT/OAuth/API Key 统一校验
速率限制防止流量冲击
IP 白名单限制调用来源
请求签名防篡改
响应过滤脱敏敏感字段
审计日志全链路追踪

6. GraphQL 安全

6.1 GraphQL 特有风险

# 深度查询攻击
query EvilQuery {
  user {
    friends {
      friends {
        friends {  # 无限嵌套
          friends {
            name
          }
        }
      }
    }
  }
}

# 资源耗尽
query ExpensiveQuery {
  allUsers {  # 百万用户
    posts {    # 每人百条
      comments { # 每条十条
        author {
          posts { ... }  # 又循环
        }
      }
    }
  }
}

6.2 GraphQL 防护

// graphql-shield + depth limit
import { rule, shield } from 'graphql-shield';
import depthLimit from 'graphql-depth-limit';
import { createComplexityLimitRule } from 'graphql-validation-complexity';

const isAuthenticated = rule()(async (parent, args, ctx) => {
  return ctx.user !== null;
});

const permissions = shield({
  Query: {
    user: isAuthenticated,
    adminData: isAdmin,
  },
  Mutation: {
    deleteUser: isAdmin,
  },
});

// Apollo Server 配置
const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    depthLimit(5),                    // 限制查询深度
    createComplexityLimitRule(1000),  // 限制复杂度
  ],
  context: ({ req }) => ({
    user: authenticate(req),
  }),
  plugins: [permissions],
});

7. API 审计与监控

// API 审计日志过滤器
@Component
public class ApiAuditFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, 
                                     HttpServletResponse res, 
                                     FilterChain chain) {
        long start = System.currentTimeMillis();
        chain.doFilter(req, res);
        long duration = System.currentTimeMillis() - start;
        
        AuditLog log = AuditLog.builder()
            .timestamp(Instant.now())
            .method(req.getMethod())
            .path(req.getRequestURI())
            .user(getCurrentUser())
            .ip(getClientIp(req))
            .userAgent(req.getHeader("User-Agent"))
            .status(res.getStatus())
            .durationMs(duration)
            .apiVersion(req.getHeader("X-API-Version"))
            .build();
            
        auditLogger.info(log);
    }
}

8. 总结

API 安全是纵深防御的关键环节:

┌────────────────────────────────────────────────────────────┐
│                        API 安全层次                         │
├────────────────────────────────────────────────────────────┤
│ 网络层    TLS 1.2+ / mTLS / IP 白名单                      │
├────────────────────────────────────────────────────────────┤
│ 网关层    认证 / 限流 / WAF / 请求签名                      │
├────────────────────────────────────────────────────────────┤
│ 应用层    输入验证 / 输出编码 / 业务鉴权                    │
├────────────────────────────────────────────────────────────┤
│ 数据层    字段级权限 / 数据脱敏 / 审计日志                  │
└────────────────────────────────────────────────────────────┘

API 安全的核心原则:

  1. 零信任:不信任任何调用方,每次都校验
  2. 最小权限:API 仅返回必要字段
  3. 深度防御:多层校验,单层失效不影响整体
  4. 可观测:全链路审计,异常可追踪

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「安全」更多文章

  1. Kubernetes安全体系:RBAC、PodSecurity与NetworkPolicy实战
  2. 安全合规与数据保护
  3. 渗透测试与红蓝对抗