Sitios web rápidos sin sacrificar seguridad

En estas fechas, ya son mayoría los sitios web que utilizan conexión segura con TLS, existiendo certificados gratuitos como los de Let’s Encrypt y los de Buypass.

Sin embargo, la seguridad de los sitios es algo más que proteger la comunicación entre el cliente y el servidor. El mantenimiento preventivo del software que se utiliza para la gestión del contenido del sitio debe ser riguroso, ya que es habitual descubrir vulnerabilidades de vez en cuando.

De estas vulnerabilidades, hay unas en particular que suelen suceder con mucha frecuencia pero no se le da tanta importancia como pueden ser las inyecciones de SQL. Lograr manipular el contenido de un sitio a partir de otro para extraer información o manipular formularios pueden ser dañinos si se realizan correctamente, como pueden ser los de tipo XSS y CSRF.

Por ejemplo, el código JavaScript adicional que viene incluido con el marco de trabajo Bootstrap ha tenido numerosas correcciones para mitigar ataques XSS en los últimos años, a pesar de que la cantidad de JavaScript que contiene es relativamente pequeña. Si miramos otras librerías JavaScript más grandes, el riesgo es posiblemente mayor.

Afortunadamente existen mecanismos para mitigar este tipo de ataques, sin embargo no son todavía muchos sitios web los que aplican estas medidas, ya que puede requerir modificar gran cantidad de código del lado del cliente (HTML, CSS y JavaScript) para que sea compatible con este tipo de prevención de ataques. Los navegadores web modernos permiten evitarlo, pero hay que indicarlo explícitamente por motivos de compatibilidad con el sitio.

Existen diversas propuestas para mitigar los ataques XSS mediante cabeceras de seguridad mediante protocolo HTTP. Por ejemplo, el observatorio web de Mozilla permite analizar qué cabeceras se pueden utilizar y si están utilizándose para mitigar potenciales ataques. Una de las más importantes es Content Security Policy, que puede evitar inyecciones de scripts si estos no vienen definidos en el documento previamente. Es decir, se puede evitar inyectar por ejemplo JavaScript si no viene de una fuente autorizada. Hay unas cuantas cabeceras más que permiten otro tipo de técnicas como clickjacking y otro tipo de ataques. En ese sitio web viene una descripción de cada uno.

Otra medida que puede emplearse en los navegadores modernos es el uso de cookies tipo SameSite. Por ejemplo, si se utilizan cookies de sesión, a partir de PHP 7.3 se puede definir que estas sean de este tipo para mitigar ataques CSRF en formularios sin necesidad de tener que rotar un token como era la mitigación del problema hasta ahora.

Es conveniente mencionar que algunas de estas cabeceras tienen bastantes años de existir, por lo que no debería ser un pretexto no implementarlas y hacer uso de tecnología web que no arrastre demasiada deuda técnica como para que impida hacer uso de las mismas, sobre todo en nuevos proyectos.

La velocidad no se sacrifica

Puede parecer que utilizar medidas como esta impediría aprovechar las supuestas ventajas de emplear redes de entrega de contenidos (CDN), que al menos parece que permiten acelerar el tiempo de carga.

En las cabeceras que cargan scripts que son de otros dominios, puede resultar un riesgo, en caso de que servicios de terceros se comprometan, de servir código que pudiera ser maligno. Y como no es la primera vez que sucede, existen soluciones como agregar una suma de comprobación (hash) o agregar nonces para evitar cargar scripts que hayan sido modificados. Tener que manejar estos aspectos podría complicar todavía más el desarrollo web.

Pero sobre el uso de servicios de terceros y CDN en general, habría que reflexionar por qué comenzaron a utilizarse. Hasta hace unos años, el protocolo HTTP/1.1 tenía limitaciones para cargar múltiples recursos a la vez, ya que cada petición utilizaba una conexión TCP. Aunque el HTTP pipelining permitió mitigar el problema, no era la mejor solución porque también estaba limitada a un número de conexiones TCP. Sin embargo, hoy en día hay una mucho mejor, especialmente desde el momento en el que las conexiones seguras entraron en escena.

Muchas personas pueden reconocen que tener que usar conexión HTTPS segura con TLS (HTTPS) es para evitar que aparezcan mensajes en los navegadores que su formulario o su dirección es insegura, pero HTTPS también es necesario para utilizar cada vez más características del navegador, como por ejemplo acceso a la geolocalización o incluso algunos dominios de primer nivel como los .app o .dev que cualquiera puede registrar, el navegador exige comunicación HTTPS de forma estricta.

Existe una nueva especificación llamada HTTP/2 que permite resolver de golpe todas las limitaciones de HTTP/1.1. Se utiliza una única conexión TCP para la transmisión de los datos. Hace unos cuantos años que servidores web como Apache HTTPd soportan esta tecnología. La mayoría de navegadores modernos han decidido que para poder disfrutar de HTTP/2 se requiere HTTPS.

Volviendo al tema de los CDN, una de las características de estos servicios es que han sido de los primeros en ofrecer HTTP/2 pero por una buena razón: el contenido se sirve mucho más rápido y por tanto aparenta competir mejor que si se sirve desde el propio sitio web, ya que todavía hay mucha gente que no ha considerado en servir sus sitios web con HTTP/2. En tiempos de HTTP/1.1 mejoraban el rendimiento, incluso sin que estos usaran HTTP/2, pero ya no es así.

