Dandelion Labs - Ship Fast. Scale Smarter.Dandelion Labs - Ship Fast. Scale Smarter.
  • Servicios
  • Proyectos
  • Carreras
  1. Inicio
  2. /
  3. Blog
  4. /
  5. Una firma válida de webhook no detiene una repetición
Una firma válida de webhook no detiene una repetición
Security

Una firma válida de webhook no detiene una repetición

Seguridad

2026-10-04·7 min de lectura·Dandelion Labs

En esta página

  • Una firma válida demuestra el origen, no la unicidad
  • Construye el límite de aceptación en el orden correcto
  • Haz atómico el rechazo de duplicados
  • Separa la entrada de los efectos de negocio
  • Prueba reintentos, reenvíos y desorden
  • Qué no cambia

Una firma válida demuestra el origen, no la unicidad

Un webhook puede ser auténtico y aun así no ser seguro procesarlo dos veces. La documentación de webhooks de Stripe explica que su firma cubre una marca de tiempo y el cuerpo sin modificar de la solicitud, y que sus bibliotecas oficiales rechazan marcas fuera de una tolerancia predeterminada de cinco minutos. GitHub recomienda por separado usar el encabezado X-GitHub-Delivery para asegurar que cada entrega se procese una sola vez.

Esos controles responden preguntas distintas. El HMAC responde si alguien que conoce el secreto compartido firmó exactamente esos bytes. La marca de tiempo responde si la firma es suficientemente reciente. Un identificador duradero de entrega o evento responde si tu sistema ya aceptó el trabajo.

Según comprobamos el 4 de octubre de 2026, Stripe puede reintentar automáticamente una entrega real durante un máximo de tres días. Un reenvío manual está disponible en el Dashboard durante 15 días y mediante la CLI durante 30 días. Cada reintento de Stripe recibe una marca de tiempo y una firma nuevas, pero conserva la identidad del evento. GitHub hace lo contrario con su identificador de entrega: un reenvío solicitado conserva el mismo valor X-GitHub-Delivery.

Por eso "la firma pasó" no puede ser la última línea de un handler de webhooks. Una solicitud copiada puede repetirse dentro de la ventana permitida. Un reintento del proveedor puede llegar con una firma nueva y válida. Cualquiera de los dos puede causar un segundo envío, correo, crédito, permiso o despliegue si el efecto de negocio no tiene un límite de unicidad.

Construye el límite de aceptación en el orden correcto

Un handler seguro debería tomar cinco decisiones antes de empezar el trabajo costoso.

Primero, lee el cuerpo sin modificar de la solicitud. Los esquemas de firma suelen cubrir los bytes enviados por la red. Analizar JSON y serializarlo otra vez puede cambiar espacios, orden de claves o caracteres escapados, lo que rompe la verificación aunque los datos parezcan idénticos.

Segundo, verifica la firma con el secreto del endpoint y una comparación de tiempo constante. Rechaza versiones desconocidas. Si el proveedor firma una marca de tiempo, vincúlala al cuerpo y aplica una tolerancia que tu reloj pueda sostener. El valor predeterminado de Stripe es cinco minutos, no cero. Su documentación advierte que una tolerancia de cero desactiva la comprobación de vigencia.

Tercero, valida el proveedor, el tipo de evento y la acción antes de enviar el trabajo. Un evento firmado correctamente no es relevante de forma automática para cada cuenta o transición de estado. Comprueba también el tenant o la instalación esperada, además del nombre del evento.

Cuarto, reclama una clave estable del evento en almacenamiento duradero. Prefiere el identificador de evento o entrega del proveedor. Limítalo por proveedor y endpoint o tenant para que dos integraciones no puedan colisionar. No derives esta clave de la marca de tiempo, porque los reintentos legítimos cambian la marca y eventos distintos pueden compartir una.

Quinto, confirma la recepción con rapidez y mueve el trabajo de negocio a una cola. GitHub pide a los receptores devolver una respuesta 2xx en 10 segundos. Stripe también recomienda responder con éxito antes de ejecutar lógica compleja. Una transacción de entrada corta reduce reintentos causados por timeouts y permite que los workers tengan su propia política de reintentos.

Haz atómico el rechazo de duplicados

Una caché en memoria ayuda a reducir carga, pero no es el límite de seguridad. Dos instancias de la aplicación pueden recibir la misma entrega al mismo tiempo. Un reinicio puede vaciar la caché. Una comprobación seguida de una inserción separada también deja una carrera entre "no visto" y "registrado ahora".

Usa una restricción de unicidad en la base de datos. La transacción de entrada debería intentar insertar una fila como (provider, endpoint_id, event_id). Si la inserción entra en conflicto, devuelve la misma confirmación exitosa que devolverías tras aceptar la primera copia. No envíes un error por un duplicado conocido, porque un error pide al proveedor que lo intente otra vez.

Guarda evidencia suficiente para investigar sin convertir la tabla en un archivo de secretos: el identificador, tipo, hora de recepción, digest del payload, resultado de verificación y estado de procesamiento. Cifra u omite campos sensibles. El payload sin modificar puede contener datos personales o de facturación, así que conservar cada cuerpo para siempre no es una función de auditoría gratuita.

