集成Stripe、Shopify、Slack时,作者每次都从零手写Webhook签名验证代码,因为上一次的代码完全无法复用。这次他直接通读了8家服务商的官方规范,发现差异远比自己预想的大。
表面上所有服务商都遵循“HMAC-SHA256”这一共同点,但具体签名的内容却各有不同。Stripe签的是<时间戳>.<请求体>,Slack是v0:<时间戳>:<请求体>,Paddle是<时间戳>:<请求体>,而Standard Webhooks则签.<时间戳>.<请求体>。GitHub和Shopify则只签请求体本身。
尤其隐蔽的是Stripe与Paddle的比较:算法相同、编码相同、参与签名的两个要素也相同,唯一区别是分隔符。把验证Stripe签名的代码直接搬到Paddle,改个Header名就能跑起来,不报任何错误,但会拒绝所有真实请求。
第一个值得注意的差异:相同的密钥前缀,含义却完全不同。Stripe的签名密钥形如whsec_abc123...,整个字符串直接作为HMAC密钥。而Standard Webhooks虽然密钥同样以whsec_开头,却要求把前缀之后的部分做Base64解码,再用解码结果作密钥。
这个坑比错误的分隔符更危险,因为失败方式是静默的。如果用Stripe的约定去实现Standard Webhooks验证,计算出的HMAC使用了错误的密钥,签名匹配永远不会成功。作者表示,他见过有人因此“修复”问题,方式是在验证外加try/catch,记录日志后直接放行。Standard Webhooks是Svix、Clerk、Resend等多家服务商背后的方案,影响范围远不止名字看上去那么大。
第二个值得注意的差异:Twilio在两方面都是异类。它用的是SHA-1而非SHA-256,并且签名对象不是请求体,而是请求URL。这意味着针对Twilio的验证逻辑从一开始就不能沿用其他服务商的模式,需要单独处理。
最终结论:Webhook签名验证不能靠“从一个项目复制到另一个项目”,必须逐家核对签名格式。真正的风险不在算法本身,而在于“看起来一样、用起来不同”的细节。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.