Correo: DKIM falla — por qué se rompe la firma (y cómo arreglarlo rápido)

¿Te fue útil?

Enviaste el correo. Los clientes no lo recibieron. O peor: llegó a spam con un cortés pero letal “DKIM=fail” estampado en la frente. El equipo de marketing piensa que “la propagación DNS” es un patrón meteorológico y pregunta si puedes “simplemente reenviarlo”.

Aquí es donde dejas de adivinar. DKIM no es errático. Es estricto. La firma se rompe porque algo cambió entre la firma y la verificación, o porque los verificadores no pueden obtener la clave, o porque firmaste lo incorrecto desde el inicio. La buena noticia: normalmente se arregla en menos de una hora si sigues un triage disciplinado.

DKIM en términos simples (qué se firma realmente)

DKIM (DomainKeys Identified Mail) no es “cifrado”, y no es “prueba de que el remitente es confiable”. Es una suma de verificación criptográfica sobre cabeceras seleccionadas y (normalmente) el cuerpo del mensaje, firmada con una clave privada que posee el dominio remitente. El receptor obtiene la clave pública desde DNS y verifica la firma. Si los bytes firmados no coinciden con lo que llega, la verificación falla. Si no se puede recuperar la clave pública, la verificación falla. Si el receptor no puede parsear la firma, la verificación falla.

En un encabezado de firma DKIM (DKIM-Signature:), verás campos como:

  • d= el dominio que firma (el dominio “responsable” para DKIM)
  • s= el selector (usado para encontrar la clave en DNS)
  • h= qué cabeceras están firmadas
  • bh= hash del cuerpo (base64 del hash canónico del cuerpo)
  • b= la firma real sobre cabeceras + hash del cuerpo
  • c= canonicalización (cómo se normalizan espacios/plegamiento de líneas)
  • t=/x= marca temporal / expiración

Aquí está el punto operativo clave: DKIM valida una representación específica del mensaje. Si cualquier relé, pasarela, inyectador de pies de página, gestor de listas, “aparato de seguridad” o servicio en la nube modifica las cabeceras firmadas o el cuerpo de forma no tolerada por la canonicalización, la firma se rompe. Eso no es un error de DKIM. Es DKIM haciendo su trabajo.

Dos modos de canonicalización, infierno infinito

La canonicalización controla cómo el firmante y el verificador normalizan el contenido antes de hashearlo y verificarlo. Verás:

  • simple: casi sin normalización. Frágil. Ideal para quienes disfrutan de páginas de on-call.
  • relaxed: tolera ciertos cambios de espacios y formato de cabeceras. Normalmente la elección correcta.

Los valores por defecto sensatos son c=relaxed/relaxed (cabeceras/cuerpo relajados) o relaxed/simple en algunos sistemas. Si usas simple/simple en una ruta de correo compleja, básicamente estás firmando un castillo de arena y enviándolo por un túnel de lavado de autos.

La promesa real de DKIM

DKIM responde: “¿Firmó este mensaje un dominio que controla esta clave privada DKIM, y llegaron sin cambios las partes firmadas?” No responde: “¿Es auténtica la dirección From?” Eso es cosa de DMARC, que usa SPF y DKIM más reglas de alineación. DKIM es un bloque constructivo en un equipo de porteros más amplio.

Datos interesantes y breve historia (porque el correo es más viejo que tu pipeline CI)

  • Dato 1: DKIM se estandarizó en 2007 (RFC 4871), fusionando ideas de DomainKeys de Yahoo y Identified Internet Mail de Cisco.
  • Dato 2: Las firmas DKIM se evalúan tras cambios de transporte; incluso una “inocua” inyección de pie de página puede invalidar el hash del cuerpo.
  • Dato 3: En despliegues tempranos se usaron ampliamente claves RSA de 1024 bits; muchos receptores ahora prefieren o requieren 2048 bits para mayor seguridad.
  • Dato 4: Los límites de tamaño de registros TXT en DNS y la fragmentación han causado históricamente fallos intermitentes en la obtención de la clave, especialmente con claves sobredimensionadas o DNS mal configurados.
  • Dato 5: DKIM solo puede firmar un subconjunto de cabeceras; lo que eliges firmar es un balance entre integridad y sobrevivencia a través de relés.
  • Dato 6: Las listas de correo son famosas por romper DKIM: modifican Subject, añaden cabeceras de lista y apéndices—exactamente lo que DKIM tiende a cubrir.
  • Dato 7: ARC (Authenticated Received Chain) se introdujo después para preservar resultados de autenticación a través de reenviadores y listas, porque DKIM por sí solo a menudo se rompe aguas abajo.
  • Dato 8: Algunos MTA reescribían finales de línea o la dot-stuffing de forma distinta; la canonicalización existe en parte para sobrevivir a ese desastre.
  • Dato 9: Pueden coexistir múltiples firmas DKIM; los sistemas receptores suelen aceptar el mensaje si cualquier firma válida cumple las políticas necesarias (especialmente para la alineación DMARC).

