Cifrar contraseñas. ¿Es totalmente seguro sha1?

En recientes días, haciendo una investigación acerca de el funcionamiento de cierta aplicación móvil me encontré con que usa una API pública, en la cual se puede obtener información de dicha app y el servicio que ofrece esa empresa.

La API en cuestión es muy fácil de consumir, basta con llamarla de la siguiente forma:

http://ip-de-la-pagina/a/metodo/parametro

Es decir si ponía:

http://ip-de-la-pagina/a/servicios/

Me aparecía un JSON con los servicios que ofrece. Hasta aquí todo bien. ¿Y qué tiene que ver esto con seguridad? Te has de estar preguntando. Aquí empieza lo bueno.

Se me ocurrió poner:

http://ip-de-la-pagina/a/usuarios/

Y la página me contestó con un JSON bien bonito donde venían los datos de los usuarios, incluido su nombre de inicio de sesión (username) y un SHA1 de su contraseña.

Hacer bypass a ese “super cifrado” fue tan fácil como entrar a http://hashkiller.co.uk o https://crackstation.net y pegar los respectivos digest *.

0caf711312d224809863a2c43fbddfbf8c2de642 --> HOLA123
07313f0e320f22cbfa35cfc220508eb3ff457c7e --> changeit

¿Entonces es malo usar SHA1 para las contraseñas?

No, lo que es malo es usarlo sin otra técnica adicional de protección. Como vimos ya, existen infinidad de páginas con enormes diccionarios, así que piénsalo bien antes de usar solo SHA1 (o MD5).

Como técnica de protección adicional, entre muchas otras,  podemos usar:

  • Doble hasheo
  • Salt + hasheo
  • Salt + doble hasheo
  • Salt cifrado + doble hasheo

A continuación explicaré cada una de ellas.

Nota: En todos los casos vamos a suponer que el usuario envió como password la cadena 1234567890, que en SHA1 produce el siguiente hash: 01b307acba4f54f55aafc33bb06bbbf6ca803e9a. Si buscamos ese hash en hashkiller.co.uk vemos que es encontrado con una coincidencia del 100%.

Doble hasheo

Aplicar esta técnica es la relativamente sencillo, generalmente se hashea primero en MD5 y luego el resultado con SHA1.

Observemos el código:

/*
 Vamos a suponer que Security::filter() es un método estático
 de la clase Security que filtra lo que manda el user.
 Ya saben para prevenir XSS y SQL injection
*/
// Pass en claro: 1234567890
$plainPass = Security::filter($_POST['txPassword']);
/* Step 1
 Obtenemos el primer hash, este consistirá en un MD5
 de la contraseña en texto plano. Esto produce:
 e807f1fcf82d132f9bb018ca6738a19f
 */
$md5Pass=md5($plainPass);

/* Step 2
 Obtenemos el segundo hash, este consistirá en un SHA1
 del MD5 generado anteriormente. Esto produce:
 b102ce1d5eebac2b6d74bda8c87c47a050c80491
 */
$sha1Pass=sha1($md5Pass);

/*
 La variable $sha1Pass es la que tendríamos que
 guardar en la base de datos
 */

Y resumido en una línea, quedaría así:

$sha1Pass=sha1(md5(Security::filter($_POST['txPassword'])));

¿Sencillo no?

Si ahora buscamos el hash final en las páginas antes mencionadas, vemos que no aparece ninguna coincidencia.

Para comprobar que el usuario escribe bien su contraseña al momento de iniciar sesión, lo único que hacemos el volver a hacer el doble hasheo y comparar el resultado con el hash guardado en la base de datos.

Salt + Hasheo

Para comprender este método (y todos los que usan salt) debemos saber primero que un pequeño cambio en la cadena a hashear genera un hash totalmente diferente. Por ejemplo:

changeit --> 07313f0e320f22cbfa35cfc220508eb3ff457c7e
changeit1 --> 1aea91d070c7c345db25af60bc6478fbf80c6b1d
1changeit --> 54aa8b98838531295aeee82754e205748eb75d81

Viendo lo anterior ya te imaginarás por donde va la cosa. Lo que se hace es generar una cadena corta de manera aleatoria (hay quienes usan números), después agregarla al principio o al final de la contraseña en claro, y luego hashear.

La desventaja de este método es que hay que guardar el número o cadena generado (el salt) en la base de datos para poderlo recuperar después y poder comprobar que el usuario escribe bien su contraseña.

Para guardar la contraseña se siguen estos pasos

$plainPass = Security::filter($_POST['txPassword']);

/* Step 1:  Generamos un salt de 12 caracteres de largo y lo metemos en $cad */

$str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ12345abcdefghijklmnopqrstuvwxyz';
$cad = "";
for($i=0;$i<12;$i++) {
 $c=rand(0,strlen($str)-1);
 $o=substr($str,$c,1);
 $cad .= $o;
} 


/* Step 2:  Agregamos el salt al principio de la contraseña en claro */

$pw = $cad.$plainPass;

/* Step3: Hasheamos */

$sha1Pass=sha1($pw);

/*
 La variable $sha1Pass es la que tendríamos que
 guardar en la base de datos, también tendríamos que guardar el salt generado (variable $cad)
 */

Para comprobar que el usuario escribe bien su contraseña al momento de iniciar sesión extraemos el salt guardado en la base de datos, lo ponemos al inicio de la contraseña enviada, hacemos el hasheo y comparamos el resultado con lo que está guardado en la base de datos.

Salt + doble hasheo

Igual que el anterior, pero se hace un doble hasheo (por ejemplo MD5 + SHA1)

$plainPass = Security::filter($_POST['txPassword']);

/* Step 1: Generamos un salt de 12 caracteres de largo y lo metemos en $cad */

$str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ12345abcdefghijklmnopqrstuvwxyz';
$cad = "";
for($i=0;$i<12;$i++) {
 $c=rand(0,strlen($str)-1);
 $o=substr($str,$c,1);
 $cad .= $o;
}

/* Step 2: Agregamos el salt al principio de la contraseña en claro */

$pw = $cad.$plainPass;

/* Step3: Hacemos el doble Hasheo */

$sha1Pass=sha1(md5($pw));

/*
 La variable $sha1Pass es la que tendríamos que
 guardar en la base de datos, también tendríamos que guardar el salt generado (variable $cad)
 */

Salt cifrado + doble hasheo

Si queremos proteger un poco más, podemos cifrar el salt, básicamente el proceso es el mismo que en el caso anterior, solo que antes de poner el salt antes de la contraseña enviada lo ciframos usando el algoritmo de nuestra preferencia. Yo en lo personal lo hago con Blowfish, ya que “No se han encontrado técnicas de criptoanálisis efectivas contra el Blowfish. Sin embargo, se ha dado más atención a la decodificación de bloques con bloques más grandes, como AES y Twofish“. El proceso sería algo como:

  • Generar el salt.
  • Cifrar el salt.
  • Ponemos el salt cifrado antes de la contraseña enviada.
  • Hacemos el doble hasheo.

Al igual que en todos los casos donde se usa un salt, debemos de guardar el salt generado (en este caso se guardaría cifrado) y el hash resultante de Salt cifrado + contraseña en claro.

Revisión técnica: Es aún más seguro usar SHA256 que SHA1. Se dice que SHA1 es cada vez más débil, ya que día con día disminuye el tiempo en el que se puede corromper.


* Las contraseñas originales que encontré no se muestran.
* La vulnerabilidad de dicha página -cuyo nombre se omitió- fue reportada al CERT de la UNAM por tratarse de una página de una empresa mexicana.

 

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s