Pasar al contenido principal

Cómo mejorar los encabezados en un sitio Drupal usando el analizador de encabezados HTTP de Dries.

En un artículo anterior se habló sobre la importancia de las cabeceras HTTP y la seguridad web evitando los tecnicismos. En este artículo se quiere profundizar en todos los detalles técnicos. 

Hace un tiempo se encontró el Analizador de Cabeceras HTTP de Dries Buytaert. Esta herramienta, como su nombre indica, analiza las cabeceras de la respuesta HTTP de un sitio web. Existen otros analizadores de cabeceras, pero este, publicado por el creador de Drupal, también tiene en cuenta cabeceras específicas de Drupal. La herramienta muestra un informe de todas las cabeceras encontradas, junto con una explicación del propósito de la cabecera y notas sobre sus valores, incluyendo a veces recomendaciones para valores mejores. También muestra información sobre cabeceras faltantes que deberían estar presentes. 

Con toda esta información, la herramienta otorga una puntuación basada en las cabeceras faltantes, advertencias y avisos detectados durante el análisis. En las primeras ejecuciones en el sitio web, se ha de admitir que la puntuación no era mala, pero tampoco buena: 6/10. Ahora se está encantado de decir que se obtiene una puntuación de 10/10 en la página de inicio. Desafortunadamente, no todas las páginas pueden obtener la puntuación máxima, como se explica más adelante.

A score of 10/10 for https://www.metadrop.net/en

Score using the HTTP Headers Analyzes by Dries Buytaert.

Puntuación 10/10 en la página de inicio de Metadrop con solo un aviso.

Esta herramienta ayudó a comprender mejor las cabeceras HTTP, algo que no es nada fácil. Se explicará cómo se obtuvo esta puntuación y se detallarán algunas de las cabeceras implicadas.

Se han agrupado las cabeceras según la funcionalidad que proporcionan o con la que están relacionadas. La clasificación no es estricta; si se desea una clasificación adecuada, se puede consultar la documentación de Mozilla sobre cabeceras HTTP.

Cabeceras de caché

Cabeceras relacionadas con el almacenamiento en caché y el comportamiento de las solicitudes según el estado de cambio de los recursos.

Cabecera Expires

La cabecera HTTP Expire se introdujo en la primera especificación HTTP, HTTP/1.0, y controla la fecha de caducidad de un contenido (¡qué sorpresa!), proporcionando una fecha exacta. Aunque esta cabecera todavía se utiliza, se recomienda usar la cabecera Cache-Control ya que proporciona un mayor control sobre el almacenamiento en caché. 

Drupal proporciona una cabecera Expires con la fecha de nacimiento de Dries, que obviamente está en el pasado. ¿Por qué ocurre esto? Es un truco para tratar con dispositivos antiguos. Los dispositivos HTTP/1.0 no soportan la cabecera Vary. Esto puede ser crítico porque Drupal puede enviar diferentes respuestas a la misma URL y depende de la cabecera Vary para evitar problemas de caché. Esto significa que un dispositivo HTTP/1.0 podría almacenar en caché una respuesta independientemente de la cabecera Vary. Para evitarlo, se establece la cabecera Expires: un dispositivo HTTP/1.0 ve una cabecera Expires en el pasado y, por lo tanto, no almacena la respuesta en caché, mientras que un dispositivo moderno ignorará la cabecera Expires porque procesa la cabecera Cache-Control, que siempre prevalece sobre la cabecera Expires si está presente.

Curiosamente, la propia herramienta de Dries considera que la cabecera Expires debe eliminarse. Como está codificada en el núcleo de Drupal, se usó la configuración del servidor web para eliminar la cabecera Expires.

Cabecera Last-Modified

La cabecera Last-Modified proporciona la fecha en que el recurso en una URL se modificó por última vez. Los clientes la utilizan para determinar si el recurso almacenado (si el cliente ya tiene almacenado el recurso de esa URL) es el mismo que el recurso actual. Esta cabecera está relacionada con las cabeceras de solicitud If-Modified-Since e If-Unmodified-Since porque debe incluirse en las respuestas a solicitudes con esas cabeceras. Estas cabeceras se utilizan para hacer condicionales las solicitudes. Por ejemplo, una solicitud con la cabecera If-Modified-Since recibirá una respuesta con el estado 200 OK si el recurso ha sido modificado después de la fecha proporcionada, o un 304 No modificado si no lo ha sido.