Si un sitio web decide usar HTTP/2 para servir la información, podría incluso ser más rápido que utilizando los CDN gracias a esto. El motivo es porque los CDN por HTTP/2 requieren usar HTTPS, lo que supone una negociación TLS completa, aparte del tiempo de resolución de cada uno de los nombres de dominio. Aunque existen técnicas como DNS prefecthing, el conjunto de negociaciones va a ser más costoso que realizarlo desde un mismo sitio, salvo excepciones en sitios web que sean realmente muy grandes donde distribuir la carga merezca la pena, aunque sea por unos pocos milisegundos. Si a esto le sumamos el tiempo ahorrado en configurar la seguridad del sitio para mitigar ataques, creo que es un aspecto relevante a tener en cuenta.

Otra de las características de HTTP/2 además de dejar bastante obsoletos los CDN, también deja obsoletos los empaquetadores de recursos (JavaScript, CSS). Estos empaquetadores, como pueden ser Webpack y otros, complican y ralentizan el despliegue, además de tener que administrar una configuración monstruosa y también requiere trabajo adicional de descarga y de velocidad si hay que depurar errores. Además, evitan que el cache de los navegadores sea útil si hay cambios en los mismos, ya que habría que volver a descargar un enorme paquete si cambia una sola línea de un script, mientras que disponibles de forma individual no lo requeriría.

HTTP/2 permite también utilizar un mecanismo llamado server PUSH para enviar al navegador contenidos antes de que se los pida. Esto permite acelerar en algunos sitios web el tiempo carga de forma muy significativa. Con HTTP/1.1 lo más parecido era el preload en etiquetas link, pero tenía que descargarse primero el documento HTML para poder saber que se tenían que descargar, perdiendo un ciclo valioso que se multiplica en conexiones con latencia elevada. En el caso de Costa Rica hay mucho tráfico que viene desde fuera incluso tráfico local rebotado con latencia significativa, por lo que reducir los round trips puede reducir varias veces el tiempo de carga en estos casos. Con PUSH puede enviarse la información a Early Hints, que se sirven incluso antes de comenzar a transmitir el documento solicitado (antes del HTTP 200).

Es conveniente servir con compresión (aunque teniendo cuidado con ataques tipo CRIME). Mucha gente utiliza solamente compresión GZIP, sin embargo también existe Brotli, que permite ahorrar un porcentaje significativo de tráfico en las descargas y por tanto cargar más rápidamente. Otras formas de ahorro de ancho de banda en la conexión segura inicial es utilizar certificados de curva elíptica (ECC), soportado por todos los navegadores modernos y permitiendo descargar certificados más pequeños, además pueden aliviar la carga del servidor al cifrar la información sin necesidad de aceleración por hardware. Utilizar versiones del protocolo TLS más modernas (1.3) también permiten utilizar cifrados más eficientes y seguros. Por compatibilidad con sistemas antiguos se pueden ofrecer ambos certificados (ECC + RSA).

Otro aspecto que implica una dependencia de terceros es la consulta de estado de revocación del certificado TLS. Esta consulta se realiza a los servidores OCSP de la autoridad de certificación, que podría tener momentos en los que funcionara más lento de lo habitual (como le podría suceder a un CDN), además de que implicaría una consulta adicional. Adicionalmente, las autoridades de certificación sabrían cuánto tráfico tendría tu sitio web y de dónde provienen sus visitantes, por lo que también, como en el caso de los CDN, tiene implicaciones de privacidad. Para mitigarlo existe el grapado OCSP, que es un mecanismo por el cual el estado de revocación se envía directamente desde el sitio web sin depender del tercero. Este grapado es un mecanismo que queda guardado en memoria cache del servidor web y contiene una consulta firmada digitalmente con sello de tiempo que garantiza que el estado de revocación del certificado era válido, teniendo un tiempo de vida relativamente corto, por lo que es el servidor web el que va renovando y cacheando estas consultas. Con esto se mejora también muy ligeramente el tiempo de negociación de la conexión HTTPS.

Recomendaciones

En resumen, algunas recomendaciones para el desarrollo web moderno, logrando seguridad, velocidad y un desarrollo más eficiente:

  • No utilizar servicios CDN si no es necesario.
  • Utilizar cabeceras de seguridad (CSP, HSTS y demás).
  • Utilizar grapado OCSP.
  • Utilizar cookies SameSite (si el sitio usa cookies).
  • Utilizar HTTP/2.
  • Utilizar server PUSH (si es con Early Hints, mejor).
  • Utilizar compresión Brotli.
  • Utilizar TLS 1.3.
  • Utilizar certificados ECC (o dual ECC + RSA).

Este sitio web hace uso de todas estas características y probablemente se note en el tiempo inicial de carga, tanto en computadoras de escritorio como en dispositivos móviles en redes fijas y celulares. Recomiendo utilizar Apache HTTPd porque es el primer servidor HTTP en cumplir al 100% con la especificación HTTP/2 (y no, no es más lento que nginx, esto lo explicaré en otro artículo de blog).