按文档用 HMAC-SHA256 校验 webhook 签名,本地用同一份 secret 算出来的值和请求头里的对不上,失败率接近一半,偶尔又能通过。

这类报错背后常见的原因有几处,逐个排除就行。签名基于原始请求体字节,框架解析后重新序列化会改变字段顺序;时间戳参与签名,本地时钟偏差超过容差就判失败;secret 用了另一个端点的,多个 endpoint 各有独立密钥;头部里有多个签名值,只取了第一个做比较。

第一件事是看本地与服务端的时间偏差

python -c "import time;print(int(time.time()))"; curl -sI https://api.openai.com | grep -i ^date

接着用原始字节复算一次签名

python -c "import hmac,hashlib;print(hmac.new(b'whsec_x',b'{\"a\":1}',hashlib.sha256).hexdigest())"

随后确认框架能拿到未解析的原始 body

python -c "from starlette.requests import Request;print(hasattr(Request,'body'))"

末了把头部里的完整签名字段打出来比对

python -c "import json;print(json.load(open('headers.json')).get('openai-signature'))"

别只看报错消失,验证要多做一步。连续二十次投递全部校验通过,签名比较改成常量时间函数后无一处失败,日志里时间戳偏差小于 30 秒。

提醒一点。验签失败多半不是密钥错,而是拿到的是解析后的 JSON,中间件里必须先缓存原始字节,再交给业务反序列化。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189