← 所有工具
我们的笔记

CyberChef 手动校验 X-Device-Signature

webhook 签名对不上时,你首先要判断的是哪一侧错了——你的还是我们的。CyberChef 能让你在浏览器里跑 UnifyPort 完全一样的签名算法,用 handler 看到的同一组输入,三十秒内出结果。

UnifyPort 到底在签什么

每次投递只要配置了签名密钥,就会带三个 header:

X-Device-Delivery-Id: d_01J2K…
X-Device-Timestamp: 1716800000
X-Device-Signature: 9f8c…

签名按以下方式计算:

X-Device-Signature = hex( HMAC-SHA256( secret, timestamp + "." + raw_body ) )

调试前贴几条在屏幕边:

  • timestamp 和 body 中间的那个字节是 ASCII 点号(.),不是换行。
  • raw_body 指我们发过去的原始字节——在你这边做任何 JSON 解析、去空白、unicode 归一化之前
  • hex 输出是小写。如果跟大写 digest 比对,会假阴性。

CyberChef recipe

在 CyberChef 里按顺序把两个操作丢进 recipe 面板:

  1. 1. HMAC——把 Hashing function 设成 SHA256。把你的签名密钥贴进 KeyTypeUTF8
  2. 2. To Hex——Delimiter 留空,Bytes per line 设成 0。这样能得到没有空格的小写 hex 字符串,跟 header 对得上。

Input 面板里粘贴的内容必须正好是 <timestamp>.<raw_body>——比如:

1716800000.{"id":"evt_demo","type":"message.received",...}

Output 面板出来的应该跟那条投递里的 X-Device-Signature 一致。如果不一致,bug 锁定在三处之一:用了不同的 secret、body 在路上被改过、或者你读的是错的 timestamp header。

这种手动校验真正派上用场的时刻

  • 轮换签名密钥之后。在你怀疑 DNS 或部署没刷新之前,先确认新密钥确实到了 handler。
  • 升级 JSON 库之后。有些解析器在交给验证逻辑前重新输出过 body——这种事很容易漏看,直到签名突然对不上。
  • 队友说"我这里能跑"的时候。两个人各跑同一个 recipe、同一组输入;digest 不一样的那个就是拿了错的 secret。

把密钥贴进网页这件事

CyberChef 是纯静态的客户端 bundle——完全在你浏览器里跑,官方托管版本里 secret 不会离开你的机器。话虽如此,如果你担心浏览器扩展或共享会话,可以本地跑 CyberChef(clone 仓库后打开 index.html)或者用 DevToys——完全离线的桌面端等价物。生产环境的签名密钥轮换,要走代码做 constant-time 比对,而不是肉眼看 CyberChef 输出。

手动验过之后

把等价逻辑搬进你的 handler。大多数语言标准库里就有原语:Node 的 crypto.createHmac、Python 的 hmac.new、Go 的 hmac.New。比对一定要用 constant-time 函数(timingSafeEqualhmac.compare_digesthmac.Equal)——绝对不要用 ==