API 是现代应用的核心接口,也是攻击者的主要目标。OWASP API Security Top 10 专门针对 API 的独特风险。本文从设计到运维,系统讲解 API 安全的关键要素。
1. OWASP API Security Top 10 2023
| 排名 | 风险 | 说明 | 示例 |
|---|---|---|---|
| API1:2023 | 对象级授权失效 | 未验证用户对特定对象的访问权限 | 修改 URL 查看他人订单 |
| API2:2023 | 认证失效 | 弱令牌、JWT 误用、无 MFA | jwt.io 修改令牌权限 |
| API3:2023 | 对象属性级授权失效 | 返回过多敏感字段 | 接口返回用户密码哈希 |
| API4:2023 | 不受限制的资源消耗 | 无速率限制导致 DoS | 大量请求耗尽资源 |
| API5:2023 | 功能级授权失效 | 普通用户调用管理员接口 | POST /admin/delete |
| API6:2023 | unrestricted access to business flow | 自动化攻击业务流程 | 批量注册、黄牛抢票 |
| API7:2023 | 服务器端请求伪造 | API 参数控制服务端请求 | SSRF via URL 参数 |
| API8:2023 | 安全配置错误 | 默认配置、CORS 过于宽松 | 调试模式开启 |
| API9:2023 | 库存管理不当 | 使用不安全的依赖 | 日志库存在 RCE |
| API10:2023 | unsafe consumption of APIs | 调用外部 API 未验证 | 外部 API 返回恶意数据 |
2. 认证与授权
2.1 API 认证方案
| 方案 | 适用 | 安全等级 | 注意 |
|---|---|---|---|
| API Key | 服务间调用 | ★★ | 需 HTTPS,定期轮换 |
| JWT | 用户态 API | ★★★ | 短过期,不存敏感信息 |
| mTLS | 高安全微服务 | ★★★★★ | 双向证书认证 |
| OAuth2 | 第三方 API | ★★★★ | 使用 PKCE |
| Signed Request | AWS/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 安全的核心原则:
- 零信任:不信任任何调用方,每次都校验
- 最小权限:API 仅返回必要字段
- 深度防御:多层校验,单层失效不影响整体
- 可观测:全链路审计,异常可追踪
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。