Stripe and Shopify Webhook HMAC-SHA256 Verification: Why JSON Body Parsers Break Raw-Body Signatures

2026-11-03来源 dev.to SecurityEnglish

The most frequent cause of 401 Unauthorized webhook failures in Node and TypeScript backends is verifying the HMAC-SHA256 signature against JSON.stringify(req.body) instead of the exact raw UTF-8 byte stream transmitted by Stripe or Shopify.

Whenever a global JSON body parser deserializes and re-serializes a payload, subtle differences in key spacing, escaped characters, or scientific notation produce a completely different SHA-256 digest.

import crypto from 'node:crypto'; export function verifyStripeWebhookRawHmac({ rawBodyBuffer, sigHeader, secret, nowSec, toleranceSec = 300 }) { const parts = Object.fromEntries( String(sigHeader || '').split(',').map((kv) => kv.trim().split('=')) ); const ts = Number(parts.t); if (!Number.isInteger(ts) || Math.abs(nowSec - ts) > toleranceSec) return false; const signedPayload = `${ts}.${rawBodyBuffer.toString('utf8')}`; const expected = crypto.createHmac('sha256', secret).update(signedPayload, 'utf8').digest(); const provided = Buffer.from(String(parts.v1 || ''), 'hex'); return expected.length === provided.length && crypto.timingSafeEqual(expected, provided); } 

原文:https://www.a8q9.cn/en/article/stripe-and-shopify-webhook-hmac-sha256-verification-why-json-body-parsers-break--774937.html

相关文章