Cómo falla DKIM: los modos de fallo que importan

1) El DNS no puede servir la clave (o sirve la equivocada)

Los receptores verifican DKIM consultando un registro DNS en:

<selector>._domainkey.<domain>

Si ese registro falta, está mal formado, fragmentado incorrectamente, es demasiado grande para tu camino DNS, o está en caché en un estado obsoleto durante una rotación, obtienes fallos DKIM que parecen aleatorios—porque lo son entre distintos resolvers.

2) El mensaje cambió tras la firma

Este es el clásico: se firma el mensaje y luego un relé lo modifica. Culpables comunes:

  • Añadir un pie de página de exención de responsabilidad (legal, RRHH o banners de “confidencialidad”)
  • Reescribir líneas Subject (marcar como “EXTERNO”)
  • Modificar boundaries MIME o re-encodificar contenido
  • Normalizar espacios de maneras no cubiertas por la canonicalización
  • Escáneres de virus o pasarelas DLP que reensamblan adjuntos

3) Firmaste las cabeceras equivocadas (o demasiadas)

Firmar cabeceras inestables es una lesión autoinfligida. Cabeceras como Received cambian en cada salto, así que firmarlas es inútil a menos que te guste fallar. Algunos sistemas también añaden o reescriben Message-ID, Date, o incluso From de formas que no esperabas. Elige cabeceras que representen identidad y contenido pero que permanezcan estables después de que tu firmador opere.

4) Tu dominio de firma no está alineado con DMARC

Puedes tener una firma DKIM aprobada y aun así fallar DMARC si el dominio d= no se alinea con el dominio visible en From: según la política DMARC (alineación estricta o relajada). Eso a menudo aparece como “DKIM=pass” pero “DMARC=fail”, que las partes de negocio interpretan como “el correo está roto”. Con razón.

5) Tiempo, expiración y defensas contra replay

Algunas firmas DKIM incluyen x= (expiración). Si tus relojes se desincronizan o tus mensajes quedan mucho tiempo en cola (hola, backpressure), las firmas pueden expirar antes de la entrega. Es menos común pero extremadamente molesto porque todo “parece correcto” salvo el tiempo.

6) Rotación de selectores/clave hecha como hobby de fin de semana

Rotar claves DKIM es normal. Rotarlas sin solapamiento es como crear un incidente de dos días. Cachés DNS, MTAs multi-región y colas largas significan que selectores antiguos pueden seguir usándose por un tiempo. Quitar el registro demasiado pronto y la verificación falla para el correo retrasado.

Broma corta #1: DKIM es como un sello a prueba de manipulaciones. Si sigues abriendo el frasco para “mejorarlo”, no te sorprendas cuando el sello grite.

Guía de diagnóstico rápido (primero/segundo/tercero)

Esta es la secuencia que encuentra el cuello de botella más rápido en entornos reales. No empieces editando configuraciones. Empieza mirando lo que vieron los receptores.

Primero: confirma qué falló (DNS vs hash del cuerpo vs parseo de cabecera)

  1. Obtén un mensaje real fallido (con las cabeceras originales tal como lo recibió). No una captura de pantalla. No un reenvío. La fuente cruda.
  2. Lee la cabecera Authentication-Results del receptor. A menudo te dice exactamente qué se rompió: “no key”, “body hash mismatch”, “bad signature”, “invalid header format”.
  3. Verifica los campos de DKIM-Signature: d=, s=, c=, h=, bh=, x=.

Segundo: verifica DNS desde múltiples ubicaciones

  1. Consulta el registro del selector usando un resolver conocido.
  2. Consulta usando tu resolver del sistema (para detectar split-horizon o rarezas de DNS corporativo).
  3. Verifica que el registro sea sintácticamente válido y completo (sin comillas rotas, fragmentos faltantes).

Tercero: aisla dónde se modifica el mensaje

  1. Compara el mensaje en el momento de la firma vs en la entrega (si puedes capturar ambos).
  2. Busca sistemas intermedios que reescriban contenido: pasarelas salientes, seguridad de correo, plataformas CRM, gestores de listas.
  3. Envía una prueba controlada: mismo remitente, mismo destinatario, pero omitiendo componentes opcionales (si es posible) para encontrar el salto que modifica.

