Guides 6 min de lectura

Cabeceras de seguridad HTTP: cuáles enviar y cómo comprobarlas

HSTS, Content-Security-Policy, X-Content-Type-Options, protección frente a frames, Referrer-Policy y Permissions-Policy: qué hace cada una y un valor seguro para empezar.

Las cabeceras de seguridad HTTP son cabeceras de respuesta que indican al navegador que aplique protecciones que, de otro modo, tu web tendría que implementar por su cuenta: mantenerse en HTTPS, ejecutar solo scripts de orígenes de confianza, no adivinar tipos de archivo, no dejar que otras webs incrusten la página en un frame y compartir menos información sobre de dónde viene un visitante. Seis cabeceras cubren casi todo: Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, X-Frame-Options (o la directiva frame-ancestors de la CSP), Referrer-Policy y Permissions-Policy. La mayoría se configura con una sola línea en el servidor. Content-Security-Policy requiere planificación, así que empieza en modo Report-Only.

Las seis cabeceras de un vistazo

CabeceraProtege contraUn valor para empezarStrict-Transport-SecurityConexiones degradadas a HTTP sin cifrarmax-age=31536000Content-Security-PolicyScripts inyectados (cross-site scripting)Una política probada antes en Report-OnlyX-Content-Type-OptionsNavegadores que ejecutan un archivo como si fuera de otro tiponosniffX-Frame-Options o frame-ancestorsClickjacking mediante framesSAMEORIGIN y frame-ancestors ‘self’Referrer-PolicyDirecciones completas que se filtran a otras websstrict-origin-when-cross-originPermissions-PolicySolicitudes de funciones del navegador que no usascamera=(), microphone=(), geolocation=()

Strict-Transport-Security (HSTS)

Aunque redirijas de HTTP a HTTPS, un visitante que escribe tu dominio hace la primera petición por HTTP sin cifrar, donde puede leerse o modificarse por el camino. HSTS indica al navegador que use HTTPS para tu dominio a partir de ese momento, durante el tiempo que marque max-age, en segundos:

Strict-Transport-Security: max-age=31536000

Empieza con poco. Los navegadores respetan HSTS hasta que caduca, así que un error se queda ahí. Prueba max-age=300 durante unos días y luego súbelo a un año (31536000).

includeSubDomains aplica la regla a todos los subdominios. Añádelo solo cuando todos, incluidos los antiguos de staging y de correo, funcionen por HTTPS.

preload solicita que el dominio se incluya de serie en los navegadores a través de hstspreload.org, lo que exige un max-age de al menos un año e includeSubDomains. La retirada tarda mucho, así que trátalo como algo permanente.

Los navegadores ignoran HSTS en las respuestas HTTP sin cifrar. Envíalo por HTTPS y vigila el certificado: con HSTS, un certificado caducado no se puede saltar con un clic.

Content-Security-Policy

Una Content Security Policy enumera desde dónde puede cargar la página scripts, estilos, imágenes, fuentes y frames, y adónde pueden enviar datos los formularios. Si un atacante consigue colar un script en la página, a través de un campo de comentarios o de un plugin comprometido, el navegador se niega a ejecutarlo salvo que la política permita su origen. Es la más eficaz de las seis y la más fácil de configurar mal, ya que una política demasiado estricta rompe tu propia analítica, el widget de chat o el formulario de pago.

Empieza con Report-Only

Envía primero la política como Content-Security-Policy-Report-Only. El navegador no bloquea nada, pero notifica cada infracción a la dirección indicada en report-uri (o en la más reciente report-to) y la registra en la consola. Un punto de partida para una web que sirve sus propios archivos:

Content-Security-Policy-Report-Only: default-src ‘self’; script-src ‘self’; style-src ‘self’; img-src ‘self’ data:; font-src ‘self’; object-src ‘none’; base-uri ‘self’; form-action ‘self’; frame-ancestors ‘self’; report-uri /csp-report

Navega por la web, recoge los informes durante una o dos semanas y añade los orígenes que de verdad usas, como tu proveedor de analítica o de pagos. Cuando los informes solo muestren cosas que quieres bloquear, envía la misma política como Content-Security-Policy.

