Durante una prueba de penetración rutinaria para una empresa mediana, nuestro equipo de seguridad encontró algo que dejó al CTO del cliente en silencio en plena llamada durante unos diez segundos.
Acabábamos de enviar un email al director financiero de la empresa desde la dirección de email del CEO. Pedía una transferencia urgente a un nuevo proveedor. Llegó a la bandeja de entrada. Sin filtro de spam, sin banner de advertencia, sin ningún aviso de "este email podría ser sospechoso". Tenía exactamente el mismo aspecto que cualquier otro email que el CEO hubiera enviado.
Lo hicimos desde un portátil en Oporto, con un solo comando y sin acceso a la infraestructura de email de la empresa. Sin contraseñas, sin cuentas hackeadas, sin acceso interno. Solo un registro DNS mal configurado que llevaba más de tres años a la vista de todos. Desde la consulta DNS hasta el email entregado, todo llevó menos de dos minutos.
Aquí tienes por qué funcionó y cómo cerrar el agujero.
Lo que encontramos
La empresa tenía una configuración normal: una plataforma de buzones alojada para la plantilla, un servicio de email transaccional para las notificaciones automáticas y una plataforma de marketing para las newsletters. Su DNS tenía tres problemas:
- DMARC estaba en
p=none. La política existía, pero indicaba a los receptores que no hicieran nada cuando un mensaje fallaba. - SPF terminaba en softfail (
~all) en lugar de hard fail (-all). Un servidor no autorizado recibía un veredicto de "sospechoso", no un rechazo. - DKIM no estaba configurado en todos sus servicios de envío.
Nos conectamos al servidor de correo de la empresa, localizado a través del registro MX público, en el puerto 25, que tiene que estar abierto para que el email funcione. El servidor receptor comprobó SPF y vio un softfail, comprobó DMARC y vio p=none, y entregó el mensaje. Cualquiera en internet podría haber hecho lo mismo.
El registro DMARC decidió el resultado: de los tres, es el único que habla de la dirección que ve el destinatario. Es el agujero detrás del Business Email Compromise (BEC): pagos a proveedores falsos, cambios falsos de datos bancarios, todo sin hackear ninguna cuenta.
Cómo funciona la suplantación
Un email lleva dos direcciones de remitente:
- El remitente del sobre, el
MAIL FROMde la conversación SMTP, que después queda guardado comoReturn-Path. El destinatario nunca lo ve. - La cabecera From, la dirección que muestra el cliente de correo. SMTP no la comprueba: quien envía puede escribir ahí lo que quiera.
Cada estándar comprueba algo distinto:
- SPF comprueba si el servidor que se conecta puede enviar en nombre del dominio del remitente del sobre. Nunca mira la cabecera From.
- DKIM comprueba que el mensaje se firmó con una clave publicada bajo algún dominio y que no ha cambiado desde entonces. Ese dominio, el valor
d=de la cabeceraDKIM-Signature, puede ser cualquiera. - DMARC pregunta si un resultado SPF o DKIM aprobado pertenece al dominio de la cabecera From. Esa coincidencia se llama alineación: un mensaje pasa DMARC si SPF pasa y el dominio del sobre está alineado con el dominio del From, o si DKIM pasa y el dominio
d=está alineado con él. La política (p=none,quarantineoreject) indica al receptor qué hacer cuando no ocurre ninguna de las dos cosas.
Por eso SPF por sí solo no puede detener el ataque, ni siquiera con -all. Un atacante puede poner su propio dominio en el sobre, con un registro SPF que pasa, y la dirección de tu CEO en la cabecera From. Solo DMARC se da cuenta de que ese aprobado pertenece a otro.
Los mensajes recibidos registran los veredictos en su cabecera Authentication-Results. Una suplantación de un dominio sin protección:
Authentication-Results: mx.example.net;
spf=pass smtp.mailfrom=attacker.example.org;
dkim=none;
dmarc=fail header.from=example.comSPF pasó, para el dominio equivocado; DMARC falló. La entrega depende ahora de la política que publique example.com.
Comprueba tu propio dominio
Tres consultas muestran lo que ve un atacante (sustituye example.com por tu dominio):
dig +short TXT example.com
dig +short TXT _dmarc.example.com
dig +short TXT s2026a._domainkey.example.comLa primera devuelve todos los registros TXT de tu dominio. Exactamente una línea debe empezar por "v=spf1; dos es un error. Una línea formada por varias cadenas entre comillas está bien: los registros largos se dividen en cadenas de 255 caracteres que los receptores vuelven a unir. Después lee el final: -all (fail), ~all (softfail), ?all (neutral) o +all (pass para cualquiera).
La segunda devuelve tu registro DMARC, por ejemplo "v=DMARC1; p=none". Sin respuesta, no hay DMARC: cada receptor aplica su propio criterio y nadie te envía informes. Dos respuestas v=DMARC1 significan que los receptores no aplican ninguna política DMARC. Cualquier cosa por debajo de p=reject, o de quarantine como paso intermedio, deja la puerta abierta.
La tercera necesita un selector, y el DNS no permite listarlos. Sácalo de un mensaje real: en su cabecera DKIM-Signature, s=s2026a; d=example.com significa que la clave está en s2026a._domainkey.example.com. Repite con cada sistema que envía en nombre de tu dominio. La respuesta debe contener v=DKIM1 y un valor p= largo; si es un CNAME hacia tu proveedor, primero aparece su destino. Un p= vacío indica una clave revocada.
Lo difícil es saber qué sistemas deben figurar en estos registros. Solicita una evaluación gratuita de email spoofing y nuestro equipo de seguridad revisará tus registros SPF, DKIM y DMARC, contará tus consultas SPF, listará todos los servicios que envían en nombre de tu dominio y te dirá si hoy se puede suplantar.
SPF: dónde se rompe sin avisar
Un registro SPF correcto enumera todos los sistemas autorizados a enviar en nombre de tu dominio y, al final, un veredicto para todo lo demás:
example.com. IN TXT "v=spf1 include:_spf.mail.example.net include:spf.crm.example.org ip4:203.0.113.25 -all"Cuatro errores explican la mayoría de los SPF rotos:
- Dos registros SPF en el mismo nombre. Añadir un remitente creando un segundo registro
v=spf1en lugar de editar el primero hace que SPF devuelvapermerror. Fusiónalos. +allo?all. El primero autoriza a todo internet. El segundo no afirma nada.- El límite de 10 consultas.
include,a,mx,ptr,existsyredirectcuentan cada uno como una consulta, también los que están dentro de los registros que incluyes;ip4,ip6yallno cuentan. Con más de 10, el resultado espermerror. - Remitentes olvidados. Facturación, CRM, ticketing, el escáner que envía PDF por email, las alertas de monitorización, el formulario de contacto de la web. En WordPress ese formulario suele funcionar con un plugin, que también forma parte de la superficie de ataque del sitio.
Para contar, ejecuta dig +short TXT sobre el destino de cada include. Supón que tu registro tiene tres includes y un mx: cuatro consultas. Si el include de la plataforma de buzones contiene otros tres, ya vas por siete. Cerca del límite, elimina los remitentes que ya no usas, mueve los envíos masivos a un subdominio con su propio registro o sustituye a y mx por tus propios rangos ip4. Nunca pegues las IP de un proveedor en tu registro: cambian sin aviso.
Cuando DMARC ya aplica su política, tanto ~all como -all son defendibles. Algunos receptores actúan ante un hard fail de SPF antes de evaluar DMARC y rechazan correo reenviado que DKIM habría salvado, por eso algunos equipos mantienen ~all. Lo que falla es ~all sin una política DMARC aplicada, justo el estado que encontramos.
Muchos remitentes alojados ponen además su propio dominio de rebote en el sobre, así que SPF pasa para ellos y no queda alineado. Un dominio de rebote personalizado bajo el tuyo lo resuelve; DKIM con tu dominio es más robusto.
DKIM: un selector por remitente
DKIM suele sobrevivir a un reenvío simple y SPF no, así que es la vía de alineación en la que apoyarte:
- Un selector por sistema de envío. La plataforma de buzones firma con
s2026a, el CRM concrm2026. Los sistemas nunca comparten clave, y dar de baja a un proveedor significa borrar un registro. - Firma como tu dominio. Muchas plataformas firman con su propio dominio hasta que configuras la firma con dominio personalizado. Esa firma pasa pero no se alinea:
d=debe serexample.como uno de sus subdominios. - Claves RSA de 2048 bits. El estándar DKIM exige al menos 1024 bits y recomienda 2048.
Para comprobar la longitud de una clave publicada:
dig +short TXT s2026a._domainkey.example.com | tr -d '" \n' | sed -n 's/.*;p=\([^;]*\).*/\1/p' | openssl base64 -d -A | openssl pkey -pubin -inform DER -noout -text | head -1Busca Public-Key: (2048 bit) en la salida.
Rota las claves con un calendario que puedas cumplir, por ejemplo dos veces al año, y de inmediato si una clave puede haberse filtrado:
- 1Publica una clave nueva con un selector nuevo (
s2026b). - 2Cuando haya expirado el TTL del DNS, cambia la firma a ese selector.
- 3Mantén el registro antiguo una semana más o menos, para que el correo retrasado siga verificándose, y después revócalo con un
p=vacío o bórralo.
DMARC: de p=none a p=reject sin perder correo
p=none está pensado para ser temporal: te muestra quién envía en nombre de tu dominio antes de pedir a los receptores que bloqueen a nadie. Si se queda así, informa del ataque y lo entrega.
Fase 1: monitorizar
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com"Los receptores que admiten informes envían informes agregados a la dirección rua, normalmente a diario, en XML comprimido. Un buzón en otro dominio tiene que estar autorizado allí con un registro TXT "v=DMARC1" en example.com._report._dmarc.reports.example.net, o no llegará ningún informe.
Fase 2: leer los informes
Cada registro muestra cuántos mensajes envió una IP de origen con tu dominio en el From, y cómo les fue:
<record>
<row>
<source_ip>198.51.100.42</source_ip>
<count>318</count>
<policy_evaluated>
<disposition>none</disposition>
<dkim>fail</dkim>
<spf>fail</spf>
</policy_evaluated>
</row>
<identifiers>
<header_from>example.com</header_from>
</identifiers>
<auth_results>
<dkim>
<domain>crm.example.org</domain>
<result>pass</result>
</dkim>
<spf>
<domain>bounce.crm.example.org</domain>
<result>pass</result>
</spf>
</auth_results>
</record>Es un remitente legítimo que falla DMARC: el CRM firma y gestiona los rebotes con su propio dominio, así que las dos comprobaciones pasan y ninguna se alinea. Clasifica cada origen en uno de cuatro grupos:
- Tuyos y alineados: hecho.
- Tuyos y fallando: tu lista de trabajo. Corrige cada uno con DKIM con tu dominio o con un dominio de rebote personalizado.
- Reenviadores y listas de correo: SPF se rompe al reenviar; DKIM sobrevive salvo que se modifique el mensaje, como suelen hacer las listas. No te toca arreglarlos.
- Desconocidos: consulta la IP (
dig +short -x 198.51.100.42). Es un remitente del que nadie te avisó o alguien suplantándote; averigua cuál antes de aplicar la política.
Un parser open source como parsedmarc permite hacer búsquedas en el XML. Quédate en esta fase hasta que la lista de trabajo lleve vacía un mes entero, para que aparezca la facturación de fin de mes.
Fase 3: cuarentena, de forma gradual
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc-reports@example.com"pct=25 pone en cuarentena una cuarta parte de los mensajes que fallan; el resto recibe la política inmediatamente más débil, aquí none. Súbelo a 50 y después a 100 (el valor por defecto), leyendo los informes en cada paso.
Fase 4: rechazar
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc-reports@example.com"Dos etiquetas merecen una decisión:
sp=define la política para los subdominios sin registro DMARC propio; si la omites, heredanp=. Una suplantación desdebilling.example.com, un nombre que nunca envió correo, no tiene SPF ni DKIM que pasar, así que es la política de subdominio la que la detiene. Unsp=rejectexplícito se mantiene mientras ajustasp=.adkim=yaspf=definen la alineación. Ambas son relajadas por defecto (r):mail.example.comse alinea conexample.com. La estricta (s) exige coincidencia exacta; elígela solo si todos los remitentes usan exactamente tu dominio.
Dominios que no envían correo
Los dominios aparcados, los dominios con erratas registrados como defensa y los dominios de marcas antiguas que solo redirigen la web son fáciles de suplantar porque nadie los vigila. Declara que no envían ni reciben nada:
example.org. IN MX 0 .
example.org. IN TXT "v=spf1 -all"
_dmarc.example.org. IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s; rua=mailto:dmarc-reports@example.com"
example.org._report._dmarc.example.com. IN TXT "v=DMARC1"El null MX (RFC 7505) indica que el dominio no acepta correo, así que los remitentes fallan al momento en lugar de reintentar. SPF no autoriza ningún servidor, DMARC rechaza todo lo que diga venir del dominio o de sus subdominios, y la última línea, en tu zona principal, permite que los informes lleguen a tu buzón.
Después de p=reject: sigue vigilando
Las configuraciones de correo se desvían con el tiempo:
- Mantén
ruapara siempre. Revisa los informes cada semana; configura alertas para un origen nuevo que falla en volumen o para un remitente legítimo cuya tasa de aprobados baja. - Controla los remitentes nuevos. Antes de que una herramienta nueva envíe en nombre de tu dominio: DKIM con tu dominio, consultas SPF recontadas y un mensaje de prueba que muestre
dmarc=pass. - Da de baja a los remitentes. Elimina el include y el selector cuando termine el contrato.
- Controla los cambios de DNS. Un segundo registro SPF suele venir de una edición apresurada en el panel del registrador. Si gestionas tus propios servidores BIND 9, DNS Manager, la interfaz web open source de Privum para BIND 9, somete los cambios de registros a control de acceso por roles y a un registro de auditoría.
- Comprueba desde fuera. Ejecuta esto desde cron o desde CI contra todos tus dominios.
#!/usr/bin/env bash
# Usage: check-mail-auth.sh example.com (exits 1 on failure)
set -eu
d="$1"
dmarc=$(dig +short TXT "_dmarc.$d")
dmarc_n=$(echo "$dmarc" | grep -ci '^"v=DMARC1' || true)
spf=$(dig +short TXT "$d" | grep -ci '^"v=spf1' || true)
[ "$dmarc_n" -eq 1 ] || { echo "FAIL $d: $dmarc_n DMARC records, expected 1"; exit 1; }
echo "$dmarc" | grep -Eq ';[[:space:]]*p=reject' || { echo "FAIL $d: DMARC is not at p=reject"; exit 1; }
[ "$spf" -eq 1 ] || { echo "FAIL $d: $spf SPF records, expected 1"; exit 1; }
echo "OK $d"Los registros pueden parecer correctos y aun así dejar un hueco, como un subdominio con su propio registro DMARC permisivo. La prueba definitiva es un intento de suplantación contra tu propio servidor de correo: así encontramos este, durante una prueba de penetración.
Descubre si tu dominio se puede suplantar antes de que lo descubra otro. Reserva una evaluación gratuita de email spoofing: revisamos SPF, DKIM y DMARC en todos tus dominios, incluidos los aparcados, identificamos cada servicio que envía en tu nombre y te entregamos un plan por fases hasta p=reject que mantiene en marcha las facturas y las newsletters.