Cuarto: arregla lo más pequeño que restaure el pass

Resiste la tentación de “re-arquitectar el correo”. Primero restaura la entregabilidad, luego fortalece la canalización. En la práctica eso suele significar:

  • Arreglar el registro clave DNS o el uso del selector
  • Cambiar la canonicalización a relaxed
  • Parar la modificación del cuerpo después de la firma (mover la firma al último salto)
  • Ajustar qué cabeceras se firman
  • Alinear d= con el dominio From para DMARC

Tareas prácticas: comandos, salidas y decisiones (12+)

Estas son tareas reales que puedes ejecutar en un host de correo o en una caja de investigación. Cada una incluye: comando, qué significa la salida y qué decisión tomar. Asumo un entorno Linux con herramientas típicas. Ajusta rutas para tu distro.

Tarea 1: Extraer Authentication-Results y DKIM-Signature de un mensaje guardado

cr0x@server:~$ awk 'BEGIN{RS="";FS="\n"} {for(i=1;i<=NF;i++) if($i ~ /^Authentication-Results:/ || $i ~ /^DKIM-Signature:/) print $i}' failed.eml
Authentication-Results: mx.example.net; dkim=fail (body hash did not verify) header.d=example.com header.s=s2025 header.b=Qn...
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=s2025; h=from:to:subject:date:mime-version:content-type; bh=3l8...; b=Qn...

Significado: Ya tienes la clase de fallo: body hash mismatch. Esto no es “DNS faltante”, ni “clave mala”, ni “firma expirada”. Algo cambió en el cuerpo después de la firma.

Decisión: Deja de mirar DNS. Ve a buscar qué modifica el cuerpo (pies de página, re-encodificación, reescritura por pasarela), o mueve la firma hacia el final de la cadena.

Tarea 2: Comprobar que exista el registro público DKIM (dig)

cr0x@server:~$ dig +short TXT s2025._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtY..."

Significado: Existe un registro TXT que devuelve una carga con apariencia DKIM.

Decisión: Si DKIM sigue fallando con “no key”, sospecha visibilidad DNS (split-horizon), fallos DNSSEC o fragmentación/comillas rotas del registro en algunos resolvers.

Tarea 3: Comprobar el mismo registro DKIM mediante un resolver específico (capturar split-horizon)

cr0x@server:~$ dig @1.1.1.1 +short TXT s2025._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtY..."

Significado: Los resolvers públicos coinciden. Si tu resolver interno devuelve otra cosa, tienes DNS dividido o cachés obsoletos.

Decisión: Si los resultados difieren, arregla el DNS autoritativo primero. Los receptores de correo no usan tu vista interna de la realidad.

Tarea 4: Detectar registros TXT DKIM multipart malformados

cr0x@server:~$ dig TXT s2025._domainkey.example.com +noall +answer
s2025._domainkey.example.com. 300 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." "G9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtY..."

Significado: Algunos proveedores DNS dividen registros TXT largos en múltiples cadenas. Eso está bien si se hace correctamente; los resolvers las concatenan. Pero errores de comillas pueden romper silenciosamente las claves.

Decisión: Si ves puntuación extraña, comillas faltantes o segmentos truncados, regenera y vuelve a publicar el registro. No edites a mano base64 en un navegador a las 2 a.m.

Tarea 5: Verificar que OpenDKIM vea la clave y el mapeo del selector

cr0x@server:~$ sudo opendkim-testkey -d example.com -s s2025 -vvv
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: checking key 's2025._domainkey.example.com'
opendkim-testkey: key OK

Significado: El registro de clave DNS es alcanzable y coincide con lo que OpenDKIM espera.

Decisión: Si esto falla, arregla DNS/selector primero. Si pasa pero los receptores fallan, sospecha flujo de correo o modificación del mensaje.

Tarea 6: Confirmar que el firmador realmente está firmando (logs de mail)

cr0x@server:~$ sudo grep -E "opendkim|DKIM-Signature" /var/log/mail.log | tail -n 5
Jan 03 10:14:22 mailgw opendkim[1423]: 6F2C12A0B1: DKIM-Signature field added (s=s2025, d=example.com)
Jan 03 10:14:23 mailgw postfix/qmgr[1111]: 6F2C12A0B1: from=, size=48213, nrcpt=1 (queue active)

Significado: OpenDKIM inyectó una firma para este ID de mensaje.

