Verificar la firma HMAC del webhook: cómo hacerlo bien

Cualquiera que conozca — o adivine — la URL de tu endpoint de webhook puede hacer un POST con un cuerpo JSON que parece exactamente un evento real: misma forma, mismos nombres de campo, completamente falso. La firma cierra ese hueco: prueba de que un payload vino de quien tiene el secreto compartido, y de que nada en él cambió por el camino. Stripe, GitHub y la mayoría de proveedores serios firman con alguna variante del mismo esquema HMAC, así que la mecánica de aquí es casi universal — el ejemplo concreto es el esquema propio, real y actual de Olmira, nombres de cabecera incluidos.

Qué demuestra realmente HMAC

HMAC (Hash-based Message Authentication Code) parte de un secreto que ambas partes conocen y nadie más. El emisor calcula el hash del payload junto con ese secreto y adjunta el resultado; tú, con el mismo secreto, lo recalculas y lo comparas. Una coincidencia demuestra dos cosas a la vez: que el mensaje vino realmente de alguien con el secreto — por mucho que un atacante conozca el payload, no puede falsificar el hash — y que no se alteró después de firmarlo, porque cambiar aunque sea un carácter produce un hash completamente distinto.

El esquema exacto que usa Olmira

Cada entrega de webhook saliente lleva tres cabeceras: X-Aether-Signature, X-Aether-Webhook-Id y X-Aether-Event. (El prefijo de la cabecera pone Aether y no Olmira: es un resto del nombre en clave interno de la plataforma; no afecta en nada al funcionamiento del esquema, solo a cómo se llama literalmente la cabecera.)

Verificar una entrega, en términos llanos

Toma el cuerpo bruto de la solicitud — los bytes exactos tal como se entregaron, antes de cualquier parseo JSON — antepónle la marca de tiempo de la cabecera de firma y un punto, calcula el HMAC de esa cadena con el secreto de tu endpoint, y compara el resultado con la firma entregada usando una comparación de tiempo constante. El módulo crypto integrado de Node cubre todo esto sin una sola dependencia; las comprobaciones previas de más abajo son donde las implementaciones correctas realmente se diferencian de las rotas.

Los errores que rompen la verificación

Casi todos los avisos de «mi firma nunca coincide» se reducen a un número pequeño de causas. Calcular el hash de un cuerpo que se ha vuelto a parsear o a serializar en lugar de los bytes crudos exactos es, con diferencia, la más habitual: los espacios en blanco, el orden de las claves o el formato de los números pueden cambiar en silencio cuando un cuerpo pasa por un parser de JSON y vuelve a convertirse en texto, y cualquier cambio, por mínimo que sea, produce un hash distinto. Olvidar que la marca de tiempo forma parte de lo que se firma —calcular el hash solo del cuerpo, sin el prefijo .— es la segunda más habitual, y es fácil caer en ella porque el cuerpo es lo más «evidente» que hashear.

Antes de fiarte de ello en producción

1

Comprueba primero la ventana de tiempo

Rechaza cualquier cosa fuera de una tolerancia pequeña (Olmira publica 300 segundos, cinco minutos, como su propia cifra de referencia) antes de gastar tiempo de CPU en calcular hashes: una firma caducada no merece la comparación.

2

Compara en tiempo constante

Usa la primitiva de comparación en tiempo constante de tu lenguaje, no una comprobación de igualdad normal, para que una discordancia nunca filtre información temporal sobre lo cerca que estaba un intento.

3

Confirma que calculas el hash del cuerpo en bruto

Registra al menos una vez durante la configuración la cadena exacta que tu código va a pasar por el hash y compárala, byte a byte, con lo que envió realmente una entrega de prueba.

4

Prevé la rotación de la clave secreta

Guardes donde guardes el secreto, haz que cambiarlo sea una modificación de configuración de una sola línea: rotarlo en el panel sin actualizarlo en ese mismo momento hace que todas las entregas empiecen a fallar hasta que te pongas al día.

Cómo lo resuelve Olmira

Cada uno de los 22 eventos de webhook de Olmira —pedidos, reservas, pagos, facturas, tickets de soporte y más— se firma exactamente igual que se describe arriba, sin excepciones y sin tipos de evento sin firmar. Cada endpoint que registras recibe su propio secreto independiente, así que una fuga en una integración nunca compromete a otra. Antes de pasar a producción, la acción «Enviar prueba» en los ajustes del endpoint dispara un envío de muestra real y completamente firmado por exactamente la misma ruta de código que un evento real —con las mismas cabeceras y la misma forma de sobre—, de modo que puedes validar tu verificador contra el esquema real sin esperar a que un pedido o una reserva de verdad lo dispare. La plataforma completa de conectores dentro de la que vive esto —importación CSV, extracciones programadas y envíos entrantes junto a los webhooks salientes— es Integraciones.

Construye la integración una vez y confía en ella

Prueba gratuita de 30 días. Registra un endpoint y envíate una entrega de prueba real y firmada en minutos.