目标
带签名的 Webhook 只在载荷含非 ASCII 字符时校验失败,指向编码问题。本配方在任何 JSON 解析之前保存原始请求字节,用原始字节计算 HMAC,并用 timingSafeEqual 比较,同时校验时间戳容差以抵御重放,从而定位是解析改写还是代理重编码导致的失败。
步骤要点
- 在 JSON 解析之前保留原始字节并计算 HMAC。
- 用 timingSafeEqual 做常量时间比较。
- 校验时间戳容差,拒绝超过五分钟的重放。
- 确认代理没有改写请求体字节。
app.use('/webhook', express.raw({ type: '*/*' }));
app.post('/webhook', (req, res) => {
const mac = crypto.createHmac('sha256', secret).update(req.body).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(req.get('x-signature') || ''));
res.status(ok ? 200 : 401).end();
});
排错提示
- 代理可能改写字节,需保证处理器拿到原始字节。
- 非 ASCII 载荷必须对字节缓冲而非重序列化字符串计算签名。
- 时间戳容差需兼顾重放防护与时钟偏移。
适用边界
- 需要访问原始请求字节。
- 不覆盖非对称签名方案。