Decisión: Si no ves firma, revisa el cableado del milter, permisos de socket o una ruta de bypass que salta el milter.

Tarea 7: Confirmar que Postfix está conectado al milter DKIM

cr0x@server:~$ sudo postconf | egrep 'smtpd_milters|non_smtpd_milters|milter_default_action|milter_protocol'
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
milter_default_action = accept
milter_protocol = 6

Significado: Tanto el correo recibido por SMTP como el generado localmente pasan por el milter DKIM. Si solo smtpd_milters está configurado, el correo inyectado por procesos locales puede no firmarse.

Decisión: Si non_smtpd_milters está vacío y generas correo localmente (cron, apps), añádelo. El correo sin firmar se trata frecuentemente con sospecha una vez que DMARC es estricto.

Tarea 8: Validar que el cuerpo del mensaje no fue reescrito por una pasarela (comparar hashes con herramientas OpenDKIM)

cr0x@server:~$ sudo opendkim-testmsg -v < failed.eml
opendkim-testmsg: dkim_eom(): verification failed: body hash did not verify

Significado: El mensaje almacenado (tal como fue recibido) no coincide con el hash del cuerpo firmado.

Decisión: Identifica el salto que modificó el cuerpo. Si controlas una pasarela downstream que inyecta pies de página, mueve la firma DKIM después de ella o evita que toque el correo firmado.

Tarea 9: Encontrar qué cabeceras firmaste (y si firmaste cabeceras inestables)

cr0x@server:~$ grep -i '^DKIM-Signature:' -m 1 failed.eml | sed 's/; /\n/g' | egrep '^(h=|c=|d=|s=)'
c=relaxed/relaxed
d=example.com
s=s2025
h=from:to:subject:date:mime-version:content-type

Significado: Firmaste un conjunto de cabeceras bastante estándar. Bien. Si ves h=...:received:... u otras cabeceras que mutan por salto, te has auto-saboteado.

Decisión: Si se firmaron cabeceras inestables, ajusta la configuración del firmador para firmar un conjunto estable.

Tarea 10: Detectar transformaciones de finales de línea o MIME (identificar el clásico “reenvuelto por pasarela”)

cr0x@server:~$ python3 - <<'PY'
import sys, email
from email import policy
msg = email.message_from_binary_file(open("failed.eml","rb"), policy=policy.default)
print("Content-Type:", msg.get_content_type())
print("Has multipart:", msg.is_multipart())
print("Transfer-Encoding:", msg.get("Content-Transfer-Encoding"))
PY
Content-Type: multipart/alternative
Has multipart: True
Transfer-Encoding: None

Significado: El correo multipart es más propenso a ser “re-escrito” por productos de seguridad. No siempre, pero es una escena frecuente del crimen.

Decisión: Si tienes una pasarela DLP/AV, comprueba si re-encodifica partes o normaliza boundaries MIME. Si es así, firma después de ese paso.

Tarea 11: Verificar pistas de alineación DMARC (parseo de Authentication-Results)

cr0x@server:~$ grep -i '^Authentication-Results:' -m 1 failed.eml
Authentication-Results: mx.example.net; spf=pass smtp.mailfrom=bounces.vendor-mail.net; dkim=pass header.d=vendor-mail.net; dmarc=fail header.from=example.com

Significado: SPF pasó para el dominio del proveedor, DKIM pasó para el dominio del proveedor, pero DMARC falló porque el From visible es example.com y ni SPF ni DKIM se alinean con ese dominio.

Decisión: Obliga al proveedor a firmar con d=example.com (tu dominio) y alinea SPF mediante un dominio de rebote personalizado, o acepta que DMARC fallará y la entregabilidad sufrirá bajo políticas estrictas.

Tarea 12: Buscar múltiples firmas DKIM y decidir cuál importa

cr0x@server:~$ grep -i '^DKIM-Signature:' failed.eml | wc -l
2

Significado: Existen dos firmas. A menudo una la añade tu sistema y otra un servicio upstream. Los receptores pueden validar una y ignorar la otra según la alineación y la política.

Decisión: Asegúrate de que al menos una firma aprobada se alinee con el dominio From para DMARC. Si la alineada falla, todavía tienes un problema incluso si otra pasa.

Tarea 13: Confirmar que el selector en el correo saliente coincida con tu registro publicado

cr0x@server:~$ grep -i '^DKIM-Signature:' -m 1 failed.eml | tr ';' '\n' | grep -E '^\s*s='
 s=s2025

