接入 IM 机器人,很多人只想到”收发消息”,容易忽略一个隐性但很重要的成本:每个渠道的 webhook 验签方式都不一样。它藏得深,但一旦做错,轻则收不到消息,重则等于把入口对伪造请求敞开。

我在做多端 IM 接入(dsh-im)时,9 个渠道在这里花的功夫,比想象中多得多。这篇讲验签差异为什么会成为隐性成本。

为什么验签这么关键

webhook 是渠道把事件推给你的公开入口。任何能访问到这个 URL 的人,都能伪装给系统”喂消息”。如果没有可靠的验签,就等于任何人都能冒充渠道给你的机器人发指令。

所以 webhook 处理的第一件事不是”解析消息”,而是先验签,确认这条消息真的是渠道发的。验签不过,直接拒收,别往下走。

差异在哪

麻烦就麻烦在,“验签”没有统一标准,每个渠道一套:

  • 签名算法:有的是 HMAC-SHA256,有的是 SHA1,有的是 RSA,算法还不一样;
  • 怎么拼:用什么字段、什么顺序、要不要带上时间戳、要不要加盐,各渠道定义不同;
  • 放哪:签名在 header 里,还是 body 里,还是 URL 参数里;
  • 是否带时间戳:有的会校验 timestamp 防重放,有的不会;
  • 密钥从哪来:应用凭据里的 secret,还是单独的 token。

同一套代码,换个渠道就要重新写一遍验签,还不能复用。

处理思路:验签也走”适配器”

好消息是,验签虽然每渠道不同,但它和”消息格式转换”是同一类问题——渠道差异,收敛到适配器里。

所以我把验签也放进每个渠道的适配器:适配器内部实现”这个渠道怎么验”,对外暴露一个统一的接口,比如 bool verify(event, headers, body)。上层只调用这个,不用知道底下是谁、用什么算法。

这样做的安全价值很直接:

  • 新渠道 = 新适配器里写一套验签,不会污染上层;
  • 上层不用为每个渠道写”敏感字段判断”,降低了把密钥/敏感信息泄漏出去的风险;
  • 一旦某个渠道的验签要改,只动那个适配器,影响面可控。

小心两个坑

  1. 密钥管理:各渠道的 secret/token 不能硬编码、不能打进日志。要集中管理、脱敏。这也是为什么适配器里要严格脱敏呼吸——别把敏感串原样抛给上层或日志。
  2. 验签失败要有可观测:验签失败不应该静默丢弃,至少要能看出来”是不是验签挂了/密钥错了/时间戳不对”。否则你会以为”没消息”,其实是”全被拒了”。

最后几条

  • webhook 处理先验签,再谈解析;验签不过直接拒收。
  • “验签没统一标准”是常态,每渠道一套,别指望复用。
  • 验签走上层统一接口、实现在适配器里,新渠道=新适配器一套验签。
  • 密钥集中管理、严格脱敏;验签失败要有日志和告警。

验签看起来是小事,但它决定了你的入口是开着还是关着。把这个隐性成本认清,是接入多渠道很重要的一步。