La fila del evento y el trabajo en cola también deben fallar juntos. Un transactional outbox es un patrón común: inserta el evento único y un trabajo de outbox en la misma transacción de base de datos, y deja que otro proceso mueva el trabajo a la cola. Sin ese vínculo, un fallo puede registrar el evento como consumido antes de que exista el trabajo, y el siguiente reintento legítimo será descartado como duplicado.

Separa la entrada de los efectos de negocio

La deduplicación de eventos protege la ruta de entrada. El worker todavía necesita lógica de negocio idempotente.

Usa una clave de negocio para el efecto, no solo la clave de entrega. Por ejemplo, un evento de pago podría crear un registro de envío único por order_id y tipo de efecto. Un evento de acceso podría actualizar el estado deseado de una membresía en lugar de añadir otro permiso. Un trabajo de correo podría ser único por plantilla, destinatario y objeto de negocio que lo activó.

Este segundo límite cubre casos en los que los proveedores emiten dos objetos de evento distintos para el mismo cambio subyacente. Stripe indica de forma explícita que esto puede ocurrir y recomienda combinar el identificador del objeto con el tipo de evento para identificar duplicados semánticos.

Diseña los handlers como transiciones de estado cuando sea posible. "Marca la factura 123 como pagada si su pago verificado terminó" es más seguro que "añade una acción de pago cada vez que llega este mensaje". Si la API del proveedor es la fuente autorizada, recupera el estado actual del objeto antes de una acción de alto impacto. El webhook puede ser la señal para reconciliar, no la única evidencia del estado final.

Prueba reintentos, reenvíos y desorden

La ruta feliz demuestra poco. Prueba las rutas que los proveedores confiables crean a propósito.

Envía la misma solicitud firmada dos veces dentro de la tolerancia temporal y confirma que solo existe una fila de evento y un efecto de negocio. Entrega dos copias al mismo tiempo a instancias distintas. Fuerza al worker a fallar después de reclamar el evento y confirma que el outbox reintenta el trabajo sin repetir el efecto.

Solicita un reenvío oficial y confirma que el identificador específico del proveedor se comporta como está documentado. En GitHub, el reenvío conserva el encabezado de entrega. En Stripe, las firmas y marcas de tiempo cambian en cada reintento, mientras el identificador del evento sigue siendo la clave de deduplicación.

Después invierte dos eventos relacionados. Stripe afirma que no garantiza el orden de los eventos y advierte contra el uso de su marca created, con resolución de un segundo, para decidir el orden o detectar duplicados. Un handler que solo funciona cuando created, updated y paid llegan en secuencia es un incidente aplazado.

Por último, rota el secreto de firma con un periodo de solapamiento. Acepta los secretos anterior y nuevo solo durante la ventana planificada, registra cuál verificó la solicitud y retira el anterior en la fecha prevista. Así pruebas la ruta operativa que, de otro modo, los equipos descubren durante una emergencia.

Qué no cambia

La protección contra repeticiones no repara un secreto de firma filtrado. Un atacante que tenga el secreto activo puede crear un payload, una marca de tiempo y una firma nuevos. El almacenamiento seguro, el alcance limitado del endpoint, la rotación y la revocación durante incidentes siguen importando.

Tampoco autoriza la acción de negocio solicitada. Un evento firmado puede ser válido pero inesperado para el tenant, la cuenta o el estado actual del objeto. Trata la verificación de firma como autenticación del mensaje y aplica después las reglas normales de autorización y estado.

El contrato útil es limitado y comprobable: acepta solo bytes auténticos y recientes, reclama de forma atómica cada identidad estable de evento, pon el trabajo en cola sin perderlo y hace único el efecto de negocio. Una firma de webhook es una parte de ese contrato. Por sí sola, no es el límite contra repeticiones.

Escrito por Dandelion Labs

Idioma

ENES

Buscar

Categorías

  • Todos los artículos
  • Security3
  • Software Market2
  • AI3
  • UI/UX2
  • Engineering1
  • Seguridad3
  • Codigo Abierto2
  • Empresa1
  • Ingeniería1

Compartir

Artículos relacionados

    Mantente al día

    Recibe los últimos insights sobre desarrollo con IA, consejos para startups y guías técnicas en tu bandeja de entrada. Sin spam, solo contenido de calidad.

    Únete a más de 200 fundadores y desarrolladores. Cancela cuando quieras.

    AI Insights
    Startup Tips
    Guías Técnicas
    Casos de Estudio
    Dandelion Labs - Ship Fast. Scale Smarter.Dandelion Labs - Ship Fast. Scale Smarter.

    Ayudamos a startups en etapa temprana a pasar de la idea a un producto construido para escalar.

    Empresa
    • Sobre Nosotros
    • Servicios
    • Carreras
    • Blog
    • QuantaKrypto (PQC)
    Contáctanos
    • [email protected]
    • Contáctanos

    Copyright © 2021-2026 | Dandelion Labs JSC

    Política de PrivacidadTérminos y Condiciones