Significado: El mensaje usó el selector s2025. Si DNS solo tiene s2024, has encontrado la discordancia.

Decisión: Publica el registro s2025 inmediatamente (y mantenlo), o vuelve la configuración del firmador al selector publicado. No “esperes la propagación” si el registro simplemente no existe.

Tarea 14: Comprobar tablas KeyTable/SigningTable de OpenDKIM (ruta común equivocada)

cr0x@server:~$ sudo egrep -v '^\s*(#|$)' /etc/opendkim/SigningTable /etc/opendkim/KeyTable
/etc/opendkim/SigningTable:*@example.com s2025._domainkey.example.com
/etc/opendkim/KeyTable:s2025._domainkey.example.com example.com:s2025:/etc/opendkim/keys/example.com/s2025.private

Significado: SigningTable mapea remitentes a un nombre de clave; KeyTable mapea ese nombre a dominio/selector/ruta de clave privada.

Decisión: Si el correo de noreply@sub.example.com no está cubierto por tu wildcard, puede que no estés firmando sin querer. Arregla los patrones de la tabla para que coincidan con las direcciones From/remitente reales.

Tarea 15: Validar que tu archivo de clave privada sea legible por el servicio firmador

cr0x@server:~$ sudo -u opendkim test -r /etc/opendkim/keys/example.com/s2025.private && echo "readable"
readable

Significado: El usuario OpenDKIM puede leer la clave. Si no, OpenDKIM puede fallar silenciosamente al firmar o registrar errores confusos.

Decisión: Arregla la propiedad/permisos. Si lo “solucionaste” haciéndola legible por todos, revierte eso y hazlo correctamente.

Tarea 16: Comprobar retrasos en la cola que pueden disparar la expiración de firmas

cr0x@server:~$ mailq | head -n 20
-Queue ID-  --Size-- ----Arrival Time---- -Sender/Recipient-------
6F2C12A0B1     48213 Fri Jan  3 10:14:23  noreply@example.com
                                         user@recipient.net

-- 48 Kbytes in 1 Request.

Significado: Si ves horas/días de backlog y tus firmas expiran (x=), la verificación puede fallar a pesar de una configuración correcta.

Decisión: Reduce el tiempo en cola, evita expiraciones DKIM salvo que tengas una razón fuerte, y arregla el cuello de botella de entrega (límites de tasa, problemas DNS, IPs bloqueadas).

Errores comunes: síntoma → causa raíz → solución

Esta sección es deliberadamente directa. La mayoría de los problemas DKIM son aburridos. El truco es reconocer la forma rápidamente.

1) “dkim=fail (no key for signature)”

  • Síntoma: Authentication-Results dice no key, o “key not found”.
  • Causa raíz: Registro TXT del selector faltante, selector (s=) equivocado en el correo saliente, DNS dividido, o formato TXT roto.
  • Solución: Publica correctamente <s>._domainkey.<d> como TXT; confirma con dig contra resolvers públicos; asegúrate de que el firmador usa ese selector.

2) “dkim=fail (body hash did not verify)”

  • Síntoma: DKIM falla; a menudo intermitente según el receptor o la ruta.
  • Causa raíz: El cuerpo del mensaje fue modificado después de la firma: disclaimer, pie de página, reescritura por filtro de contenido, re-encodificación, cambios de ajuste de líneas no tolerados.
  • Solución: Mueve la firma al último sistema antes de Internet; desactiva modificaciones posteriores a la firma; usa canonicalización relaxed; evita añadir contenido tras la firma.

3) “dkim=fail (signature did not verify)” con la clave presente

  • Síntoma: La clave existe, pero la firma falla aunque el cuerpo parezca intacto.
  • Causa raíz: Clave publicada equivocada para el selector (mismatch de rotación), clave privada corrupta, firma con dominio/selector distinto al esperado, o mismatch de canonicalización en algunas implementaciones.
  • Solución: Valida que el firmador use el mismo selector/clave que DNS; solapa selectores viejos/nuevos durante la rotación; regenera claves si sospechas corrupción.

4) DKIM pasa, DMARC falla

  • Síntoma: dkim=pass pero dmarc=fail.
  • Causa raíz: DKIM firmó con d= que no se alinea con el dominio visible en From, o SPF pasó para un dominio distinto.
  • Solución: Asegura que al menos una firma DKIM se alinee con el From-domain; configura a los proveedores para firmar con dominio personalizado; valida el modo de alineación DMARC (relaxed vs strict).

