Breve e incompleta historia de las contraseñas, aliados y enemigos (I)

martes, 27 de noviembre de 2018

Mucho se ha hablado (y se seguirá haciendo) de las contraseñas. Una "tecnología" con demasiados defectos y primitiva. Podríamos calificarla de eslabón débil con el permiso de las "preguntas de seguridad"; no son más que un refrito que nunca debió existir y mucho menos en la era de la exhibición, donde un disco duro en un rack de un centro de datos en Arkansas tiene más datos públicos de nosotros mismos de los que creemos recordar. De hecho, muchos titulares de los medios generalistas ignoran que tras ese "Hackean..." no se esconde una APT sigilosa sino una menos cinematográfica y rimbombante versión real de los hechos: han dado con una contraseña débil o robada mediante un phishing. Una acción que mezcla buen tino, suerte, una mala interpretación de las reglas seguras para gestionar credenciales y la ignorancia o despiste de un usuario desprevenido. Vamos a borrar todo lo que sabemos sobre autenticación y comencemos con un lienzo en blanco. Tenemos una empresa que ha desarrollado una aplicación y unos usuarios dispuestos a usarla. Naturalmente, no queremos que nadie más acceda a los recursos de cada usuario que sus legítimos propietarios. ¿Cómo solucionas esto? 

Lo más fácil sería poner una norma y que todo el mundo la acepte: "Se prohíbe el acceso a los recursos de otros usuarios". Listo, ¿no? Problema solucionado. Pero ya sabemos que el mundo no funciona así, ni mucho menos. Sí el lunes se inventó la puerta, el martes ya había un señor instalando la primera cerradura de la historia. 

infografia contraseñas
                                                          https://imgs.xkcd.com/comics/security_question.png
En el otro lado del extremo, la empresa podría hacer un despliegue de medios para asegurar mediante tecnología la identidad y voluntad del usuario. Por ejemplo, enviando un kit de autenticación continua, basada en el análisis permanente de ADN y lectura de ondas cerebrales; por si acaso el cliente está siendo obligado a iniciar sesión en contra de su voluntad. El problema es que si eso existe debe ser bastante caro y, además, poco práctico si el cliente quiere salir a la calle con los electrodos puestos… El termino medio entre los absurdos es algo barato, probado y que sea fácil de implementar. La seguridad, que en el negocio es muchas veces percibida como una incómoda esquina que entorpece los rectos renglones del desarrollo, posee muchos mecanismos que cuadran con el exiguo presupuesto y tiempo que se invierte en poner cerraduras a las puertas. Así pues, lo que se busca son soluciones probadas, fáciles de implementar y a ser posible, que ya vengan de serie en las librerías de desarrollo. Un checklist de peticiones que cumplen de sobra las contraseñas.

La triada de la autenticación
Los tres tipos de autenticación que todos conocemos se basan en lo que se conoce o sabe, lo que se posee y los sistemas biométricos. Las contraseñas entrarían en la primera categoría, pero, gracias a esas notitas pegajosas de color amarillo chillón, poseen la increíble cualidad de mutar hacia la segunda categoría. Luego de algo que se sabe y no debería ser conocido por nadie más, se convierte en algo que poseemos y por descuido, que podrían poseer los demás. Hablando de la primera categoría, las contraseñas entran dentro de esta, es decir, "lo que se sabe". Pero ¡ay! amigo, la memoria humana tiene su propia papelera de reciclaje y termina por desechar aquellos recuerdos que no le son útiles. Esto es una debilidad implícita en nosotros, al final terminamos creando una contraseña única para casi todo y con un claro esquema de composición "para no olvidarla". Este truco barato que nos imponemos es precisamente la técnica que se usa para adivinar las contraseñas. La facilidad para recordarlas es inversamente proporcional a la dificultad para dar con ellas.

infografia contraseñas dos image
https://imgs.xkcd.com/comics/identity.png

Ahora dejemos la orilla del cliente y vámonos a la parte del servidor. El problema del almacenamiento seguro. En primer lugar: NO SE ALMACENAN LAS CONTRASEÑAS. Nunca, jamás. Es un pecado capital y lo peor de todo es que a estas alturas de siglo XXI, aún siguen cayendo tablas repletas de contraseñas en texto plano en diferentes leaks y pentestings.

