Comprender la integridad de los subrecursos

Publicado: 2022-03-10
Resumen rápido ↬ Cada bit de JavaScript que agregas a un sitio es una forma potencial de acceso para un hacker. Esto es doblemente cierto si ese JavaScript está alojado por otra persona, como en un CDN público. Subresource Integrity es una característica del navegador que puede usar para asegurarse de que el código que se usa es exactamente lo que pretendía.

Si alguna vez usó una versión alojada en CDN de una biblioteca de JavaScript, es posible que haya notado un atributo de integrity de aspecto extraño en la etiqueta del script. Este atributo contiene basura alfanumérica aparentemente interminable que puede verse tentado a eliminar en la búsqueda de un código más limpio.

Toda esa basura es en realidad una característica de seguridad realmente útil llamada Integridad de subrecursos (SRI) que puede ayudar a defender su sitio contra ciertos tipos de ataques y compromisos. En este artículo, veremos qué es SRI, cómo puede ayudarlo a protegerse y cómo puede comenzar a usarlo en sus propios proyectos, no solo para archivos alojados en CDN.

Un poco de historia

Hace mucho tiempo, en los días en que JavaScript era el primo más pobre de HTML y CSS, no necesitábamos pensar demasiado en cómo nuestros scripts podrían usarse como un vector de ataque para nuestros sitios web. La mayoría de los sitios estaban alojados en un solo servidor físico en algún lugar de nuestra propia infraestructura de alojamiento, y fue el servidor que pensamos en defender en lo que respecta a las mejores prácticas de seguridad.

A medida que los navegadores se volvieron más capaces y las conexiones de red se hicieron más pesadas, comenzamos a usar más y más JavaScript y, finalmente, comenzaron a surgir bibliotecas de JavaScript reutilizables. En esos primeros días, bibliotecas como script.aculo.us, Prototype y, finalmente, jQuery comenzaron a ser adoptadas entre los desarrolladores que buscaban agregar más interactividad en sus páginas.

Con estas bibliotecas agregadas y los complementos posteriores, se agregó peso de página y, en poco tiempo, comenzamos a pensar seriamente en el rendimiento del front-end. Los recursos como las redes de entrega de contenido (CDN) que anteriormente habían sido la reserva de las corporaciones gigantes se estaban volviendo comunes para la gente común que crea sitios web ágiles.

¡Más después del salto! Continúe leyendo a continuación ↓

En el camino, una chispa brillante notó que todos los sitios solicitaban sus propias copias de bibliotecas comunes, como el jQuery más reciente, y si hubiera una versión CDN común de esas bibliotecas que todos los sitios pudieran usar, entonces el usuario no lo haría. No es necesario seguir descargando el mismo archivo. Recibirían el golpe para el primer sitio que usara el archivo, pero luego se ubicaría en el caché de su navegador local y las descargas podrían omitirse para cada sitio posterior. ¡Genio!

Es por eso que verá enlaces de CDN para sus bibliotecas favoritas que usan URL como jsdelivr.com : están utilizando un CDN común para alojar los archivos para que sus usuarios vean los beneficios de rendimiento.

¿Qué puede salir mal?

Esta sigue siendo una forma buena y práctica de trabajar, pero introduce un potencial vector de ataque. Imaginemos que es 2012 y todos usan el nuevo jQuery 1.8. Volviendo a la forma tradicional de hacer las cosas, todos tendrían su propio archivo jQuery 1.8 alojado como parte de su propio sitio web en su propio servidor.

Si fuera una especie de actor malvado, como una especie de Hamburglar basado en jQuery, y hubiera descubierto una forma furtiva de piratear la biblioteca para sus propias ganancias malvadas, tendría que apuntar a cada sitio web individualmente y comprometer sus servidores para tener cualquier impacto. Eso es mucho esfuerzo.

Pero no es así como son las cosas ahora, ya que todos usan jQuery cargado desde un CDN común. Y cuando digo todos, no me refiero a cientos de páginas web. Me refiero a millones de páginas web. De repente, ese archivo se ha convertido en un objetivo muy atractivo para nuestro hacker sombrío. Si pueden comprometer ese archivo, pueden tener código ejecutándose muy rápidamente en millones de páginas web en todo el mundo.

No importa cuál sea ese código. Podría ser una broma para desfigurar páginas, podría ser un código para robar sus contraseñas, podría ser un código para extraer criptomonedas o podrían ser rastreadores furtivos para seguirlo por la web y crear un perfil de marketing. Lo importante es que el archivo inocente que el desarrollador agregó a una página se ha cambiado y ahora tienes JavaScript malicioso ejecutándose como parte de tu sitio. Ese es un gran problema.

