Generador de HMAC
Calcula HMAC-SHA-1, SHA-256, SHA-384 y SHA-512 a partir de un mensaje y una clave, e identifica de cuál procede una firma dada.
Introduzca una clave para calcular las firmas.
Qué hace esta herramienta
HMAC convierte un mensaje y un secreto compartido en una firma corta. Quien tenga el mismo secreto puede recalcularla y ver si el mensaje llegó sin cambios y si vino de alguien que también conoce el secreto. Esta herramienta calcula las cuatro variantes habituales a la vez y, dada una firma que le enviaron, le dice cuál la produjo.
Ese segundo sentido suele ser el motivo por el que la gente llega aquí. Un webhook falla la verificación, y la pregunta real no es «¿es válida esta firma?» sino «¿cuál de las varias cosas plausibles estoy haciendo distinto del remitente?». Un prefijo del estilo sha256= delante de una firma pegada se reconoce y se elimina, así que el valor de la cabecera puede pegarse tal y como llegó.
La clave es donde esto se tuerce
HMAC firma bytes con bytes. El mensaje suele ser evidente —el cuerpo de la petición en bruto— pero la clave casi nunca lo es, porque un secreto llega como cadena y una cadena no es bytes hasta que usted decide cómo leerla:
a3f2 -> 61 33 66 32 4 bytes como texto a3f2 -> a3 f2 2 bytes como hex
Ambas lecturas son legítimas, ambas producen una firma perfectamente bien formada, y las dos firmas no tienen nada en común. Nada le avisa: no hay error, no hay queja de longitud, solo un valor que no coincide con el que calculó el remitente. Por eso aquí la codificación de la clave es una elección visible y no una suposición: cuando una firma no coincide, esto es lo primero que hay que cambiar.
Como guía aproximada: un secreto con un prefijo tipo whsec_ o una tirada de letras y dígitos en mayúsculas y minúsculas suele ir como texto; una cadena de exactamente 32 o 64 caracteres que solo usa 0-9 y a-f suele ser hexadecimal; y una que termina en = es casi con seguridad Base64. Pero consulte la documentación del remitente en vez de la forma, porque la forma no es prueba.
El mensaje tiene que ser los bytes exactos
La otra mitad de una discrepancia es el mensaje. HMAC está definido sobre bytes, así que cualquier cosa que cambie los bytes cambia la firma por completo: no hay nota parcial ni casi acierto.
- Volver a serializar el JSON. Analizar un cuerpo y volver a convertirlo en cadena puede reordenar claves, cambiar espacios o normalizar números. Firme y verifique el cuerpo en bruto que recibió, nunca una copia que ha ido y vuelto.
- Un salto de línea final. Algunas herramientas lo añaden al guardar el cuerpo en un archivo; es un byte, y lo cambia todo.
- La codificación de caracteres. Un cuerpo con texto no ASCII tiene que leerse con la misma codificación en ambos lados: en la práctica, UTF-8.
- Compresión o middleware. Si algo delante de su manejador descomprime o reescribe el cuerpo, verifique antes de que eso ocurra, no después.
Además, muchos proveedores no firman solo el cuerpo. Stripe firma una marca de tiempo y el cuerpo unidos por un punto; AWS firma una petición canónica construida a partir del método, la ruta, las cabeceras y un hash de la carga. Si la verificación falla contra el cuerpo simple, es probable que la cadena firmada no sea el cuerpo simple: eso está documentado del lado del remitente y conviene leerlo antes de seguir depurando.
Por qué aquí se ofrece SHA-1 y en la herramienta de hash no
La herramienta de hash marca SHA-1 como roto. Esta lista HMAC-SHA-1 sin advertencia, y es deliberado, no un descuido.
SHA-1 es inservible para firmas y certificados porque se pueden construir colisiones: dos documentos distintos pueden hacerse compartir un resumen. HMAC no depende de esa propiedad. Su seguridad descansa en la clave secreta, y la construcción —resumir el mensaje dos veces, con la clave mezclada en ambas— aguanta incluso cuando el hash subyacente es débil frente a colisiones. HMAC-SHA-1 sigue siendo sólido y es lo que usan OAuth 1.0a y la firma antigua de AWS, así que una herramienta que se negara a calcularlo sería simplemente menos útil sin ser más segura.
Para algo nuevo, SHA-256 es la opción sensata. Los resúmenes más largos no son significativamente más fuertes aquí —el techo de seguridad es la clave, no la longitud del resumen—, así que SHA-384 y SHA-512 solo merecen la pena cuando algo con lo que debe interoperar los pide.
Comparar firmas con seguridad
Esta página compara con una comparación de cadenas normal, y aquí está bien: usted tiene la clave, así que no hay nada que filtrar. En un servidor que verifica una petición entrante, no está bien. Una comparación normal se detiene en el primer byte distinto, así que el tiempo que tarda revela qué parte de una conjetura era correcta, y un atacante que pueda enviar muchas peticiones puede recuperar una firma válida byte a byte.
Use la comparación en tiempo constante que ofrezca su plataforma —crypto.timingSafeEqual en Node, hmac.compare_digest en Python, hash_equals en PHP— sobre los bytes en bruto y no sobre cadenas hexadecimales. Junto a eso, compare contra una firma que haya calculado usted y no confíe en un algoritmo nombrado en la petición, y rechace un mensaje cuya marca de tiempo esté lejos de ahora para que una firma válida antigua no pueda reproducirse.
HMAC no es un hash, ni una firma
Aquí se confunden tres cosas, y la diferencia importa para lo que se puede afirmar después:
- Un hash toma un mensaje y produce un resumen. Cualquiera puede calcularlo, así que demuestra que el mensaje no cambió por accidente, no que venga de alguien en concreto.
- Un HMAC toma un mensaje y un secreto compartido. Las dos partes tienen la misma clave, así que demuestra que el remitente conocía el secreto. No puede demostrar cuál de las dos lo envió, porque cualquiera pudo producirlo.
- Una firma usa una clave privada que solo tiene el remitente y una clave pública con la que cualquiera puede verificar. Eso es lo que da el no repudio: el remitente no puede negarlo después.
Así que HMAC es la herramienta adecuada entre dos sistemas que ya comparten un secreto —webhooks, API internas, tokens de sesión— y la equivocada cuando hay que demostrar a un tercero quién envió algo. Fíjese también en que autentica pero no oculta: el mensaje viaja en claro, y HMAC no dice nada sobre confidencialidad.
Verificar la firma de un webhook de Stripe
Stripe es la única receta de aquí que no es un pegado, y suele ser la que la gente ya ha intentado antes de llegar. La cabecera Stripe-Signature es una lista de elementos separados por comas y no una firma, y la cadena que Stripe firmó no es el cuerpo de la petición por sí solo. La propia documentación de Stripe parte la cabecera en líneas para que se lea mejor; una real llega en una sola línea.
Stripe-Signature: t=1492774577, v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd, v0=6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39
- Mensaje: el valor de t=, luego un punto, luego el cuerpo de la petición en bruto exactamente como llegó. Para la cabecera de arriba esa cadena empieza por 1492774577. y el cuerpo va justo detrás. Stripe la llama la carga firmada, y es toda la razón por la que una firma de Stripe nunca coincide con el cuerpo solo.
- Clave: el secreto de firma de ese único endpoint, entero, incluido su prefijo whsec_. No es su clave de API, ni el secreto de otro endpoint: la misma URL registrada dos veces tiene dos.
- Codificación de la clave: Texto. Un secreto de firma es una cadena que eligió Stripe y, aunque parezca aleatorio, no es hexadecimal ni Base64; leerlo como cualquiera de los dos da bytes distintos y una firma que no coincide con nada.
- Firma a comprobar: solo el elemento v1, no la cabecera entera. v1= se pega tal cual, porque esa etiqueta se elimina igual que sha256=; la cabecera completa, en cambio, no, porque el t= inicial se toma por la etiqueta y lo que le sigue no es una firma en absoluto.
Conviene saber tres cosas más de esta cabecera antes de depurar cualquier otra cosa. Ignore todo esquema que no sea v1: el elemento v0 de los eventos de prueba no es una firma real a propósito, e ignorar el resto es justo lo que impide que le impongan un esquema más débil. Mientras se rota el secreto de un endpoint, ambos siguen vivos hasta un día y la cabecera lleva un elemento v1 por secreto, de los que solo uno coincidirá. Y cada intento de entrega se firma de nuevo, así que un reintento no lleva la firma del primero: la marca de tiempo dentro de la cadena firmada es además lo que convierte el rechazo de una firma antigua en una defensa real y no en un gesto, porque no puede alterarse sin romper la firma.
Verificar la firma de un webhook de GitHub
GitHub es el pegado alrededor del cual se construyó esta herramienta, y la única receta de aquí que puede comprobarse de principio a fin, porque GitHub publica un ejemplo resuelto. La firma llega en X-Hub-Signature-256 como la etiqueta sha256= seguida de un resumen hexadecimal, y esa etiqueta se reconoce y se elimina aquí, así que el valor de la cabecera entra exactamente como llegó.
X-Hub-Signature-256: sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17
- Mensaje: el cuerpo de la petición en bruto, byte a byte. El ejemplo publicado por GitHub firma el cuerpo Hello, World! y nada detrás: ningún salto de línea final.
- Clave: el secreto que escribió en los ajustes del webhook, exactamente como lo escribió. El ejemplo publicado usa It's a Secret to Everybody.
- Codificación de la clave: Texto. Un secreto de GitHub es una cadena que eligió usted, así que nunca es hexadecimal: incluso un secreto formado solo por dígitos hexadecimales se lee como los caracteres que escribió.
- Firma a comprobar: el valor entero de la cabecera, sha256= incluido. El hexadecimal se compara sin distinguir mayúsculas, así que da igual cómo lo impriman sus registros.
Esos tres valores juntos son la comprobación de cordura más rápida que existe: péguelos y esta página informa de una coincidencia con HMAC-SHA-256 en hexadecimal, lo que le dice que tanto la calculadora como su lectura de la receta son correctas antes de probar cualquiera de las dos en una entrega real. GitHub envía además una cabecera más antigua, X-Hub-Signature, que es HMAC-SHA-1 sobre el mismo cuerpo y se mantiene solo por compatibilidad heredada; como esta página calcula los cuatro resúmenes a la vez, una firma de cualquiera de las dos cabeceras se identifica sin que usted tenga que decir de cuál. Lo único que no puede rescatar es un cuerpo que llegó como algo distinto de los bytes que GitHub envió: las cargas pueden llevar caracteres fuera de ASCII, y ambos lados tienen que leerlos como UTF-8.
Verificar la firma de un webhook de Shopify
Shopify escribe su resumen en Base64 y no en hexadecimal, y esa es la única diferencia que importa aquí: los mismos 32 bytes se convierten en 44 caracteres que terminan en un solo signo igual, y ese carácter es relleno y no una etiqueta, así que no se le recorta nada por delante.
X-Shopify-Hmac-SHA256: dXEH6g6yUJ/CESIczphLijdXC211hsIsRvQ3nIsEPhc=
- Mensaje: el cuerpo de la petición en bruto, byte a byte tal como se entregó. El propio aviso de Shopify es sobre el middleware que analiza el cuerpo: verifique primero y analice después, porque un analizador que ya ha convertido el cuerpo en un objeto ha tirado los bytes.
- Clave: el secreto de cliente de la aplicación a la que pertenece el webhook. No su token de acceso, ni la clave de API que está al lado en el mismo panel.
- Codificación de la clave: Texto. Como en los otros dos, el secreto es una cadena y no una codificación de bytes.
- Firma a comprobar: el valor entero de la cabecera. Se pega tal cual y, como contra cada resumen se prueban tanto hexadecimal como Base64, la codificación que esta página devuelve es en sí misma la respuesta a en qué grafía la escribió el remitente.
El valor del bloque de arriba está ahí para mostrar la forma: son los mismos 32 bytes del ejemplo de GitHub, escritos en Base64 en vez de en hexadecimal. Merece la pena verlos uno al lado del otro, porque es todo el contenido de la diferencia entre las dos cabeceras —un resumen, dos grafías— y es la razón por la que esta página nombra la codificación junto al algoritmo en vez de decirle solo que algo coincidió.
Preguntas frecuentes
- Mi firma no coincide. ¿Qué compruebo primero?
- La codificación de la clave y luego los bytes del mensaje. Un secreto que parece hexadecimal a menudo se quiere como hexadecimal y no como texto, y ambos producen firmas completamente distintas sin error en ningún caso. Después, asegúrese de firmar el cuerpo en bruto tal como se recibió y no una copia re-serializada.
- ¿Por qué se ofrece SHA-1 si la herramienta de hash dice que está roto?
- Porque HMAC no depende de la resistencia a colisiones, que es la propiedad que SHA-1 perdió. Su seguridad viene de la clave. HMAC-SHA-1 sigue siendo sólido y en uso por OAuth 1.0a y la firma antigua de AWS, aunque SHA-256 es el valor por defecto adecuado para algo nuevo.
- ¿Un resumen más largo es más seguro?
- No de forma significativa. La fuerza de un HMAC está limitada por el secreto, no por la longitud del resumen, así que SHA-512 no es cuatro veces mejor que SHA-256. Elija el que espere la otra parte.
- ¿Qué diferencia hay entre HMAC y una firma digital?
- HMAC usa un secreto que ambas partes conocen, así que demuestra que el remitente conocía el secreto pero no cuál de los dos lo envió. Una firma digital usa una clave privada que solo tiene el remitente, de modo que un tercero puede verificarla y el remitente no puede negarla. Si necesita esa última propiedad, HMAC no es la herramienta.
- ¿Debo comparar firmas con ===?
- En un servidor, no. Una comparación normal vuelve en cuanto los bytes difieren, y ese tiempo filtra qué parte de una firma adivinada era correcta. Use la comparación en tiempo constante de su plataforma sobre los bytes en bruto. En esta página da igual, porque usted ya tiene la clave.
- ¿Se envía mi clave a algún sitio?
- No. Todo se calcula en su navegador con la Web Crypto API; el mensaje y la clave nunca salen de su dispositivo.
- ¿Cómo verifico la firma de un webhook de Stripe?
- Una tres cosas en una sola cadena —la marca de tiempo del elemento t= de la cabecera, un punto y el cuerpo de la petición en bruto— y fírmela con el secreto de firma del endpoint leído como texto; después compare el resultado con el elemento v1 de la cabecera. La sección de arriba lo recorre campo por campo. Lo que hace caer a casi todo el mundo es el primer paso: el cuerpo por sí solo no es lo que Stripe firmó.
- ¿Cómo verifico la firma de un webhook de GitHub?
- Firme el cuerpo de la petición en bruto con el secreto del webhook leído como texto, con SHA-256, y compare el resumen hexadecimal con la cabecera X-Hub-Signature-256. La etiqueta sha256= puede quedarse al pegarla aquí. GitHub publica un ejemplo resuelto —el cuerpo Hello, World! con el secreto It's a Secret to Everybody— y es la forma más rápida de comprobar su lectura de la receta antes de probarla en una entrega real.
- ¿Cómo verifico la firma de un webhook de Shopify?
- Firme el cuerpo de la petición en bruto con el secreto de cliente de la aplicación a la que pertenece el webhook, leído como texto, con SHA-256, y compare el resumen en Base64 con la cabecera X-Shopify-Hmac-SHA256. El valor entero de la cabecera se pega tal cual: el signo igual final es relleno de Base64. Cuando no coincide, la causa habitual es un middleware que analizó el cuerpo antes de que su manejador llegara a verlo.
- ¿Cómo verifico la firma de un webhook de cualquier otro proveedor?
- Lo resuelven cuatro preguntas, y la documentación del remitente tiene las cuatro: qué cabecera lleva la firma, qué cadena se firma en realidad, cómo se supone que hay que leer el secreto y si el resumen es hexadecimal o Base64. Rellene eso en los campos de arriba. Si aun así no coincide, la respuesta es casi siempre la segunda: muchísimos remitentes firman el cuerpo con algo unido a él y no el cuerpo solo.
Herramientas relacionadas
- Depurador de códigos TOTP / 2FA
Genera y depura códigos 2FA, con la derivación completa.
- Bcrypt
Genera un hash bcrypt o comprueba una contraseña contra uno.
- Decodificador / verificador de JWT
Decodifica y verifica JSON Web Tokens: firma y claims.
- Generador de hash
MD5, SHA-1, SHA-256, SHA-384 y SHA-512 a la vez.