Sin embargo, esta cabecera se considera menos precisa que la cabecera ETag, que proporciona un identificador único para cada versión específica de un recurso en una URL. Los clientes no necesitan saber cuándo se modificó el recurso, solo necesitan saber si es diferente o no, y la ETag proporciona esta información de manera mucho más sencilla, ya que no es necesario analizar fechas.

Por eso, la herramienta de Dries sugiere eliminarla si hay una ETag presente, algo que ocurre en cada Drupal, ya que la ETag la proporciona el núcleo. De nuevo, esto implica eliminarla usando una capa externa como el servidor web o la capa de proxy.

Cabecera X-Drupal-Cache

Esta cabecera es específica de Drupal y simplemente informa si la respuesta se obtuvo de la caché gestionada por el módulo Internal Page Cache del núcleo de Drupal. Aunque la herramienta solo informa si la respuesta se almacenó en caché o no, se considera interesante conocerlo. Un valor HIT significa que se recuperó de la caché, un MISS indica un fallo de caché.

Este módulo hace el mismo trabajo que cualquier capa de caché externa como Varnish o una CDN. Si se dispone de dicha capa, se puede deshabilitar (y se debería, porque se está sobrecargando el backend de caché con datos que no se utilizan, ya que ya los proporciona la capa de caché externa).

Cabecera X-Drupal-Dynamic-Cache

Al igual que la cabecera anterior, esta está relacionada con el almacenamiento en caché de Drupal. En este caso, la caché es gestionada por el módulo Dynamic Page Cache del núcleo de Drupal. Este módulo almacena en caché las partes de la respuesta (se podría decir fragmentos del HTML servido) que son iguales para todos los usuarios. Es automático y su magia proviene del potente mecanismo de caché que utiliza Drupal, basado en Cache Tags y Cache Contexts

De nuevo, la herramienta no espera un valor específico, pero es interesante conocerlo. Un valor UNCACHEABLE significa que ningún fragmento se pudo almacenar en caché y probablemente se debería revisar la página. Normalmente, esto ocurre cuando ciertas funcionalidades como CAPTCHA prohíben el almacenamiento en caché porque no funcionan con páginas cacheadas.

La incidencia Mejorar las cabeceras X-Drupal-Cache y X-Drupal-Dynamic-Cache, incluso para respuestas que no se pueden almacenar en caché añade más información a esta cabecera y se fusionó en octubre de 2024, y se lanzará con Drupal 10.4. Ahora será más fácil saber por qué la página no se puede almacenar en caché porque la cabecera puede tener tres valores diferentes: UNCACHEABLE (cacheabilidad pobre), UNCACHEABLE (sin cacheabilidad) y UNCACHEABLE (política).

Cabeceras de seguridad

Las cabeceras de seguridad son la parte complicada porque una mala configuración puede romper la funcionalidad del sitio o aumentar la superficie de ataque.

Estas cabeceras son el mecanismo que utilizan los sitios para hacer más segura la visita del usuario. Y algunos podrían pensar, ¿qué sentido tiene? Si los sitios son legítimos, no debería haber problema, y si los sitios dudosos no usan estas cabeceras porque, bueno, son dudosos, ¿verdad? Como se explicó en un artículo anterior sobre navegación segura y cabeceras de seguridad, cuando un usuario visita un sitio, se cargan muchos recursos de terceros que están fuera del control del sitio original. El sitio tiene que delegar en otros, por ejemplo para cargar publicidad, medios externos o herramientas de medición de audiencia, y esto significa que en algún momento se podría cargar un script malicioso. O el sitio podría verse comprometido y verse obligado a servir scripts maliciosos a los visitantes. En estos casos, las cabeceras de seguridad ayudan a prevenir cualquier daño y tienen sentido porque esta plétora de terceros se está cargando en el navegador.

Tres conceptos clave a conocer son el significado de cross-origin, same-origin y same-site, ya que se utilizan al definir el comportamiento de muchas cabeceras de seguridad. Dado que un origen es la combinación de esquema, dominio y puerto, una solicitud same-origin es una solicitud desde un origen dado al mismo origen. Cualquier otra cosa es cross-origin. Por ejemplo, una solicitud a desde es same-origin, mientras que una solicitud a desde o domain.com es cross-origin.

Same-site, por otro lado, es un poco más complejo. Depende del dominio de nivel superior y de ciertas reglas, pero es más amplio que same-origin en el sentido de que dos dominios diferentes pueden considerarse el mismo sitio: y se consideran el mismo sitio. 