Ingrese la integridad de los subrecursos

En lugar de hacer retroceder los relojes y abandonar una forma útil de usar el código, SRI es una solución que agrega un nivel simple de seguridad en la parte superior. Lo que hace SRI y el atributo de integrity es asegurarse de que el archivo que vinculó a una página nunca cambie. Y si cambia, el navegador lo rechazará.

Comprobar que el código no ha cambiado es un problema muy antiguo en informática y, afortunadamente, tiene algunas soluciones muy bien establecidas. SRI hace un buen trabajo al adoptar el hash de archivos más simple.

El hashing de archivos es el proceso de tomar un archivo y ejecutarlo a través de un algoritmo que lo reduce a una representación de cadena corta, conocida como hash o suma de verificación. Sin entrar en detalles, el proceso es repetible o reversible, tanto que si le diera a otra persona un archivo junto con el hash, podría ejecutar el mismo algoritmo para verificar que los dos coincidan. Si el archivo cambia o el hash cambia, entonces ya no hay una coincidencia y sabe que algo anda mal y debe desconfiar del archivo.

Cuando usa SRI, su página web contiene el hash y el servidor (CDN o en cualquier lugar) contiene el archivo. El navegador descarga el archivo, luego lo calcula rápidamente para asegurarse de que coincida con el hash en el atributo de integrity . Si coincide se utiliza el archivo, y si no se bloquea.

probandolo

Si voy a getbootstrap.com hoy para obtener un enlace CDN a una versión de Bootstrap, me dan una etiqueta que se ve así:

 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

Puede ver que el atributo src es como estamos acostumbrados, y el atributo de integrity contiene lo que ahora sabemos que es un hash.

El hash es en realidad en dos partes. El primero es un prefijo para declarar qué algoritmo hash usar. En este caso, es sha384 . A esto le sigue un guión y luego el propio hash, codificado con base64 .

Es posible que esté familiarizado con base64 como una forma de codificar archivos en línea como imágenes en páginas. No es un proceso criptográfico, es solo una forma rápida y conveniente de codificar datos potencialmente desordenados de una manera que se traduce perfectamente a ASCII. Es por eso que se usa mucho en la web.

Al ver esto, el navegador descargará bootstrap.min.js . Antes de ejecutarlo, decodificará en base64 el hash y luego usará el algoritmo hash sha384 para confirmar que el hash coincide con el archivo que acaba de descargar. Si coincide, el archivo se ejecuta.

Puedo probar esto colocando esa etiqueta en una página y luego pasando a la pestaña Red en las herramientas de mi navegador para ver que el archivo se ha cargado.

Pestaña Red
(Vista previa grande)

Puedo ver que bootstrap.min.js (y también el archivo jQuery que necesita) se han cargado correctamente.

Veamos qué sucedería si actualizo el hash para que sea algo que sé que es incorrecto.

 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-SmashingMagazineIsCoolForCats" crossorigin="anonymous"></script> 
picadillo
(Vista previa grande)

Como puede ver, el hash que se especifica en mi página ya no coincide con el archivo, por lo que el archivo se bloquea.

Usando SRI en sus propios proyectos

Tener esta capacidad para bibliotecas en un CDN es excelente, y si ve la opción de usar un archivo incrustado con un atributo de integrity , definitivamente debería favorecer esa opción. Pero no se limita a grandes proyectos en CDN, puede usarlo usted mismo para sus propios sitios.

No es nada descabellado imaginar un escenario en el que un pirata informático logra acceder a solo unos pocos archivos en su sitio. Creo que la mayoría de nosotros hemos visto a un cliente, colega o amigo que en algún momento tuvo un sitio de WordPress comprometido con un montón de basura desagradable que ni siquiera se dieron cuenta de que estaba allí.

SRI también puede protegerlo de esto. Si genera hashes de integridad para sus propios archivos, puede hacer que su sitio rechace cualquier cambio tal como lo haría con un archivo alojado de forma remota.

Generación de hashes

Puede, como era de esperar, ejecutar algunos comandos en la terminal de su computadora para generar un hash para un archivo. Este ejemplo de cómo hacerlo proviene de la página Integridad de subrecursos de MDN:

 cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A

Eso es obtener el contenido de FILENAME.js y pasarlo como entrada a openssl para crear un resumen usando sha384 , que luego se pasa como entrada a otro comando openssl para codificar en base64 el resultado. No solo es complicado y oscuro, sino que tampoco es el tipo de cosa que desea hacer a mano cada vez que cambia su archivo JavaScript.

