SuperAPI AI 工具集

receiving-webhooks

编程与开发

查看项目

更新于 10-16 05:36

技能介绍

接收 Webhook 是一套通用指南,用于构建健壮的 Webhook 接收器与处理器。Webhook 本质上只是来自不可控来源的 HTTP POST,因此每个请求在签名验证通过前都应视为不可信。这些准则适用于任何消费 Webhook 的处理器,与具体提供商或平台无关。不同提供商的头名称、密钥格式和容忍度各异,文中名称遵循 Standard Webhooks 规范,但编写代码前应始终对照提供商文档确认确切的头名称、签名方案和重放窗口。

Standard Webhooks 是一个开放规范,并提供大多数主流语言的参考库,用于安全一致地发送和验证 Webhook。它定义了单一可互操作的格式,取代各提供商自创头名称和签名方案的做法。当提供商支持时,应优先使用官方 standardwebhooks 库而非手写验证,因为它会验证签名、强制执行时间戳容忍并防范重放攻击。如果提供商通过 Svix 发送 Webhook(表现为 svix-id、svix-timestamp 和 svix-signature 头),则应改用官方 Svix SDK。

不可妥协的要点:一是对每个请求验证签名,未验证的 Webhook 就是匿名互联网 POST,任何知道 URL 的人都能伪造事件,只对通过验证的负载采取行动;二是基于原始请求体验证,签名基于发送的精确字节计算,任何解析 JSON 再重新字符串化的框架都会破坏验证;三是在数秒内返回 2xx,其他任何状态(包括 3xx 重定向)通常被视为失败并重试,繁重工作应异步处理。

处理器结构:先读取原始请求体,验证前不要解析 JSON;使用提供商的官方库(或其文档化方案)传入签名头和签名密钥进行验证,失败返回 400;快速确认,立即返回 2xx(如 204),如果实际工作可能超过一两秒,则放入后台任务或队列;根据负载(事件类型)分支处理。文中给出了 standardwebhooks 的示例代码,并提醒 rawBody 必须是原始请求体(字符串或字节),而非解析后的 JSON,secret 是该端点的 base64 签名密钥。

签名头与密钥方面,多数提供商发送消息 ID(webhook-id)、时间戳(webhook-timestamp)和签名(webhook-signature,通常带版本的 HMAC 签名)的组合。应使用签名密钥而非 API 密钥,用于验证入站 Webhook 的密钥通常与调用提供商管理 API 的令牌不同;密钥仅保留在服务端,绝不放入客户端包或提交到版本库。

响应、重试与自动停用方面,只有 2xx 表示成功,其他所有状态码(通常包括 3xx)都被视为失败并按指数退避重试;应在提供商超时内响应(通常几秒到约 15 秒),若处理更久则先确认再异步处理;用 4xx 拒绝错误或伪造请求(验证失败返回 400),仅对真正需要重试的瞬时故障使用 5xx 或超时;端点常在持续失败后自动停用,许多提供商会通知(如运维或告警 Webhook、邮件),应保持处理器健康以避免被静默停用,并接入失败通知。

仅当没有官方库时才进行手动验证。若所用语言确实没有官方库,应严格遵循提供商文档的签名方案,不要自创。步骤为:提取密钥字节,签名密钥以 whsec_ 为前缀,去掉前缀并对剩余部分进行 base64 解码得到 HMAC 密钥;读取 webhook-id、webhook-timestamp 和 webhook-signature 头;检查时间戳容忍,若 webhook-timestamp 距当前时间过远则拒绝(Standard Webhooks 使用 5 分钟窗口)以防重放;使用原始请求体字节构造签名内容 {id}.{timestamp}.{body};用解码后的密钥对签名内容计算 HMAC-SHA256 并 base64 编码;使用常量时间比较,webhook-signature 是以空格分隔的 v1,<sig> 条目列表,若计算出的签名与其中任意一个匹配则通过,务必使用常量时间比较而非 ==。

常见验证陷阱包括:验证前解析了请求体导致重新字符串化改变字节,应使用原始请求体访问;使用了错误的密钥;密钥格式错误,某些提供商期望从 base64/hex 解码或去掉文档化前缀;用 curl 重放旧捕获的负载,验证会拒绝过期时间戳,应触发全新投递(如提供商的“resend”);反向代理剥离签名头,应检查代理配置;时钟偏移,未同步的服务器时间会导致时间戳验证失败。

检查清单:每个请求都验证签名(优先官方库);验证基于原始请求体(无预解析);验证失败返回 400;处理器在提供商超时内返回 2xx,繁重工作异步化;使用签名密钥并保留在服务端。