用途:本技能整理 Webhook(HTTP 回调)发送端与接收端的实现模式,重点解决正确性与安全问题,包括签名、防重放、至少一次投递、幂等消费与 SSRF。
主要能力:
- 签名与验证:对原始请求体(非重新序列化的 JSON)做 HMAC,签名串包含时间戳(如 t=...,v1=...),使用 timing-safe 比较并先校验长度;接收端必须在任何 JSON 解析器之前读取原始字节。
- 防重放:时间戳加容忍窗口,按事件 id 去重。
- 投递可靠性:至少一次投递,指数退避加抖动重试,仅重试网络错误与 408/429/500/502/503/504,尊重 Retry-After,30 秒超时,异步队列投递,先持久化事件再入队。
- 幂等消费:用 Redis SET NX 或数据库唯一约束按 event.id 去重,失败时删除去重键以允许重试。
- SSRF 防护:仅允许 HTTPS,阻止私有/回环/链路本地/云元数据地址段,禁用或逐跳校验重定向,处理 DNS 重绑定。
- 死信队列与自动禁用:重试耗尽后进入 DLQ,按失败率告警并自动禁用长期失效端点,提供手动重放。
- 监控:按端点统计投递量、成功率、延迟分位、错误分布与 DLQ 深度。
使用方式:资料给出 TypeScript 示例,包括 sign()、signedHeaders()、nextDelay()、dispatch()、matches()、verify() 中间件、process() 幂等处理与 onExhausted()。接收端示例使用 express.raw({ type: "application/json" }) 保留原始请求体,验证通过后再 JSON.parse,并先返回 2xx 再异步处理。
适用场景:构建 Webhook 发送方或接收方端点、事件订阅模型(resource.action 命名、通配符)、胖/瘦事件设计取舍。
限制与注意:胖事件载荷不可用于安全或授权决策,应重新拉取权威状态;Redis 去重为尽力而为,涉及资金变动需数据库唯一约束;投递不保证顺序,需按 created/version 处理乱序与过期;接收端应在持久化后再返回 2xx。参考值:重试 5 次、初始 1 秒、最大 1 小时、因子 2、0–25% 抖动;签名容忍约 300 秒;去重 TTL 约 24 小时;载荷上限小于 256KB;轮换期间支持两个有效密钥。具体平台与收费信息暂未提供。