De manera más útil, querrá integrar esto de alguna manera en el proceso de creación de su sitio y, como puede imaginar, hay muchas opciones listas para usar allí. La implementación exacta variará enormemente según su proyecto, pero aquí hay algunos componentes básicos.

Si usa Gulp para construir sus sitios, hay gulp-sri que generará un archivo JSON con una lista de sus archivos y sus hashes. A continuación, puede hacer uso de esto en su sitio. Por ejemplo, para un sitio renderizado dinámicamente, puede crear un complemento de plantilla para leer ese archivo y agregar los hash a sus plantillas donde sea necesario.

Si todavía está con Gulp pero tiene un sitio estático (o un sitio generado estáticamente), puede usar gulp-sri-hash, que en realidad se ejecutará a través de sus páginas HTML y modificará las páginas para agregar hashes donde sea necesario, lo cual es muy útil.

Si está utilizando Webpack, existe la integridad de los subrecursos de la página web que, en el verdadero estilo de Webpack, es más compleja de lo que cualquier humano podría esperar, pero parece funcionar.

Para aquellos que usan el motor de plantillas de Handlebars, parece que hay opciones disponibles para usted, y si su proceso de compilación es solo JavaScript básico, también hay soluciones simples.

Si está utilizando un CMS como WordPress, encontré un complemento que parece facilitarlo, aunque yo mismo no lo he probado. Buscar en Google su propia plataforma de elección con SRI o Sub Resource Integrity probablemente lo guiará en la dirección correcta.

Esencialmente, desea conectar su hash después de que sus archivos JavaScript se hayan minimizado y luego hacer que ese hash esté disponible de alguna manera para cualquier parte de su sistema que genere las etiquetas <script> . Una de las maravillas de la plataforma web es que es tan diversa desde el punto de vista técnico, ¡pero lamentablemente no puedo darte buenas instrucciones de implementación!

Otras cosas a tener en cuenta

En este artículo, he hablado mucho sobre los archivos de JavaScript porque ahí es donde realmente tiene más sentido defenderse de los ataques de piratería. SRI también funciona con CSS, por lo que puede usarlo exactamente de la misma manera allí. El riesgo de CSS malicioso es mucho menor, pero el potencial de desfigurar un sitio aún existe y quién sabe qué errores del navegador también podrían llevar a que el CSS exponga inadvertidamente su sitio a un pirata informático. Así que es trabajo usando SRI allí también.

Otra cosa interesante que puede hacer es usar una Política de seguridad de contenido para especificar que cualquier secuencia de comandos (o estilos) en su página debe usar SRI y, por supuesto, que SRI debe validar.

 Content-Security-Policy: require-sri-for script;

Esta es una manera de garantizar que siempre se use SRI, lo que podría ser útil en sitios en los que trabajan varios miembros del equipo que pueden o no estar completamente al tanto de cómo hacer las cosas. Una vez más, un buen lugar para leer más sobre esto es la siempre excelente documentación de MDN para la integridad de los subrecursos.

Lo último de lo que vale la pena hablar es de la compatibilidad del navegador con SRI. El soporte en los navegadores modernos es amplio, con la principal excepción de Internet Explorer. Sin embargo, debido a la compatibilidad con versiones anteriores de la especificación, es seguro usarla de inmediato. Los navegadores que entienden el atributo de integrity usarán el hash y verificarán la integridad, y los navegadores más antiguos simplemente continuarán como siempre y seguirán funcionando. Por supuesto, no obtendrá la protección adicional en esos navegadores más antiguos, pero sí en los navegadores que ofrecen soporte.

Conclusión

No solo hemos visto lo que hacen esos hash extraños en los atributos de integrity , sino también cómo podemos usarlos para defendernos contra ciertos tipos de ataques en nuestro sitio web. Por supuesto, no existe una panacea que defienda nuestros sitios contra todo tipo de vulnerabilidad, pero Subresource Integrity es una herramienta realmente útil en la cadena.

Explotar una falla de seguridad a menudo consiste en alinear varias piezas pequeñas. Si A está en su lugar y puede hacer que B suceda, entonces un error en C hace que D sea posible. Las características del navegador como SRI nos brindan una buena manera de atar las cosas un poco más y potencialmente romper esa cadena y evitar que un pirata informático obtenga lo que quiere. Además, si puede integrarlo en su proceso de compilación o CMS, es algo que debería poder configurar una vez y luego olvidarse y no le causará inconvenientes diarios.

Como tal, realmente recomendaría echar un vistazo serio a la integridad de los subrecursos e implementarla en sus sitios si puede.