Scripts inline: nonces, no unsafe-inline

Una política estricta bloquea los bloques <script> inline y los atributos onclick, porque un script inyectado tiene el mismo aspecto. Añadir ‘unsafe-inline’ hace que el problema desaparezca, y con él la mayor parte de la protección. En su lugar, genera un nonce aleatorio en cada respuesta, inclúyelo en la política como ‘nonce-…’ y pon el mismo valor en tus propias etiquetas script; traslada los manejadores de eventos inline a archivos de script. Los gestores de etiquetas que inyectan sus propios scripts requieren especial cuidado, ya que cada etiqueta que añaden también tiene que estar permitida.

X-Content-Type-Options

Antes, los navegadores adivinaban el tipo de un archivo a partir de su contenido cuando el Content-Type del servidor parecía incorrecto. Eso permitía que una «imagen» subida se ejecutara como script. X-Content-Type-Options: nosniff desactiva esas suposiciones. Tiene un único valor válido y ningún inconveniente conocido, siempre que tu servidor envíe tipos de contenido correctos, así que envíala en todas las respuestas.

X-Frame-Options y frame-ancestors

En un ataque de clickjacking, otra web muestra tu página en un frame invisible y engaña a los visitantes para que hagan clic en sus botones. X-Frame-Options: DENY impide cualquier frame, y SAMEORIGIN solo permite tus propias páginas. La directiva CSP frame-ancestors hace lo mismo con más control, por ejemplo con una lista de webs asociadas, y tiene prioridad en los navegadores que la admiten. Envía ambas: frame-ancestors para los navegadores actuales y X-Frame-Options para los antiguos. El antiguo valor ALLOW-FROM ya no se admite, así que usa frame-ancestors para cualquier cosa más allá de tu propio origen.

Referrer-Policy

Cuando un visitante sigue un enlace, el navegador le dice a la siguiente web de dónde viene en la cabecera Referer. Sin límites, puede ser la dirección completa, incluida una búsqueda, un token de restablecimiento o un número de pedido en la URL. strict-origin-when-cross-origin envía la dirección completa dentro de tu propia web, solo el dominio a otras webs y nada al pasar de HTTPS a HTTP. Es lo que hacen los navegadores actuales cuando falta la cabecera, pero enviarla deja la decisión explícita. Usa no-referrer en páginas con datos privados en la URL y evita unsafe-url, que lo envía todo a todas partes.

Permissions-Policy

Permissions-Policy, antes Feature-Policy, desactiva las funciones del navegador que tu web no usa, tanto para la página como para cada frame incrustado en ella. Una lista vacía, (), significa que nadie puede usar esa función:

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()

Deja fuera las funciones que sí usas, o permítelas para tu propio origen con (self), por ejemplo payment=(self) en una tienda con un botón de pago.

Cabeceras que puedes omitir

X-XSS-Protection controlaba un filtro que los navegadores actuales han eliminado. Omítela o envía 0.

Expect-CT y Public-Key-Pins están obsoletas. El pinning podía dejar a los visitantes sin acceso a una web, y por eso los navegadores lo abandonaron.

Server y X-Powered-By no son cabeceras de seguridad, pero los números de versión exactos que contienen ayudan a los atacantes a elegir un exploit. Recórtalas donde tu servidor lo permita.

Dónde configurarlas

Configura las cabeceras en un solo sitio, lo más cerca posible del borde: el servidor web, la CDN o el middleware de la aplicación. En nginx y Apache:

# nginx (in the server block)
add_header Strict-Transport-Security «max-age=31536000» 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;

# Apache (.htaccess or virtual host, with mod_headers)
Header always set X-Content-Type-Options «nosniff»
Header always set Referrer-Policy «strict-origin-when-cross-origin»