Strict-Transport-Security

La cabecera Strict-Transport-Security, a veces abreviada como HSTS, es una cabecera de respuesta que indica al navegador que use siempre HTTPS al conectarse al sitio. La parte interesante es que el navegador recuerda esta configuración, por lo que usará HTTPS en el futuro para los sitios que incluyan esta cabecera. Esto evita ataques de degradación (forzar al navegador a usar HTTP en lugar de HTTPS) y el secuestro de cookies (por ejemplo, robar una cookie de sesión que podría usarse para suplantar al usuario).

Dada la omnipresencia actual de las conexiones HTTPS, el uso de esta cabecera es altamente recomendable. La cabecera incluye un max-age que especifica durante cuánto tiempo el navegador debe recordar usar HTTPS.

Esta cabecera se puede configurar en el servidor web o la capa de proxy, o usando el módulo Security Kit de Drupal.

En el caso de Metadrop, se habilitó la cabecera y se estableció un max-age de un año porque el sitio web ha estado funcionando con HTTPS durante años y no se prevé eliminarlo en el futuro, por lo que es correcto obligar a los navegadores a conectarse mediante HTTPS.

Permissions-Policy

La cabecera Permissions-Policy permite a los sitios informar al navegador de las funciones que pueden ser necesarias o que deben estar deshabilitadas. Un claro ejemplo es deshabilitar el uso del micrófono y la cámara web. Si un sitio está seguro de que no ofrece videollamadas u otras funciones que requieran micrófono y cámara web, puede enviar esta cabecera de respuesta indicando que no son necesarias. Si un script malicioso logra cargarse e intenta usar una función no autorizada, el navegador denegará el acceso pase lo que pase.

¿Y qué funcionalidad se puede permitir o denegar con esta cabecera? Bueno, una lista bastante larga, pero las que son estables son:

  • Camera: acceder al dispositivo de cámara
  • Fullscreen: entrar en modo de pantalla completa
  • Geolocation: proporcionar la geolocalización del usuario
  • Microphone: acceder al dispositivo de micrófono
  • Credentials: acceso a la API de autenticación web, utilizada para iniciar sesión en sitios sin contraseña gracias a diferentes métodos como la biometría o los autenticadores de hardware.
    • Wake lock: evita que la pantalla se apague o se atende.
  • Web share: controla el uso de la API WebShare, que proporciona un mecanismo para compartir texto, enlaces, archivos y otro contenido.

Hay muchas otras funcionalidades como el acceso a Bluetooth, acelerómetros, sensores de luz, pagos o USB que pueden ser gestionadas por esta cabecera, pero son experimentales.

Como el sitio no utiliza ninguna de estas funciones, se han bloqueado la mayoría:

permissions-policy: "accelerometer=(), camera=(), geolocation=(), microphone=()"

Supongamos que se desea permitir mensajes de voz además del formulario de contacto. En este caso, se necesitaría permitir el uso del micrófono, usando algo como

permissions-policy: "accelerometer=(), camera=(), geolocation=(), microphone=self"

Eso permitiría que el micrófono fuera utilizado por recursos del mismo origen que el sitio. Una configuración demasiado amplia sería:

permissions-policy: "accelerometer=(), camera=(), geolocation=(), microphone=*"

Esto permitiría que cualquier fuente solicitara usar el micrófono, y podría ser un riesgo potencial de seguridad.

Para establecer esta cabecera, se puede usar el módulo Permissions Policy de Drupal, o configurarla en el servidor web o la capa de proxy.

Referrer-Policy

La cabecera Referrer-Policy controla cuánta información de referente se envía en las solicitudes. Esto es importante para proteger la privacidad del usuario y evitar la filtración de datos si hay información sensible en los parámetros de consulta de las URL del sitio.

Las diferentes opciones van desde enviar siempre toda la información del referente (origen, ruta, parámetros de consulta) hasta no enviar nunca ninguna información del referente. Se puede ajustar el comportamiento según el destino (mismo origen o diferente) y cuando la conexión se degrada (por ejemplo, al conectar desde HTTPS a un recurso HTTP).  

El valor predeterminado, strict-origin-when-cross-origin, indica a los navegadores que envíen el origen, la ruta y el parámetro de consulta en solicitudes same-origin y en solicitudes cross-origin con el mismo nivel de seguridad (por ejemplo, al solicitar un recurso HTTPS desde una URL HTTPS). Este es un valor razonable que funciona para la mayoría de los sitios, y aunque la herramienta de Dries no parece penalizar si falta, sí genera una advertencia si no está presente. Por esta razón, se configuró la cabecera explícitamente en el sitio, usando el valor predeterminado. 

