---
title: API 限流与请求签名(HMAC、时间窗与防重放)
date: 2025-11-26
keywords:
- 限流
- 请求签名
- HMAC
- 时间窗
- 防重放
- Nonce
description: 结合令牌桶/漏桶限流与HMAC请求签名, 通过时间窗与Nonce复用检测实现防重放与审计, 并提供服务端/客户端实现与验证流程。
categories:
- 文章资讯
- 编程技术
---
概述
API安全需要在入口层实现速率限制与请求完整性校验。通过令牌桶或漏桶在多维度限流, 配合HMAC签名与时间窗有效期以及Nonce复用检测, 可有效抵御重放与参数篡改, 并形成可审计链路。
关键实践与参数
- 限流模型: 令牌桶
capacity=100fill_rate=10/s, 漏桶平滑输出 - 维度: 每IP/每客户端Key/每用户维度限流, 叠加全局阈值
- 签名算法:
HMAC-SHA256基于规范化请求串 - 时间窗:
ts与服务器时钟偏移≤±300s, 过期拒绝 - Nonce复用检测: Redis
SETNXTTL=300s, 复用即拒绝 - Canonical字符串:
method\npath\nquery\nbodyHash\nts\nnonce - 响应语义: 限流返回
429, 签名失败返回401/403
示例/配置/实现
limit_req_zone $binary_remote_addr zone=ip:10m rate=10r/s;
server {
location /api/ {
limit_req zone=ip burst=50 nodelay;
proxy_pass http://api;
}
}
import crypto from "crypto"
function sign({ method, path, query, body, ts, nonce, secret }) {
const bodyHash = crypto.createHash("sha256").update(body || "").digest("hex")
const canonical = [method.toUpperCase(), path, query || "", bodyHash, String(ts), nonce].join("\n")
return crypto.createHmac("sha256", secret).update(canonical).digest("hex")
}
// 服务端校验(示意)
async function verify(req, secret, redis) {
const ts = Number(req.headers["x-ts"]) || 0
const nonce = String(req.headers["x-nonce"]) || ""
const sig = String(req.headers["x-signature"]) || ""
if (Math.abs(Date.now() / 1000 - ts) > 300) return { ok: false, code: 401 }
const ok = await redis.set(`nonce:${nonce}`, 1, { NX: true, EX: 300 })
if (!ok) return { ok: false, code: 401 }
const expect = sign({ method: req.method, path: req.path, query: req.queryString, body: req.rawBody, ts, nonce, secret })
return { ok: crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expect)) }
}
验证
- 重放测试: 复用相同
nonce与ts发起请求应被拒绝, 返回401/403 - 时钟偏移: 将
ts超出±300s, 服务端拒绝并记录审计事件 - 限流生效: 在并发压力下观察
429返回比例与队列延迟, 验证令牌桶配置 - 审计完整性: 记录签名失败与限流事件并可检索, 保证合规追溯
注意事项
- 严格规范化请求串以防参数顺序影响签名, 统一编码
- 对大请求体建议使用
bodyHash, 避免原文参与签名导致性能问题 - 时钟同步: 生产环境使用NTP保证偏差在合理范围
- 对高敏接口叠加二次挑战或设备绑定以提升安全

发表评论 取消回复