5) DKIM falla solo para listas de correo / reenvíos

  • Síntoma: El correo directo pasa; el reenviado/lista falla.
  • Causa raíz: Las listas añaden pies de página/etiquetas en el subject; los reenviadores envuelven contenido; el reescrito (o no) del From interactúa con DMARC. DKIM se rompe en tránsito.
  • Solución: Prefiere ARC en reenviadores/listas; minimiza modificaciones; considera un comportamiento amigable con DMARC en la lista; firma en el último salto que controles.

6) DKIM falla solo para correos grandes o ciertos destinatarios

  • Síntoma: Newsletters grandes fallan, alertas pequeñas pasan.
  • Causa raíz: Transformaciones MIME, re-encodificación por pasarela, o problemas DNS en resolvers que no manejan bien TXT grandes/fragmentación; a veces un problema MTU/EDNS en la ruta.
  • Solución: Mantén registros DKIM limpios; prueba DNS mediante múltiples resolvers; evita componentes que reenvuelvan HTML; valida que tu ESP no “optimice” el marcado tras firmar.

7) DKIM falla después de un cambio “inofensivo” en la infraestructura

  • Síntoma: De repente DKIM falla tras migrar a una nueva pasarela, añadir DLP, activar etiquetado “externo” o habilitar un conector en la nube.
  • Causa raíz: Cambiaste los bytes. DKIM lo notó.
  • Solución: Mueve la firma DKIM después del componente que modifica, o configura el componente para no modificar correo saliente tras la firma.

Tres micro-historias corporativas desde el campo

Micro-historia 1: El incidente causado por una suposición equivocada

La empresa A tenía una pila saliente ordenada: servidores de aplicaciones enviaban correo a Postfix, Postfix lo pasaba a un relé en la nube, y el relé entregaba al mundo. Tenían DKIM “habilitado” y una política DMARC que no era agresiva. La vida iba bien, que es como empiezan los peligros.

Un equipo de seguridad añadió un nuevo banner saliente: anteponer “[EXTERNAL]” al asunto de cualquier correo que salga de la compañía. Lo hicieron en el relé en la nube, porque ahí estaba la casilla de la característica. El cambio se aprobó rápido; “solo es un prefijo de asunto”.

Tres días después, recibos a clientes empezaron a aterrizar en spam y algunos socios rechazaron correos. El SRE de guardia vio dkim=fail en varios receptores, y alguien dijo con seguridad: “DKIM no debería importar el Subject, es solo una cabecera.” Esa frase les costó una tarde.

Por supuesto que a DKIM le importó. Su firmador estaba en la máquina Postfix, antes del relé en la nube, y firmaban la cabecera Subject. El relé modificó Subject después de la firma. Cada mensaje quedaba criptográficamente “manipulado”.

La solución no fue heroica. Moveron la firma DKIM al relé (el último salto antes de Internet) y dejaron de firmar cabeceras que sistemas downstream reescriben. También añadieron un control de cambios: “¿Esto modifica cabeceras/cuerpo tras la firma?” Suena burocrático. Es más barato que turismo inesperado por la carpeta de spam.

Micro-historia 2: La optimización que salió mal

La empresa B tenía una configuración madura y rotaba claves DKIM trimestralmente. Alguien notó que su proveedor DNS cobraba extra por “registros avanzados” y propuso una optimización: consolidar selectores y reutilizar la misma clave DKIM para múltiples subdominios y unidades de negocio. Menos registros, menos lío, menos partes móviles. ¿Qué podría salir mal?

Hicieron la consolidación, y también acortaron los TTL para acelerar cambios futuros. En la siguiente rotación, actualizaron el registro DNS para el selector y desplegaron la nueva clave privada a la mitad de su flota de envío primero. La otra mitad se retrasó un día por un desliz en una ventana de mantenimiento.

La mitad de la flota firmó con la clave nueva, la otra mitad con la antigua, y DNS servía solo la clave pública nueva. Cada mensaje de la mitad retrasada falló la verificación DKIM. Los receptores no aceptaron la explicación de la planificación interna. Solo vieron firmas rotas y ajustaron reputación en consecuencia.

La “optimización” eliminó la válvula de seguridad: tener selectores múltiples en paralelo. Si hubieran usado selectores solapados—viejo y nuevo—cada segmento de la flota podría haber firmado con el selector correspondiente a su clave desplegada, y DNS podría haber servido ambas claves hasta completar el despliegue.

Se recuperaron re-publicando el registro del selector antiguo, y luego planearon las rotaciones con ventanas de solapamiento y una regla explícita: “no eliminar selectores antiguos hasta que las colas estén vaciadas”. Menos registros no fue la victoria que esperaban.

