Los usuarios de internet cada vez estamos más preocupados por la seguridad de nuestras actividades en la red.
Los medios de comunicación nos informan de ataques a empresas e instituciones, de delitos cometidos utilizando la web,… y nos alertan de las amenazas que nos acechan en cualquier rincón de la red. Las recomendaciones para navegar seguro, son bien conocidas:
- Tener un antivirus actualizado.
- Visitar páginas https.
- Evitar los enlaces sospechosos y sitios web de dudosa reputación
- Utilizar contraseñas fuertes.
Pero, como creadores de mapas web, ¿podemos hacer algo más para mejorar la seguridad de los mapas en la web?
Veamos un ejemplo utilizando un mapa creado con Leaflet.
Utilizando Leaflet de forma segura
Leaflet es una librería JavaScript ampliamente utilizada en webmapping. La forma de utilizar esta librería es acudiendo a la página de Leaflet y descargando los archivos o alternativamente mediante un CDN (Content Delivery Network), es decir utilizando los archivos hospedados en un servidor.
En la práctica lo que hacemos es llamar a los dos archivos de la librería, de la siguiente forma:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
Sin embargo en la última versión de Leaflet se nos recomienda que para evitar posibles problemas de seguridad, habilitemos la integridad de los subrecursos cuando utilicemos Leaflet desde un CDN.
Esto se traduce que ahora escribiremos nuestro código de la siguiente forma:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js" integrity="sha512-A7vV8IFfih/D732iSSKi20u/ooOfj/AGehOKq0f4vLT1Zr2Y+RX7C+w8A1gaSasGtRUZpF/NZgzSAu4/Gc41Lg==" crossorigin=""></script>
¿Qué es la integridad de los subrecursos?
Subresource Integrity (SRI) es una característica de seguridad que permite a los navegadores verificar que los archivos que buscan (por ejemplo, desde un CDN) se entregan sin manipulación inesperada. Funciona proporcionando un hash criptográfico que tiene que coincidir con el del archivo obtenido.
El uso de redes de distribución de contenido (CDN) para alojar archivos en JavaScript y hojas de estilo que se comparten entre varios sitios puede mejorar el rendimiento del sitio y conservar el ancho de banda. Sin embargo, el uso de CDNs también conlleva un riesgo, ya que si un atacante gana el control de un CDN, puede inyectar contenido malicioso arbitrario en los archivos del CDN (o reemplazar los archivos por completo) y así también atacar potencialmente todos los sitios que buscan archivos de ese CDN.
La característica de Integridad de Sub-recursos nos permite mitigar el riesgo de ataques como este, asegurando que los archivos que la aplicación Web o documentos Web que se obtengan (desde un CDN o en cualquier lugar) han sido entregados sin que un tercero haya inyectado ningún contenido adicional en esos Archivos.
Hash criptográfico
Un hash es una cadena de texto que acompaña al archivo descargado. Cuando el navegador recibe un archivo calcula su hash y lo compara con el que acompaña al archivo. Si ambos son coincidentes lo acepta, en caso contrario lo rechaza. Un valor de integridad comienza con un prefijo que indica un algoritmo de hash particular (sha256- sha384- y sha512- ).
Herramientas para generar hash
Puedes generar hashes SRI desde la línea de comandos con openssl. Para instalarlo en ubuntu 16.04 escribiremos en la línea de comandos los siguiente:
sudo apt-get update sudo apt-get install openssl
Una vez instalado generamos el hash invocando al archivo de la siguiente forma:
cat FILENAME.js | openssl dgst -sha384 -binary | openssl enc -base64 -A
Veamos un ejemplo en que generamos un hash para un script de Leaflet. Nuestro código html es el siguiente:
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>Mi primer Leaflet</title> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" /> <style> #map { width: 700px; height: 600px; } </style> </head> <body> <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script> <div id ="map"> </div> <script src="script.js"></script> </body> </html>
Y el código del script es:
var map = L.map('map', {center: [40.965, -5.664],zoom: 16}); L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map); var myMarker = L.marker([40.965, -5.664]); map.addLayer(myMarker);
Estamos creando un mapa muy sencillo en el que, sobre un mapa base de OpenStreetMap, insertamos un marcador. Siendo el resultado el que se muestra a continuación:
A continuación crearemos el hash para el archivo script.js. Esto se hace escribiendo en la línea de comandos lo siguiente:
cat script.js | openssl dgst -sha384 -binary | openssl enc -base64 -A
El resultado es que nos genera la siguiente cadena:
JxNpUceLr2mD/vWiJ7vFq2mbOZ4zjpFS9Q3j6oTDMOp3MJBFjD1e09o/3GwXUeJH
Ahora añadimos la integridad a nuestro código de la siguiente forma:
<script src="script.js" integrity="sha384-JxNpUceLr2mD/vWiJ7vFq2mbOZ4zjpFS9Q3j6oTDMOp3MJBFjD1e09o/3GwXUeJH" crossorigin="anonymous"> </script>
En el valor de integridad escribimos el prefijo sha384- precediendo a la cadena del hash que obtuvimos. Asignamos el valor anonymus al atributo crossorigin, de esta forma estamos obligando al navegador a que rechace cualquier archivo con una integridad diferente.
Los navegadores manejan SRI haciendo lo siguiente:
- Cuando un navegador encuentra un elemento <script>
o <link>
con un atributo de integridad, antes de ejecutar el script o antes de aplicar cualquier hoja de estilo especificada por el elemento <link>
, el navegador debe comparar primero el script o la hoja de estilo con el hash esperado dado en el valor de integridad .
- Si la secuencia de comandos o la hoja de estilos no coincide con su valor de integridad asociado, el explorador debe rechazar ejecutar la secuencia de comandos o aplicar la hoja de estilo y, en su lugar, devolver un error de red que indica que la recuperación de dicha secuencia de comandos o hoja de estilo ha fallado.
Compruébalo tu mismo
Si has seguido los pasos que hemos descrito hasta el momento puedes comprobar lo que estamos explicando fácilmente. Introduce una modificación cualquiera en script.js. Por ejemplo puedes cambiar el nombre a la variable myMarker, por myMarker1. Deberás cambiar, por supuesto, el nombre en la capa para que tenga consistencia, con lo que el script quedaría de la siguiente forma:
var map = L.map('map', {center: [40.965, -5.664],zoom: 16}); L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map); var myMarker1 = L.marker([40.965, -5.664]); map.addLayer(myMarker1);
Hemos modificado el script, por lo tanto cuando el navegador calcule el hash verá que no coincide con el que acompaña a script.js, en consecuencia rechazará al archivo y por tanto no mostrará nada.
Tutor del curso online de Análisis GeoEspacial con Python y de los cursos online de webmapping. Echa un vistazo a todos nuestros cursos de SIG online.
Muy buen articulo y aviso. Soy relativamente nuevo haciendo visores y me asalta una duda, según lo que pude entender, esto aplicaría solo en el caso que el ataque fuera hecho al archivo alojado sin saber en especifico que aplicaciones están consumiendo dicho archivo?
Saludos y gracias
Muchas gracias Gian por tu comentario.
Efectivamente el hash criptográfico lo único que nos puede garantizar es que el archivo que estamos pidiendo al CDN no ha sido manipulado.
Saludos
Muy buen articulo, Gracias!.