Cómo se usa
- Elige el Algoritmo. HMAC-SHA256 está seleccionado de forma predeterminada y es el que usan la mayoría de los webhooks y las API.
- Introduce la Clave secreta y ajusta el Formato de la clave a Texto (se lee como UTF-8), Hexadecimal o Base64. Usa Hexadecimal o Base64 cuando la clave sean bytes sin procesar y no una cadena parecida a una contraseña.
- Escribe o pega el Mensaje, cambia la entrada a Hexadecimal para usar bytes sin procesar o haz clic en Abrir archivo. El HMAC se actualiza mientras escribes.
- En Formato de salida, elige hex (minúsculas), HEX (mayúsculas) o Base64 y copia el resultado, o pega una firma recibida en Comparar con para ver Coincide o No coincide.
Cómo funciona HMAC
HMAC (código de autenticación de mensajes basado en hash, RFC 2104) combina un hash criptográfico con una clave secreta. Solo quien conoce la clave puede obtener el valor correcto para un mensaje, así que un HMAC que coincide demuestra dos cosas a la vez: que el mensaje no se ha modificado y que procede de alguien que tiene la clave.
La construcción es H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ message)). Si la clave es más larga que el tamaño de bloque del hash (64 bytes para MD5, SHA-1 y SHA-256, y 128 para SHA-384 y SHA-512), primero se le aplica el hash; después se rellena con ceros hasta ese tamaño y se combina mediante XOR con ipad (bytes 0x36) para calcular un hash interno sobre el mensaje, y con opad (bytes 0x5c) para calcular un hash externo sobre el resultado interno. Una construcción ingenua como SHA-256(key ‖ message) no tiene hash externo, así que cualquiera que vea una etiqueta puede añadir datos al mensaje y calcular una etiqueta nueva y válida sin conocer la clave (un ataque de extensión de longitud).
La salida tiene la longitud del hash subyacente: 32 bytes, o 64 caracteres hexadecimales, en HMAC-SHA256. RFC 2104 recomienda una clave al menos así de larga, y el generador de Base64 aleatorio crea claves adecuadas.
Dónde se usan las firmas HMAC
- Webhooks de GitHub. El encabezado
X-Hub-Signature-256essha256=seguido del HMAC-SHA256 en hexadecimal del cuerpo sin procesar de la solicitud, con el secreto del webhook como clave. Puedes pegar el valor completo del encabezado en Comparar con; el prefijosha256=se ignora. - Webhooks de Stripe. El encabezado
Stripe-Signaturecontiene una marca de tiempoty una firmav1: el HMAC-SHA256 en hexadecimal de la marca de tiempo, un punto y el cuerpo sin procesar, con el secreto del endpoint (whsec_...) como clave, tratado como texto. - Webhooks de Shopify.
X-Shopify-Hmac-Sha256contiene el HMAC-SHA256 del cuerpo sin procesar en Base64, con el secreto de cliente de la app como clave, así que elige la salida Base64 o pega el valor del encabezado en Comparar con. - AWS Signature Version 4. La clave de firma se deriva encadenando HMAC-SHA256: primero la fecha, con
AWS4más la clave de acceso secreta como clave, y después la región, el servicio yaws4_request, cada uno con el resultado anterior como clave. Ajusta el Formato de la clave a Hexadecimal para usar la salida de un paso como clave del siguiente. - JSON Web Tokens. HS256, HS384 y HS512 son HMAC-SHA256, -384 y -512 sobre el encabezado codificado, un punto y la carga útil codificada. La tercera parte del token es ese HMAC en Base64 seguro para URL, que Comparar con acepta tal cual. El decodificador JWT verifica tokens completos.
Calcula siempre el HMAC sobre el cuerpo sin procesar, exactamente como se recibió. Analizar el JSON y volver a serializarlo cambia los espacios o el orden de las claves, e incluso un solo salto de línea de más produce un HMAC completamente distinto.
HMAC en código
# Python
import hmac, hashlib
sig = hmac.new(b'key', b'message', hashlib.sha256).hexdigest()
hmac.compare_digest(sig, received) # comparación en tiempo constante
// Node.js
const crypto = require('crypto');
const sig = crypto.createHmac('sha256', 'key').update('message').digest('hex');
// timingSafeEqual lanza un error si las longitudes difieren, así que compruébalas antes
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(received));
// PHP: el mensaje va antes que la clave
$sig = hash_hmac('sha256', 'message', 'key');
hash_equals($sig, $received);
# OpenSSL: una clave de texto y, después, la misma clave en hexadecimal
printf '%s' 'message' | openssl dgst -sha256 -hmac 'key'
printf '%s' 'message' | openssl dgst -sha256 -mac HMAC -macopt hexkey:6b6579
# Vector de prueba: clave "key", mensaje "The quick brown fox jumps over the lazy dog"
# HMAC-SHA256 = f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
Cuando verifiques una firma en tu propio código, usa una comparación en tiempo constante como las de arriba. Un simple == se detiene en el primer carácter distinto, y esa diferencia de tiempo puede permitir a un atacante encontrar una firma válida byte a byte. En los esquemas con marca de tiempo, como el de Stripe, rechaza también las solicitudes cuya marca de tiempo tenga más de unos minutos de antigüedad; así se bloquean los ataques de repetición.
Preguntas frecuentes
¿HMAC-MD5 o HMAC-SHA1 siguen siendo seguros?
Los ataques de colisión rompen MD5 y SHA-1 como hashes simples, pero no ofrecen una forma práctica de falsificar un HMAC, que depende de otras propiedades del hash y de una clave secreta (RFC 6151 analiza el caso de HMAC-MD5). Puedes mantener las integraciones que los exigen, como los códigos TOTP basados en HMAC-SHA1. Para diseños nuevos, usa HMAC-SHA256.
¿Por qué mi HMAC no coincide con la firma del webhook?
Las causas habituales son calcular el HMAC del JSON analizado y vuelto a serializar en lugar del cuerpo sin procesar, un salto de línea al final del mensaje, un secreto o un formato de clave incorrectos (un secreto en Base64 o hexadecimal introducido como Texto, o al revés) y un algoritmo equivocado. Que la firma esté en hexadecimal o en Base64 no es un problema: Comparar con acepta hexadecimal en mayúsculas o minúsculas, Base64 estándar y Base64 seguro para URL.
¿Qué diferencia hay entre un HMAC y un hash simple?
¿Un HMAC es lo mismo que una firma digital?
No. Las dos partes de un HMAC comparten un mismo secreto, así que cualquiera que pueda verificar una etiqueta también puede crearla. Las firmas como RSA o ECDSA (RS256 o ES256 en los JWT) se crean con una clave privada y se comprueban con una clave pública, de modo que quienes las verifican no pueden falsificarlas.
¿Se envía mi clave secreta a un servidor?
No. HMAC-SHA1 y las variantes SHA-2 se calculan con la Web Crypto API de tu navegador, y HMAC-MD5, con JavaScript en la propia página. La clave y el mensaje nunca salen de tu dispositivo. Aun así, trata con cuidado los secretos de producción y rota una clave si ha podido quedar expuesta.