La seguridad de PHP mail() depende principalmente de cómo la aplicación recibe, valida y utiliza los datos. La función no convierte automáticamente un formulario en inseguro, pero un código mal diseñado puede permitir inyección de cabeceras, envío de spam, consumo excesivo de recursos o suplantación del remitente.
La defensa debe comenzar antes de llamar a mail(): el formulario, la validación del servidor y la construcción del mensaje forman parte del mismo proceso.
Qué es la inyección de cabeceras
Las cabeceras de un correo se separan mediante saltos de línea. Si una aplicación copia directamente texto del visitante dentro de From, Reply-To, Subject, Cc o Bcc, un atacante puede intentar introducir caracteres de control para crear cabeceras adicionales.
Como resultado, el formulario podría transformarse en una herramienta para enviar mensajes a destinatarios no previstos. Aunque versiones actuales de PHP y algunas bibliotecas incorporen controles, la aplicación debe validar todos los valores externos.
No permitas que el visitante construya las cabeceras
- Definí el destinatario en la configuración de la aplicación, no en un campo oculto editable.
- Usá una dirección fija del propio dominio como remitente.
- Validá la dirección del visitante y utilizala solamente como Reply-To cuando sea necesario.
- Rechazá saltos de línea y caracteres de control en campos destinados a una sola línea.
- No aceptes cabeceras Cc o Bcc proporcionadas por el usuario.
- No pases datos externos a parámetros adicionales del programa de correo.
Validar una dirección con una función específica ayuda a comprobar su formato, pero también deben aplicarse longitud máxima y reglas adecuadas al contexto.
Por qué conviene una biblioteca de correo
Una biblioteca mantenida administra cabeceras, codificación, MIME, adjuntos y SMTP de forma más consistente que la concatenación manual de cadenas. Sin embargo, no reemplaza la validación: si se le entrega un destinatario arbitrario o contenido peligroso, la aplicación puede seguir siendo abusada.
La guía sobre cómo enviar correo desde PHP mediante SMTP explica por qué el envío autenticado facilita el diagnóstico y mejora el control.
Formularios usados como repetidores de spam
Un formulario puede no tener una vulnerabilidad de inyección y aun así ser automatizado miles de veces. Cada solicitud genera un correo legítimo desde el punto de vista técnico, pero el volumen termina consumiendo recursos, llenando casillas, afectando la reputación del dominio o activando límites del servidor.
Para reducir el abuso combiná:
- Límites de frecuencia por sesión, cuenta, IP y comportamiento.
- Un límite global para evitar que una distribución de bots eluda el control por IP.
- Campos señuelo que los visitantes normales no completan.
- Turnstile o CAPTCHA cuando el nivel de abuso lo justifique.
- Validación obligatoria en el servidor.
- Registros que permitan detectar aumentos o patrones anómalos.
Una protección visual en el navegador no basta. El atacante puede enviar solicitudes directamente al endpoint sin cargar la página.
Turnstile debe validarse en el servidor
El widget genera un token, pero la aplicación debe enviarlo al servicio de verificación desde el backend y comprobar una respuesta válida antes de procesar el formulario. Los tokens vencen y son de un solo uso, por lo que no deben aceptarse repetidos.
La clave secreta pertenece al servidor y nunca debe aparecer en JavaScript público. También conviene validar el dominio y la acción esperada cuando la integración los utiliza.
CSRF y spam no son el mismo problema
La protección contra bots intenta reducir automatización. Un token CSRF busca impedir que otro sitio provoque una acción utilizando la sesión autenticada de la víctima. CAPTCHA o Turnstile no reemplazan una defensa CSRF en operaciones que cambian datos.
Usá las funciones de seguridad del framework para generar y validar tokens CSRF en solicitudes autenticadas. No realices cambios de estado mediante enlaces GET.
Validación y codificación de salida
Validar significa comprobar formato, tipo, longitud y reglas del negocio. Escapar o codificar significa preparar el dato para el contexto donde será utilizado. Son controles diferentes.
- Validá una dirección como email antes de usarla en Reply-To.
- Limitá la longitud de nombre, asunto y mensaje.
- Codificá contenido al mostrarlo nuevamente en HTML para evitar XSS.
- Utilizá consultas preparadas si los datos se almacenan en una base.
- No insertes texto del usuario en comandos o rutas del sistema.
Podés ampliar estos controles en las guías sobre prevención de XSS e inyección SQL.
Remitente, Reply-To y autenticación del dominio
Usar como From una dirección de Gmail, Outlook o del visitante puede provocar fallos de alineación y suplantación. Configurá una casilla real del dominio como remitente y colocá el email validado del visitante en Reply-To.
Comprobá también SPF, DKIM y DMARC. La seguridad del formulario y la autenticación del dominio son controles complementarios.
Proteger las credenciales SMTP
Una contraseña SMTP expuesta puede utilizarse fuera del formulario y generar un abuso mucho mayor. Guardá las credenciales en una ubicación de configuración protegida, evitá subirlas a repositorios y restringí la lectura de los archivos.
No muestres contraseñas, respuestas SMTP completas ni trazas internas a los visitantes. Registrá el detalle técnico en una ubicación no pública y presentá un mensaje genérico en pantalla.
Límites y registros útiles
Registrá fecha, resultado, endpoint y una referencia suficiente para investigar, evitando almacenar innecesariamente el contenido completo o datos sensibles. Si conservás direcciones IP, definí una finalidad, acceso restringido y tiempo de retención.
Los límites deberían considerar tanto ráfagas cortas como volumen acumulado. Un control excesivamente estricto puede bloquear usuarios legítimos; uno basado solamente en IP puede ser evadido o afectar redes compartidas.
Señales de que el formulario fue abusado
- Aumento repentino de mensajes o procesos PHP.
- Destinatarios desconocidos en registros o cola.
- Rebotes y respuestas por correo que el sitio no esperaba.
- Bloqueos por límites de destinatarios fallidos.
- Contenido repetitivo, enlaces sospechosos o caracteres de control.
- Intentos enviados sin haber cargado previamente el formulario.
Qué hacer ante un abuso
- Deshabilitá temporalmente el endpoint o el envío afectado.
- Conservá registros suficientes para identificar el origen.
- Cambiá las credenciales si pudieron quedar expuestas.
- Eliminá mensajes pendientes no autorizados.
- Corregí validación, destinatarios, límites y protección contra bots.
- Actualizá la aplicación y sus dependencias.
- Probá nuevamente antes de habilitarlo.
No te limites a bloquear una dirección IP: un atacante puede cambiarla y la vulnerabilidad continuará abierta. La solución debe estar en el código y en los controles del endpoint.
Lista de comprobación
- Destinatario y From definidos por la aplicación.
- Reply-To validado y sin caracteres de control.
- Biblioteca de correo actualizada y SMTP autenticado.
- Límites de frecuencia y volumen global.
- Turnstile o CAPTCHA validado en el servidor.
- Protección CSRF cuando existe una sesión autenticada.
- Registros, alertas y respuesta ante incidentes.