Si se desea configurar esta cabecera, el módulo Security Kit de Drupal proporciona la configuración para hacerlo. O como antes, usar la capa preferida como el servidor web o proxy.

Content-Security-Policy

La cabecera Content-Security-Policy (CSP) ayuda a prevenir ciertos tipos de ataques como Cross-Site Scripting (XSS) o la inyección de datos. Funciona indicando a los navegadores qué dominios son fuentes válidas de recursos. Por ejemplo, puede establecer algunos dominios desde los que se pueden cargar scripts. Todos los demás scripts cargados desde otros dominios no se ejecutarán. También puede marcar todas las cookies con el atributo secure (por lo que las cookies solo se enviarán a través de conexiones HTTPS).

CSP es una cabecera bastante compleja que potencialmente puede romper el sitio (por ejemplo, si se prohíbe cargar un recurso JavaScript crítico). Afortunadamente, las políticas de CSP se pueden implementar en modo solo informe. En este modo, el navegador detectará cualquier infracción pero no la bloqueará, aunque informará de la infracción para una URL determinada. Esto permite probar las políticas de CSP sin romper el sitio, y estar al tanto de cualquier problema que los clientes puedan tener simplemente revisando las notificaciones de CSP.

El módulo Content-Security-Policy de Drupal proporciona una manera de gestionar esta cabecera en un sitio Drupal, incluyendo el manejo del modo de informe y la recepción de notificaciones.

Por cierto, durante la subida a la puntuación de 10/10 se encontró un error en la herramienta de cabeceras: se quejaba de usar la cláusula report-uri obsoleta y pedía usar report-to en su lugar. Sin embargo, report-to no es ampliamente compatible y el uso de ambas cláusulas hacía que la herramienta se quejara de todas formas. Se envió un correo al propio Dries y proporcionó una corrección al día siguiente. Se agradece mucho la rápida respuesta, ¡gracias!

Cross-Origin-Resource-Policy (CORP), Cross-Origin-Embedder-Policy (COEP) y Cross-Origin-Opener-Policy (COOP)

Aquí es donde las cosas se vuelven muy complejas. Hace unos años se descubrieron las vulnerabilidades Spectre y Meltdown. Estos problemas podían permitir que un script malicioso accediera a datos no autorizados utilizando ciertas técnicas integradas en el propio procesador. Esto significa que no era fácil de solucionar porque se originaba en el hardware. El problema se puede mitigar creando contextos aislados para diferentes procesos, ya que los procesos en el mismo contexto podían leer datos de otros. Estas cabeceras ayudan a crear o gestionar el aislamiento de contexto, pero explicar los detalles está fuera del alcance de este artículo. Probablemente no mucha gente entienda completamente estas vulnerabilidades y cómo usar las cabeceras anteriores, por lo que no se pretenderá entenderlas completamente, aunque se desearía. 

Durante el proceso de configuración de estas cabeceras se enfrentó un error en el sitio web de YouTube (al menos eso se cree), mucho ensayo y error y algo de pérdida de cordura. Fue un camino difícil. Debido al error de YouTube, no se puede establecer un valor adecuado en la cabecera Cross-Origin-Embedder-Policy para todas las URL porque los vídeos de YouTube no se renderizarán si está presente. Por lo tanto, se usa el módulo HTTP Response Headers para establecer la cabecera COEP solo en la página principal.

Otro analizador HTTP

Después de obtener la puntuación más alta con la herramienta de Dries, se probó la herramienta Security Headers. El resultado fue bueno, muy bueno:

score-a-security-headers.png

Por eso la herramienta de Dries resulta muy interesante, porque si se hace bien, otras herramientas coincidirán en la puntuación alta, pero también porque está adaptada para Drupal. Encantaría ver esta herramienta disponible como API o publicada como software independiente para incluirla en el conjunto de pruebas automatizadas. Hasta entonces, se puede inundar la herramienta. Lo sentimos, Dries, esperamos que Acquia te dé un buen precio por el alojamiento, el tráfico puede aumentar 😇

Analizador de cabeceras HTTP para metadrop.net.

  • RIcardo Sanz Ante

    Ricardo Sanz

    CTO