Generador de HMAC y validador de webhooks
Calcula HMAC-SHA256, SHA-384, SHA-512 o SHA-1 y comprueba si la firma recibida en el webhook coincide. Los modelos de GitHub, Stripe, Shopify, Mercado Pago y Slack ya montan el mensaje firmado como lo hace cada uno. El payload y la clave se quedan en el navegador.
HMAC del payload tal cual, con el algoritmo y la codificación que elijas.
La firma cubre el cuerpo en bruto, byte a byte. Un espacio, un salto de línea o un orden de campos distinto ya cambia el resultado. Pega el cuerpo tal como llegó, antes de pasar por JSON.parse o por un formateador.
Firma
Falta la clave
Introduce la clave secreta. La firma aparece mientras escribes.
Un JWT HS256 es un HMAC-SHA256 del header y del payload. El decodificador comprueba la firma del token con tu secreto.
¿Quieres leer el payload del webhook? Formatéalo en otra pestaña. El JSON formateado tiene otros bytes y no sirve para comprobar la firma.
¿Solo necesitas el hash, sin clave? El generador de hash calcula MD5, SHA-1 y SHA-256 de texto y archivos.
Qué es HMAC
HMAC es un hash calculado con una clave secreta (RFC 2104). Un SHA-256 normal lo puede recalcular cualquiera después de modificar el mensaje. Con HMAC, solo quien conoce la clave puede producir la firma correcta. Por eso se usa para demostrar que una petición vino de quien dice y no se modificó por el camino: webhooks, firma de APIs como AWS Signature V4 y tokens JWT HS256.
Formato de cada proveedor
Cada proveedor elige qué firma y cómo escribe el resultado. La tabla resume los modelos de esta página; el formato se comprobó en la documentación oficial de cada uno.
| Proveedor | Cabecera | Qué se firma | Formato |
|---|---|---|---|
| GitHub | X-Hub-Signature-256 | cuerpo | sha256=<hex> |
| Stripe | Stripe-Signature | t.cuerpo | t=<ts>,v1=<hex> |
| Shopify | X-Shopify-Hmac-Sha256 | cuerpo | <base64> |
| Mercado Pago | x-signature | id:…;request-id:…;ts:…; | ts=<ts>,v1=<hex> |
| Slack | X-Slack-Signature | v0:timestamp:cuerpo | v0=<hex> |
Cómo funciona la verificación de webhooks
El proveedor y tu aplicación guardan el mismo secreto. Al enviar el evento, el proveedor calcula el HMAC del cuerpo, o de un mensaje montado con timestamp y cuerpo, y envía el resultado en una cabecera. Tu aplicación lo recalcula con el cuerpo recibido y compara. Si coincide, el evento es auténtico. La comparación debe hacerse en tiempo constante, con crypto.timingSafeEqual en Node, hash_equals en PHP o hmac.compare_digest en Python, para no filtrar la firma por el tiempo de respuesta.
La causa número uno de firma inválida
El framework convierte el JSON en objeto antes de tu código, y al volver a serializarlo cambian el orden de las claves, los espacios y el escape de los acentos. La firma deja de coincidir aunque la clave sea correcta. Lee el cuerpo en bruto: express.raw() en Express, $request->getContent() en Laravel, un parámetro String con @RequestBody en Spring y request.get_data() en Flask. En NestJS, crea la aplicación con rawBody: true y usa req.rawBody.
Clave en texto, hex o Base64
HMAC trabaja con los bytes de la clave. Si el panel del proveedor muestra un secreto en hex y usas esa cadena como texto, los bytes son otros y la firma también. La mayoría de los proveedores de webhooks, entre ellos GitHub, Stripe, Shopify, Mercado Pago y Slack, usan el secreto como texto, tal como aparece en el panel, incluido el prefijo whsec_ de Stripe.
Timestamp y reenvíos
Una petición válida capturada sigue siendo válida si se reenvía. Stripe y Slack incluyen el timestamp en el mensaje firmado, y sus bibliotecas rechazan eventos de más de 5 minutos. Al probar con un payload antiguo copiado del log, la firma puede coincidir aquí y aun así tu servidor la rechaza por la hora.
Preguntas frecuentes sobre HMAC
Firma de webhooks, clave, codificación y los errores más comunes al validar.