Verificar a assinatura
Cada entrega é assinada com o signing secret do endpoint (pwhsec_…), do jeito que a Stripe assina as dela.
O header
Headers
X-Pay-Signature: t=<unix time>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")> X-Pay-Event-Id: <uuid, the same on a resend>
Verifique
Ruby
t, v1 = request.headers["X-Pay-Signature"].scan(/t=(\d+),v1=(\h+)/).first body = request.raw_post expected = OpenSSL::HMAC.hexdigest("SHA256", ENV["PAY_WEBHOOK_SECRET"], "#{t}.#{body}") head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(expected, v1.to_s) head :unauthorized if (Time.now.to_i - t.to_i).abs > 300
Node
import crypto from "node:crypto" const [, t, v1] = req.get("X-Pay-Signature").match(/t=(\d+),v1=([0-9a-f]+)/) const expected = crypto.createHmac("sha256", process.env.PAY_WEBHOOK_SECRET) .update(`${t}.${rawBody}`).digest("hex") const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - Number(t)) <= 300
Lembre-se
- 1Assine o body cru exatamente como chegou; fazer parse e serializar o JSON de novo muda o conteúdo.
- 2Compare em tempo constante e recuse timestamps com mais de 5 minutos.
- 3Trocar o secret na página do app desativa o antigo na hora.