En resumen: las cabeceras de seguridad son instrucciones del servidor web al navegador que protegen a los visitantes frente a ataques como el cross-site scripting, el clickjacking y las conexiones sin cifrar. Las más importantes son HSTS, Content-Security-Policy, X-Content-Type-Options, la protección frente a marcos ajenos, Referrer-Policy y Permissions-Policy. Son un refuerzo razonable, pero no sustituyen a las actualizaciones en el servidor.
Qué consiguen las cabeceras de seguridad y qué no
En cada acceso a una página, el servidor web envía, además del contenido, cabeceras. Algunas de ellas controlan funciones de seguridad del navegador. Con ellas se puede establecer, por ejemplo, que una página se cargue solo por HTTPS o que el navegador no ejecute scripts de dominios ajenos.
Para los responsables es importante la clasificación: las cabeceras protegen en primer lugar a los visitantes, por ejemplo frente a la captura de sesiones o a formularios manipulados. No cierran vulnerabilidades en el servidor. Una debilidad en PHP, en el CMS o en una extensión permanece, por bien que estén configuradas las cabeceras. En combinación con un código limpio, sin embargo, limitan notablemente las consecuencias de muchos errores.
Las cabeceras de seguridad más importantes, de un vistazo
| Cabecera | Finalidad | Recomendación como punto de partida |
|---|---|---|
| Strict-Transport-Security (HSTS) | fuerza HTTPS en los accesos futuros | max-age=31536000; includeSubDomains |
| Content-Security-Policy (CSP) | define las fuentes permitidas para scripts, imágenes, marcos | individual, primero como Report-Only |
| X-Content-Type-Options | impide que el navegador adivine los tipos de archivo | nosniff |
| X-Frame-Options o CSP frame-ancestors | impide la incrustación en páginas ajenas (clickjacking) | SAMEORIGIN o frame-ancestors 'self' |
| Referrer-Policy | limita qué dirección se transmite a otras páginas | strict-origin-when-cross-origin |
| Permissions-Policy | desactiva funciones del navegador como la cámara o la ubicación | permitir solo las funciones necesarias |
HSTS solo debería establecerlo cuando todos los subdominios sean accesibles de forma fiable por HTTPS. El navegador recuerda la instrucción durante el tiempo indicado; un error no se puede deshacer entonces de inmediato. Comience, si procede, con una duración corta y auméntela tras pruebas exitosas. La opción adicional preload inscribe el dominio de forma permanente en listas de los fabricantes de navegadores y solo debería establecerse tras una revisión cuidadosa.
Las cabeceras tampoco son el único lugar en el que el navegador ofrece protección. Las cookies deberían llevar los atributos Secure, HttpOnly y SameSite, para que las sesiones no puedan capturarse a través de conexiones sin cifrar o de scripts. Cómo configurarlo en PHP lo describe la guía sobre sesiones.
Configurar cabeceras de seguridad en Nginx
En Nginx, las cabeceras van en el bloque server del sitio web correspondiente. El añadido always hace que se envíen también en las páginas de error.
server {
# ...
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'" always;
server_tokens off;
}
Una peculiaridad de Nginx: si un bloque location define sus propias instrucciones add_header, las cabeceras del bloque server no se heredan allí. Revise por eso también las subpáginas y los archivos estáticos. Un refuerzo adicional para esta combinación lo describe Proteger Nginx con PHP-FPM.
Configurar cabeceras de seguridad en Apache
En Apache necesita el módulo mod_headers. Las instrucciones van en el VirtualHost o, si no hay acceso a él, en el .htaccess.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
</IfModule>
ServerTokens Prod
ServerSignature Off
ServerTokens y ServerSignature van en la configuración principal. Impiden que el servidor indique su versión exacta en las respuestas y en las páginas de error. En php.ini se logra lo mismo para PHP con expose_php = Off.
Introducir la Content-Security-Policy de forma gradual
La Content-Security-Policy es la cabecera más eficaz, pero también la más exigente. Define desde qué fuentes pueden cargarse scripts, hojas de estilo, imágenes, fuentes tipográficas y marcos. Si un script introducido se descarga desde un dominio ajeno, el navegador lo bloquea.
Los sitios web modernos suelen incorporar herramientas de análisis, proveedores de pago, servicios de mapas o vídeos. Una política demasiado estricta deja entonces funciones inutilizadas. Proceda por eso en etapas:
- Empezar primero con
Content-Security-Policy-Report-Only. El navegador comunica las infracciones en la consola de desarrollo o a una dirección colectora, pero no bloquea nada. - Registrar todas las fuentes necesarias y autorizarlas de forma específica, en lugar de usar comodines generales.
- Sacar gradualmente los scripts en línea a archivos o dotarlos de nonces, para poder prescindir de
'unsafe-inline'. - Pasar a
Content-Security-Policysolo tras una fase de pruebas sin avisos inesperados.
La CSP complementa el escapado limpio en el código, pero no lo sustituye. Cómo proteger bien las salidas en PHP se explica en la guía Evitar el cross-site scripting en PHP.
Comprobar las cabeceras y evitar errores típicos
Tras cada cambio debería comprobar si las cabeceras llegan realmente. Se puede hacer sin herramientas adicionales con curl:
# Probar la configuración y recargarla
nginx -t && systemctl reload nginx
# Mostrar las cabeceras de la página de inicio y de un archivo estático
curl -sI https://www.example.de/ | grep -iE 'strict|content-security|x-content|x-frame|referrer|permissions|server'
curl -sI https://www.example.de/favicon.ico | grep -iE 'strict|x-content'
Los errores típicos son las cabeceras definidas por duplicado cuando el servidor web y la aplicación envían las mismas, un CDN antepuesto que elimina o sobrescribe cabeceras y cabeceras que solo aparecen en la página de inicio, pero no en las redirecciones ni en las páginas de error. Compruebe además, tras las actualizaciones del CMS o de las extensiones de seguridad, si las cabeceras han cambiado.
Determine por eso un único lugar donde se mantengan las cabeceras, preferiblemente el servidor web o el servicio antepuesto. Muchas extensiones de CMS ofrecen también ajustes para cabeceras de seguridad. Es cómodo, pero conduce fácilmente a indicaciones duplicadas o contradictorias si además el servidor establece cabeceras. Para pruebas puntuales sirven también las herramientas de desarrollo del navegador: en la pestaña de red se ven las cabeceras de respuesta de cada petición, y la consola muestra las infracciones de la Content-Security-Policy.
¿Cómo ayuda PHP SOS en concreto?
- Revisamos las cabeceras actuales de todos sus sitios web, también tras CDN y proxy, y señalamos lagunas y contradicciones.
- Establecemos las cabeceras de forma centralizada en el servidor web en el marco del refuerzo del servidor y probamos la página de inicio, las subpáginas y las páginas de error.
- Introducimos la Content-Security-Policy en modo Report-Only, analizamos los avisos y la activamos tras acordarlo con usted.
- Donde no sean posibles cambios de código de inmediato, complementamos la protección con reglas WAF y parcheo virtual.
Preguntas frecuentes
¿Qué son las cabeceras de seguridad?
Las cabeceras de seguridad son indicaciones adicionales en la respuesta del servidor web. Dicen al navegador, por ejemplo, que cargue la página solo por HTTPS, que no ejecute scripts ajenos o que no muestre la página dentro de marcos ajenos. Protegen sobre todo a los visitantes.
¿Protegen las cabeceras de seguridad frente a PHP2Shell?
No. PHP2Shell es una vulnerabilidad de PHP en el servidor; las cabeceras actúan en el navegador del visitante. Contra PHP2Shell ayuda la actualización a una versión corregida de PHP y el reinicio de todos los workers. Las cabeceras son un refuerzo complementario frente a otros tipos de ataque.
¿Puede una Content Security Policy romper mi sitio web?
Sí, una política demasiado estricta bloquea scripts, fuentes o contenidos incrustados que se necesitan. Por eso se introduce primero en modo solo informes, se analizan los avisos y solo después se pasa a aplicarla.
¿Tiene todavía sentido X-XSS-Protection?
No. Los navegadores actuales ya no evalúan esta cabecera, y la recomendación es omitirla o establecerla en 0. La protección frente al cross-site scripting la ofrecen un escapado limpio en el código y una Content Security Policy.