Hashes to hashes
La solución clásica a esto es almacenar no la contraseña sino su hash. Es decir, pasamos la contraseña que hemos recibido del usuario por una función unidireccional y esta nos producirá una cadena de una determinada longitud. Esa cadena es la que, en principio, se almacenaría, luego veremos cómo. Así cuando por un ataque o filtración se ven expuestos los hashes al menos no se pueden usar estos directamente puesto que no son la contraseña ni contienen información alguna que dé alguna pista. Notemos que hemos dicho "función unidireccional". Esto significa que las funciones hash no poseen una función inversa o reciproca. Esta es una propiedad muy interesante y necesaria. La salida de una función hash no debe servir de parámetro para una función recíproca de esta que nos devuelva la contraseña original. Si esa función existiera, nos daría igual almacenar tanto el hash como la contraseña en claro, puesto que sería igualmente inseguro.

Echando de nuevo mano a las matemáticas, las funciones hash son sobreyectivas o producen colisión. Recordemos que son las mismas funciones que usamos precisamente para implementar el tipo abstracto de datos "tabla hash" (de ahí su nombre). Esto quiere decir que hay un número más grande de entradas que de salidas, por lo que es perfectamente posible que un grupo de entradas obtengan idéntica salida. ¿Jugáis a la lotería? Pues, eso, que no jugáis a un número en exclusiva, este posee numerosas impresiones en diferentes series, pero al final es el mismo número.

infografia contraseñas 3 imagen
         https://imgs.xkcd.com/comics/code_talkers.png

Un posible ataque sobre estos hashes, es intentar dar con la contraseña introduciendo miles y miles de contraseñas posibles en una función hash y comprobar si obtenemos un hash idéntico. Si es el mismo hash, entonces es la misma (o no) contraseña. Un momento ¿hemos dicho "o no"? Exacto. Por la misma propiedad de colisión, sería perfectamente posible dar con una cadena que no tenga parecido alguno con la contraseña original y sin embargo la salida de la función que calcula el hash… ¡Sea la misma! Eureka, podemos entrar en la cuenta de un tercero usando una contraseña…que no es la contraseña.

Un conocido ejemplo de colisión usando la función (ya obsoleta) MD5, disponible aquí. Otro, en el que podemos ver la visualización de bloques, donde a pesar de las diferencias de bits, estos bloques producen un solo e idéntico hash. Este tipo de ataques permitió la proliferación de gigantescas bases de datos que contienen hashes precalculados. Hay cientos de páginas disponibles para comprobar si un hash está ahí. Otro curioso mecanismo que ayuda a dar con la contraseña son las denominadas Rainbow Tables o tablas arcoíris. Para evitar o dificultar este tipo de ataques se agrego lo que conocemos como sal.

La sal, el pequeño detalle que marca la diferencia
La sal. Muchas veces no entendida correctamente. La sal mete ruido, interferencia. Entorpece todo el trabajo hecho de antemano por esas bases de datos o tablas arcoíris que comentábamos ¿Por qué? Pues porque si agregamos una cadena "extra" a la hora de calcular el hash evidentemente el resultado no es el mismo y aunque la contraseña ya haya sido calculada de antemano y se sepa el hash, deberíamos de volver a calcular de nuevo todos los hashes de nuestra colección.

En primer lugar, no es necesario cifrar, ocultar o dificultar el acceso a la sal. Eso sí, si solo tenemos un valor de sal para todas las contraseñas, entonces deberíamos preocuparnos si este valor se hace público y alguien se toma la molestia de crear tablas de hashes precalculadas con este valor. No siendo el caso, la sal puede ser incluida perfectamente en texto plano como una columna más en la tabla de una base de datos.

      https://imgs.xkcd.com/comics/authorization.png


No obstante, idealmente la sal debe tener ciertas propiedades para considerarla segura:
  • Siempre, una sal diferente por cada contraseña.
  • Que la longitud de la sal no sea demasiado corta.
El primero dificulta todo tipo de ataque con tablas precalculadas y el segundo complica sobremanera la creación de nuevas tablas arcoíris para esa sal. 

Por esto, no debe preocuparnos almacenar esta sal en su forma, al final se debe rescatar de la base de datos, adosarla a la contraseña del usuario y generar el hash final. Lo que se busca aquí no es esconder un secreto arcano sino dificultar los ataques una vez los hashes han sido comprometidos. 

