Saltar al contenido

Cabeceras de seguridad HTTP: CSP, HSTS y compañía sin dolor de cabeza

    Cabeceras de seguridad HTTP:

    CSP, HSTS y compañía sin dolor de cabeza

    Privacidad y seguridad web  |  unfantasmaenelsistema.com

    Cuando un navegador pide una página, el servidor no solo devuelve el HTML: adjunta una lista de cabeceras HTTP con instrucciones sobre cómo tratar esa respuesta. Un puñado de esas cabeceras existe solo para reforzar la seguridad —le dicen al navegador qué está permitido y qué no—, y activarlas suele ser cuestión de añadir unas pocas líneas a la configuración del servidor. El problema es que sus nombres asustan (Content-Security-Policy, Strict-Transport-Security) y la documentación oficial tiende a ser un muro de directivas.

    Este artículo recorre las cabeceras de seguridad que de verdad importan hoy: qué ataque frena cada una, qué valor poner, y cómo configurarlas en Nginx y en Apache sin romper el sitio. Es un complemento natural de nuestro artículo sobre Burp Suite: allí se ven los ataques desde el lado ofensivo, aquí se cierran desde el lado del servidor.

    Qué aprenderás en este artículo

    Qué hace cada cabecera de seguridad y contra qué protege · Cómo construir una Content-Security-Policy sin bloquear tu propio sitio · Por qué HSTS necesita cuidado (y qué es la lista de precarga) · Los atributos de cookie que convierten una sesión robable en una sesión protegida · Bloques de configuración listos para Nginx y Apache, y cómo verificar el resultado

    1. Por qué el navegador necesita que le des permiso

    El modelo de seguridad del navegador es permisivo por defecto por motivos históricos: carga scripts de donde le digan, permite que la página se incruste en un iframe ajeno, envía cookies en casi cualquier petición. Muchos ataques web —desde el robo de sesión por XSS hasta el clickjacking— se aprovechan justo de esa permisividad.

    Las cabeceras de seguridad son la forma que tiene el servidor de decirle al navegador «para este sitio, endurece las reglas». No sustituyen a programar de forma segura, pero sí actúan como una segunda capa: si un fallo de la aplicación deja colar un script inyectado, una CSP bien puesta puede impedir que ese script llegue a ejecutarse o a filtrar datos.

    Regla de oro antes de tocar nada

    Aplica estas cabeceras en un entorno de pruebas primero · CSP y HSTS pueden dejar un sitio aparentemente roto (recursos que no cargan, imposibilidad de volver a HTTP) si se configuran a ciegas · Casi todas tienen un modo «solo reportar» o un despliegue gradual: úsalo

    2. Tabla de referencia rápida

    CabeceraQué ataque frenaValor recomendado para empezar
    Strict-Transport-SecurityDowngrade a HTTP, SSL stripping en la primera visita repetidamax-age=31536000; includeSubDomains
    Content-Security-PolicyXSS, inyección de recursos, exfiltración de datosdefault-src ‘self’; object-src ‘none’; frame-ancestors ‘none’
    X-Content-Type-OptionsMIME sniffing (que el navegador ejecute como script algo que no lo es)nosniff
    X-Frame-OptionsClickjacking (versión heredada, para navegadores viejos)DENY
    Referrer-PolicyFuga de URLs internas y tokens en el encabezado Refererstrict-origin-when-cross-origin
    Permissions-PolicyUso no deseado de cámara, micrófono, geolocalización, etc.camera=(), microphone=(), geolocation=()
    Cross-Origin-Opener-PolicyAtaques de canal lateral entre ventanas (Spectre), tabnabbingsame-origin

    El resto del artículo desarrolla las que tienen más matices. Las tres últimas de la tabla (X-Content-Type-Options, Referrer-Policy y Permissions-Policy) se pueden poner tal cual y olvidarse; las demás merecen un párrafo.

    3. HSTS: obligar a HTTPS de forma permanente

    Strict-Transport-Security le dice al navegador «a partir de ahora, para este dominio, conéctate siempre por HTTPS aunque el usuario escriba http:// o siga un enlace inseguro». El navegador recuerda esa instrucción durante el tiempo que marque max-age (en segundos) y ni siquiera intenta la conexión insegura: la reescribe a HTTPS antes de salir.

    3.1 Las directivas

    • max-age=31536000: cuánto tiempo (aquí, un año) el navegador recuerda la política. Empieza con un valor bajo, como 300, mientras pruebas
    • includeSubDomains: aplica la regla a todos los subdominios. No lo añadas hasta estar seguro de que TODOS tus subdominios sirven HTTPS correctamente
    • preload: solicita la inclusión en la lista de precarga que los navegadores traen de fábrica

    HSTS no se puede deshacer con rapidez

    Si activas HSTS con max-age de un año y luego tienes un problema con el certificado, los visitantes que ya recibieron la cabecera NO podrán acceder al sitio hasta que lo arregles: el navegador se niega a conectar por HTTP y no hay forma de saltárselo · La directiva preload es prácticamente permanente: salir de la lista de precarga tarda meses en propagarse. No la uses salvo que HTTPS sea definitivo en todo el dominio y subdominios

    4. Content-Security-Policy: la más potente y la más temida

    CSP es una lista blanca de orígenes desde los que el navegador tiene permiso para cargar cada tipo de recurso: scripts, hojas de estilo, imágenes, fuentes, marcos, conexiones. Todo lo que no esté en la lista se bloquea. Bien afinada, es la defensa más eficaz contra el XSS: aunque un atacante consiga inyectar una etiqueta <script>, si su código intenta cargarse desde un dominio ajeno o ejecutarse en línea, la CSP lo corta.

    4.1 Directivas más habituales

    DirectivaControla
    default-srcValor por defecto para el resto de directivas que no especifiques
    script-srcDe dónde se pueden cargar y ejecutar scripts
    style-srcHojas de estilo y estilos en línea
    img-srcImágenes
    connect-srcDestinos de fetch, XHR, WebSocket, EventSource
    font-srcFuentes web
    frame-ancestorsQuién puede incrustar TU página en un iframe (sustituye a X-Frame-Options)
    object-srcPlugins: <object>, <embed>. Casi siempre debe ser ‘none’
    base-uriRestringe la etiqueta <base>, que un atacante podría usar para redirigir rutas relativas

    4.2 Valores de origen que más se usan

    • ‘self’: el mismo origen (esquema + dominio + puerto) que la página
    • ‘none’: no se permite ningún origen
    • ‘unsafe-inline’: permite scripts y estilos en línea — evítalo en script-src, es la puerta que la CSP intenta cerrar
    • ‘nonce-XXXX’: permite solo las etiquetas script que lleven ese número aleatorio de un solo uso, generado por petición
    • ‘strict-dynamic’: confía en los scripts cargados por un script ya autorizado; simplifica CSP en sitios con mucho JavaScript

    4.3 Despliegue sin romper nada: primero, modo reporte

    En lugar de Content-Security-Policy, envía Content-Security-Policy-Report-Only. El navegador no bloquea nada: solo registra en la consola (y, si configuras un destino con report-to, envía a un endpoint) todo lo que la política habría bloqueado. Dejas eso unos días, revisas los informes, ajustas la lista blanca hasta que no haya falsos positivos, y entonces cambias la cabecera al modo que sí aplica.

    # Fase 1: observar sin bloquear
    Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
     
    # Fase 2: politica estricta con nonce (el valor cambia en cada respuesta)
    Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-r4nd0m'; \
      object-src 'none'; base-uri 'self'; frame-ancestors 'none'

    5. Clickjacking: frame-ancestors y X-Frame-Options

    El clickjacking consiste en cargar tu sitio en un iframe transparente sobre otra página, de forma que la víctima crea que pulsa un botón inocente cuando en realidad está pulsando algo en tu sitio (donde tiene la sesión iniciada). La defensa es impedir que tu página se incruste donde no quieras.

    • Content-Security-Policy: frame-ancestors ‘none’ — método moderno y preferido. Usa ‘self’ si necesitas incrustarte en tu propio dominio, o lista dominios concretos
    • X-Frame-Options: DENY — método heredado. Solo entiende DENY, SAMEORIGIN y (mal soportado) ALLOW-FROM. Mantenlo por navegadores antiguos, pero frame-ancestors manda cuando ambos están

    6. Cookies: Secure, HttpOnly y SameSite

    No son cabeceras de respuesta independientes, sino atributos que se añaden a la cabecera Set-Cookie, y son de lo más rentable que puedes tocar: convierten una cookie de sesión trivialmente robable en una mucho mejor protegida.

    AtributoEfectoCuándo usarlo
    SecureLa cookie solo se envía por HTTPS, nunca en claroSiempre, en cualquier cookie de un sitio con HTTPS
    HttpOnlyJavaScript no puede leer la cookie (document.cookie no la ve)Siempre en cookies de sesión: neutraliza el robo por XSS
    SameSite=LaxLa cookie no viaja en peticiones cross-site, salvo navegación de nivel superiorValor por defecto razonable, mitiga CSRF
    SameSite=StrictLa cookie nunca viaja en peticiones cross-siteCookies muy sensibles, si no rompe el flujo de login
    SameSite=NoneSe permite cross-site (obliga a Secure)Solo si de verdad necesitas la cookie en contextos de terceros
    Set-Cookie: session=a1b2c3; Path=/; Secure; HttpOnly; SameSite=Lax

    7. Configuración en Nginx y Apache

    7.1 Nginx

    # En el bloque server { } (o dentro de un include comun)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Content-Security-Policy "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
     
    # El parametro 'always' es importante: sin el, la cabecera no se anade
    # en respuestas de error (404, 500), justo donde tambien hace falta.

    Cuidado con add_header y la herencia en Nginx

    Si defines add_header en un bloque hijo (location), Nginx DESCARTA todos los add_header heredados del bloque padre · O centralizas todas las cabeceras en el nivel más alto, o las repites en cada location que use su propio add_header

    7.2 Apache

    # Requiere: a2enmod headers
    # En el VirtualHost o en un .htaccess
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "DENY"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"

    8. Verificar el resultado

    • securityheaders.com — escáner rápido de Scott Helme: puntúa de A+ a F y explica cada cabecera ausente
    • Mozilla Observatory (observatory.mozilla.org) — análisis más completo, incluye TLS y cookies, con recomendaciones concretas
    • csp-evaluator.withgoogle.com — específico para auditar si tu Content-Security-Policy tiene huecos conocidos (dominios que permiten bypass, ‘unsafe-inline’ olvidado)
    • curl -I https://tu-sitio — comprobación manual de las cabeceras que devuelve el servidor
    curl -sI https://unfantasmaenelsistema.com | grep -iE 'content-security|strict-transport|x-frame|x-content|referrer|permissions'

    No persigas la A+ a ciegas

    La puntuación de un escáner es una guía, no el objetivo · Una CSP que puntúa alto pero incluye ‘unsafe-inline’ porque no te atreviste a quitarlo protege menos que una CSP intermedia bien entendida · Prioriza: HTTPS + HSTS, cookies con HttpOnly/Secure/SameSite, y una CSP real (aunque empiece siendo modesta) por encima de rellenar cabeceras cosméticas

    Conclusión

    Las cabeceras de seguridad HTTP son de las mejoras con mejor relación esfuerzo/beneficio en toda la seguridad web: unas líneas en la configuración del servidor y ganas una capa de defensa que actúa incluso cuando la aplicación falla. Las de bajo riesgo —X-Content-Type-Options, Referrer-Policy, Permissions-Policy, los atributos de cookie— se ponen hoy y no se vuelven a tocar.

    Las dos que exigen cabeza son HSTS y CSP. HSTS, porque es difícil de revertir: despliégala con max-age corto y ve subiéndolo. CSP, porque puede romper el sitio: empléala primero en modo solo-reporte, lee los informes, y endurécela por fases hasta llegar a una política estricta con nonces. Hecho así, el dolor de cabeza del título no llega a aparecer.

    Referencias

    MDN Web Docs — referencia de cada cabecera HTTP (developer.mozilla.org) · OWASP Secure Headers Project — owasp.org/www-project-secure-headers · securityheaders.com y observatory.mozilla.org — escáneres online · content-security-policy.com — guía práctica de CSP directiva por directiva · Nuestro artículo sobre Burp Suite, para ver estos ataques desde el lado ofensivo

    unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)

    Resumen de privacidad

    Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.