Micro-historia 3: La práctica aburrida pero correcta que salvó el día

La empresa C tenía un entorno con cumplimiento fuerte donde cada mensaje saliente pasaba por una pasarela de archivado. La pasarela era famosa por a veces reescribir la estructura MIME cuando “normalizaba” adjuntos. A nadie le gustaba, pero era innegociable.

Años antes, un ingeniero insistió en un diseño poco glamuroso: la firma DKIM ocurre después de la pasarela de archivado, en el MTA final saliente. Eso significaba que la caja de archivado nunca veía mensajes ya firmados por DKIM y no podía romper las firmas. La gente se quejaba porque hacía la gestión de claves “una cosa más” en el salto final.

Un viernes, el equipo de archivado empujó una actualización que cambió cómo se generaban boundaries multipart. No alteró nada semánticamente, pero cambió bytes en tránsito para algunos mensajes. Si la firma DKIM hubiera estado corriente arriba, habría causado fallos DKIM masivos y probablemente un desastre DMARC el lunes.

En su lugar, no pasó nada. Las firmas DKIM se añadieron después de que la pasarela hiciera su peculiaridad. Los receptores validaron correo limpio. El incidente se degradó a “cambio interno creó diferente MIME” y nunca tocó la entregabilidad.

Broma corta #2: La entregabilidad de correo es el único deporte donde puedes perder porque alguien añadió dos espacios y un pie de página.

Listas de verificación / plan paso a paso

Paso a paso: arreglar un fallo DKIM en menos de una hora

  1. Captura un mensaje fallido crudo del buzón receptor (cabeceras + cuerpo crudos). Guarda como failed.eml.
  2. Lee Authentication-Results: decide si es “no key”, “body hash mismatch”, “signature invalid”, o “pass pero DMARC fail”.
  3. Extrae selector y dominio de DKIM-Signature (s=, d=).
  4. Consulta DNS por s._domainkey.d usando al menos un resolver público y tu entorno local.
  5. Valida el cableado del servicio firmador (config del milter, logs que muestren “DKIM-Signature field added”).
  6. Si mismatch de body hash: mapea la ruta saliente e identifica el salto que modifica contenido. Si es posible, omite temporalmente ese salto para confirmar.
  7. Aplica el cambio correctivo mínimo:
    • Arreglar registro DNS/discordancia de selector
    • Mover la firma río abajo de los modificadores
    • Detener modificadores que tocan correo saliente
    • Cambiar a relaxed/relaxed canonicalization
  8. Envía una prueba controlada a al menos dos receptores externos y confirma dkim=pass.
  9. Sólo entonces habla de endurecer rotación de claves, ARC y ajustes de política DMARC.

Lista de higiene operativa (lo que haces antes de que falle)

  • Firma en el último salto que controles antes de salir a Internet.
  • Usa claves de 2048 bits a menos que tengas una restricción fuerte; mantén los registros TXT limpios y correctamente divididos.
  • Rota selectores con solapamiento: publica selector nuevo primero, despliega firmadores, mantén el registro antiguo hasta que colas y rezagados se entreguen.
  • Escoge cabeceras estables para firmar; no firmes cabeceras que mutan por salto.
  • Registra eventos de firma DKIM y alerta sobre caídas súbitas en volumen de firmas.
  • Rastrea cada sistema que pueda reescribir correo (DLP, AV, etiquetado, relés “inteligentes”, CRMs, sistemas de tickets).
  • Valida la alineación DMARC para correo enviado por proveedores con tu dominio.

Una idea de fiabilidad que merece conservarse (parafraseada)

Idea parafraseada (John Allspaw): La fiabilidad surge de hacer observable el comportamiento del sistema y aprender de las fallas, no de fingir que las fallas no ocurrirán.

Preguntas frecuentes

1) ¿Por qué DKIM pasa a veces y falla otras para el mismo remitente?

Normalmente porque el correo tomó rutas distintas. Un camino modifica el mensaje (inyección de pie de página, etiquetado de asunto, reescritura MIME), otro no. Menos comúnmente es inconsistencia DNS: algunos resolvers ven el registro de clave y otros no debido a cachés, DNS dividido o un autoritativo roto.

2) ¿Cuál es la forma más rápida de saber si es DNS o modificación del mensaje?

Lee la Authentication-Results del receptor. “No key” indica DNS/selector. “Body hash did not verify” indica modificación después de la firma. “Signature did not verify” puede ser o clave equivocada/mismatch de rotación o cambios en cabeceras/cuerpo.