Por cierto, es un error tremendamente común decir que se han comprometido las contraseñas de un sitio, cuando en realidad lo que se ha obtenido son los hashes; luego habrá que ver si estos resisten los ataques comentados o no, pero si las contraseñas no estaban almacenadas en texto claro…no han sido comprometidas.

Funciones hash seguras… de momento
MD4, MD5, SHA1, estas funciones ya no son consideradas seguras para usos o procesos críticos. Ojo, esto no significa que no dejen de tener uso, por ejemplo, SHA1 sigue siendo usado en Git para identificar unívocamente los objetos con los que trata. Significa que no podemos garantizar que el uso de estas funciones sea seguro en determinados ámbitos, entre ellos la autenticación. ¿Por qué? Porque es relativamente fácil encontrar colisiones y, por ende, podemos encontrar cadenas que nos otorguen el mismo hash; ergo podremos usarlas como contraseñas válidas.

¿Entonces, que usamos? ¿SHA-256, SHA-512…? Ninguna de ellas. Desde hace unos años este tema de las funciones hash o funciones de derivación de claves ha sido tratado de diferentes perspectivas y aproximaciones. Una iniciativa curiosa es la de Password Hashing Competition, una competición de funciones de esta familia a la que se presentaron 24 candidatos y durante un largo proceso, finalmente, proclamaron como ganadora la especificación Argon2 (implementación canónica en C, disponible aquí) Aunque no está muy extendido su uso, Argon2 reúne un buen número de características deseadas: resistencia a ataques de fuerza bruta, solvencia frente a ataques del tipo side-channel, etc. 

https://imgs.xkcd.com/comics/encryptic.png

Otro "concurso" famoso es el promocionado por el NIST que resultó en la elección de Keccak como nuevo estándar de la familia SHA, en concreto el SHA-3. Esta función ya posee numerosas implementaciones en las distintas y populares librerías criptográficas, incluidas OpenSSL o Bouncy Castle. 

Junto con Argon2, también disponemos de funciones aceptables para esta tarea, se trata de PBKDF2, scrypt y bcrypt; muy conocidas en el ámbito UNIX. Esta última, bcrypt, está basada en el algoritmo de cifrado simétrico Blowfish y presente en muchas distribuciones Linux. Una de las características notables es que ya incluye la sal en la forma final del hash producido. Una vieja conocida, que podemos encontrar en los archivos shadow de muchos sistemas UNIX. 

Scrypt, por otro lado, agrego una característica interesante respecto a los ataques de fuerza bruta: que fuera muy costoso generar un hash. Es decir, una especie de "prueba de trabajo" que exige recursos para crear un intento. Es, por supuesto, un arma de doble filo en aquellos sistemas con necesidad de generar grandes cantidades de hashes por segundo… ¿Os suena esto de algo? Exacto, de hecho, scrypt se usa precisamente en criptomoneda como prueba de trabajo. 

Para terminar, PBKDF2. Esta función es muy conocida, probablemente te sonará si alguna vez has buceado por la documentación técnica del estándar WIFI WPA2. De hecho, la salida de esta función es la PSK o Pre-shared Key. Una curiosa propiedad es el número de iteraciones de la función. Este parámetro es un compromiso entre seguridad y rendimiento. Su recomendación ha crecido y seguirá haciéndolo con el tiempo. Por ejemplo, hace años se recomendaba 1000 iteraciones (año 2000), mientras que actualmente el número de iteraciones para ciertos ambientes productivos se ha incrementado hasta 100.000.

Ahora bien, tenemos que tener presente el factor tiempo. En criptografía, lo que hoy es seguro mañana no lo será. Notemos que no hablamos en condicional, "podría ser". No, con toda certeza, tanto por el aumento de capacidad computacional como los sofisticados ataques de criptoanálisis, consiguen cada cierto tiempo destronar un cifrado o función que se consideraba robusta y confiable. Hoy es seguro, mañana no. 

Vale, pero entonces, ¿cómo deben ser las contraseñas para que sean seguras? Lo veremos en la siguiente entrada. 

David García
Innovación y laboratorio de ElevenPaths
david.garcianunez@telefonica.com

No hay comentarios:

Publicar un comentario