Hay dos detalles de nginx que pillan a mucha gente. Sin always, las cabeceras solo se añaden a las respuestas correctas, no a las páginas de error. Y un bloque location con cualquier add_header propio descarta todos los del bloque server, así que una simple cabecera de caché para imágenes puede eliminar sin avisar tus cabeceras de seguridad en esa ruta. Vigila también que la misma cabecera no se configure dos veces, en el servidor y en la aplicación o la CDN: los navegadores tratan los duplicados de forma distinta según la cabecera, así que elige un único responsable para cada una.

Cómo comprobar tus cabeceras

Desde un terminal, curl -sI https://example.com muestra las cabeceras de respuesta. Comprueba también la dirección final tras las redirecciones, una página de error y un archivo estático, ya que cada uno puede servirlo una parte distinta de tu stack. En el navegador, la pestaña Red de las herramientas para desarrolladores muestra las cabeceras de cada petición, y la consola muestra las infracciones de la CSP.

O usa nuestro comprobador de cabeceras HTTP gratuito. Enumera todas las cabeceras que envía una URL tras seguir las redirecciones, marca cuáles de las seis cabeceras de seguridad faltan, señala algunos valores que las anulan, como un max-age de HSTS corto, una CSP que solo está en Report-Only o unsafe-url, y muestra cómo se cachea la página y en qué se va el tiempo. Comprueba que las cabeceras estén presentes; no evalúa lo que permite una Content Security Policy, así que eso revísalo tú.

Evita que desaparezcan

Las cabeceras de seguridad rara vez fallan de forma ruidosa. Desaparecen cuando una web se traslada a un servidor nuevo, se cambia de CDN, se actualiza un plugin o alguien añade un bloque location, y la web sigue funcionando, así que nadie se da cuenta. Un monitor HTTP de la monitorización de uptime de Uptimecheck puede comprobar las cabeceras de respuesta en cada petición, con hasta 10 comprobaciones de respuesta por monitor, por ejemplo:

Strict-Transport-Security existe.

X-Content-Type-Options es igual a nosniff.

Content-Security-Policy contiene frame-ancestors.

Todas las comprobaciones deben cumplirse para que el monitor cuente como en línea, así que si falta una cabecera, el monitor pasa a caído, una segunda comprobación lo confirma y recibes una alerta. Pon las comprobaciones de cabeceras en un monitor aparte si no quieres que cuenten para el uptime de la página. Las comprobaciones se ejecutan hasta cada minuto desde Estocolmo, Londres y Nueva York; consulta cómo monitorizar un endpoint de API para configurar las comprobaciones de respuesta y la checklist de mantenimiento web para el resto de la rutina.

Entérate el mismo día en que falte una cabecera. Prueba Uptimecheck gratis durante 14 días, sin tarjeta de crédito.

Preguntas

¿Qué cabeceras de seguridad HTTP debería enviar toda web?

Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options o una directiva CSP frame-ancestors, Referrer-Policy y Permissions-Policy son fáciles de añadir. Content-Security-Policy es la que más protege, pero hay que probarla antes en modo Report-Only.

¿Qué es Content-Security-Policy-Report-Only?

La misma política que Content-Security-Policy, pero el navegador solo informa de lo que habría bloqueado en lugar de bloquearlo. Úsala para descubrir qué orígenes necesita de verdad tu web antes de aplicar la política.

¿Sigo necesitando X-Frame-Options si uso frame-ancestors?

Los navegadores actuales siguen frame-ancestors e ignoran X-Frame-Options cuando están las dos. Enviar ambas no cuesta nada y protege a los visitantes con navegadores antiguos.

¿Sigue haciendo falta X-XSS-Protection?

No. El filtro que controlaba se ha eliminado de los navegadores actuales. Omite la cabecera o envía 0, y confía en una Content Security Policy.

Sigue leyendo

Guides 6 min de lectura

Presupuesto de errores: qué es y cómo usarlo

Cómo calcular un presupuesto de errores a partir de un SLO, qué lo consume, cómo funciona el burn rate y cómo redactar una política de presupuesto de errores que tu equipo cumpla de verdad.

Leer

Las caídas son cosa del pasado.

Configura tu primer monitor en menos de un minuto, o echa antes un vistazo a la demo en vivo.

14 días de prueba gratis · Después, gratis con 5 monitores · Sin tarjeta de crédito