3) ¿Debo usar siempre canonicalización relaxed/relaxed?

En rutas de correo de producción con intermediarios del mundo real, sí, más a menudo que no. simple es frágil. Si controlas toda la ruta y quieres estrictitud, adelante, pero la mayoría de organizaciones no controlan realmente toda la ruta—aunque el diagrama de arquitectura lo afirme.

4) ¿Puedo firmar menos cabeceras para que DKIM sobreviva mejor?

Sí, pero no te pongas creativo. Firma cabeceras que signifiquen identidad como From, más metadatos de enrutamiento/contenido que no cambien después de la firma. Evita firmar cabeceras conocidas por cambiar aguas abajo. La meta es sobrevivencia sin hacer la firma irrelevante.

5) ¿El reenvío siempre rompe DKIM?

El reenvío suele romper SPF (porque el reenviador se convierte en la IP remitente) y puede romper DKIM si el reenviador modifica contenido. Un reenvío SMTP puro sin modificación puede preservar DKIM. Muchos reenviadores sí modifican (reenvuelven, añaden cabeceras, a veces alteran contenido), ahí donde ARC se vuelve valioso.

6) ¿Y las listas de correo?

Las listas de correo son el depredador natural de DKIM. Comúnmente añaden pies de página, modifican el subject y cambian cabeceras. Espera fallos DKIM a menos que la lista esté configurada para ser amigable con DKIM/DMARC (o use ARC correctamente). Si la participación en listas importa, planifica para ello en lugar de culpar a “que el correo es así”.

7) ¿Es aceptable aún una clave DKIM de 1024 bits?

Algunos receptores la aceptan, otros la penalizan, y el margen de seguridad es menor. La respuesta práctica: usa RSA de 2048 bits salvo que tu proveedor DNS o plataforma no lo soporte bien. Si no puedes, arregla la restricción del DNS/proveedor en lugar de aferrarte a 1024 bits.

8) Usamos un proveedor para enviar correo. ¿Por qué tenemos fallos DMARC aunque el proveedor diga “DKIM está habilitado”?

Porque probablemente el proveedor firma con su propio dominio (d=vendor.com) y tu From visible es el tuyo. DKIM puede pasar, pero no se alinea con tu dominio, así que DMARC falla. La solución es firma DKIM con dominio personalizado y un remitente de rebote alineado (custom bounce domain) si es necesario.

9) ¿Debería tener múltiples firmas DKIM?

Puede ser útil: una firma de tu infraestructura y otra de un proveedor, o una por identidad de dominio. Pero asegúrate de que al menos una firma se alinee con tu From-domain. Múltiples firmas también complican el debugging; mantenlo intencional, no accidental.

10) ¿Cómo rotar claves DKIM sin romper correo?

Usa un selector nuevo, publica su registro DNS primero, despliega firmadores para usarlo y mantén el registro antiguo publicado durante una ventana de solapamiento. Solo elimina el registro antiguo cuando estés seguro de que el correo retrasado y los MTAs rezagados ya no lo están usando.

Conclusión: próximos pasos que puedes hacer hoy

Las fallas DKIM parecen místicas hasta que las tratas como cualquier otro problema de integridad en producción: identifica qué cambió, dónde cambió y evita que cambie en el lugar equivocado.

Haz lo siguiente:

  1. Recopila un mensaje fallido crudo y clasifica el fallo vía Authentication-Results.
  2. Verifica el selector DNS desde un resolver público y tu entorno local. Arregla “no key” antes de tocar cualquier otra cosa.
  3. Si es mismatch de body hash, detén las modificaciones post-firma: mueve la firma DKIM al salto final saliente o desactiva funciones de reescritura aguas abajo.
  4. Asegura la alineación DMARC para tus dominios From reales, especialmente con correo enviado por proveedores.
  5. Institucionaliza lo aburrido: solapamiento en rotación de claves, registrar eventos de firma y tratar “añadir un pie de página” como un cambio de entregabilidad, no como una mejora cosmética.

El correo es un sistema distribuido hostil disfrazado de servicio commodity. DKIM es una de las pocas piezas que se comporta de forma predecible. Si falla, algo cambió. Encuéntralo. Arréglalo. Luego apúntalo para que el Tú del Futuro no tenga que redescubrirlo a las 3 a.m.

← Anterior
GPU de Docker en contenedores: por qué falla y cómo solucionarlo
Siguiente →
Blockchain en todas partes: el bombo que se pegó a los productos

Deja un comentario