Lo que necesita saber sobre Salt como desarrollador
No, no me refiero a su sal de mesa, sino a la Salt utilizada en el cifrado de contraseñas de tu aplicación. Al igual que para salar sus platos al gusto, para las contraseñas se realizan para aumentar la protección de ellas en las aplicaciones.
Contraseñas sin Salt
Sin ‘salar’ sus contraseñas, cada conjunto de contraseñas idénticas dará como resultado el mismo hash exacto.
User PasswordHash tom 1a833da63a6b7e20098dae06d06602e1 bob 1a833da63a6b7e20098dae06d06602e1
Sin salt, los usuarios tom
y bob
con contraseña helloWorld
tienen el mismo hash. Si un atacante utiliza un ataque de diccionario y encuentra a una persona con la contraseña helloWorld
, podrá deducir fácilmente a todos los demás usuarios con la misma contraseña en la base de datos.
Contraseñas con Salt
¡Agreguemos un poco de sabor a nuestros suaves hashes! 😋
Una salt se genera mediante una función criptográficamente segura que se agrega a la entrada de funciones hash para crear hash únicos para cada entrada. Esto significa que todas las contraseñas que sean iguales ahora tendrán un valor hash diferente.
Suena genial, pero si cada hash es diferente, ¿cómo valido mis contraseñas cuando mis usuarios inician sesión ahora?
¡Almacenamos la sal en la base de datos junto con la contraseña!
User PasswordHash Salt tom c505b8ede7275cf5ddef2a8525b05bae 2kc02kd bob 918afd7892c4c05d137abfe4040baf06 92kd8le
Cuando un usuario inicia sesión, recuperamos el salt y el hash de la contraseña y lo comparamos con la contraseña entrante.
// Load hash from your password DB. compare(incomingPassword, {hash, salt}).then(function(result) { // result == true });
Quizás se esté preguntando si almacenamos el sal en la base de datos y si el atacante obtiene acceso a la base de datos, ¿todavía no puede averiguar las contraseñas de los usuarios?
El concepto clave para entender aquí es que aunque el atacante puede descifrar contraseñas individualmente, el atacante ahora tiene que calcular un hash con esa sal para cada entrada en el ataque de diccionario para que cada usuario determine su contraseña real. Esto es un gran inconveniente para el atacante y le impide determinar todas las contraseñas idénticas en la base de datos.
Independientemente, cuando ocurre una infracción como esta en una aplicación, una empresa debe considerar que todas las contraseñas están comprometidas y alentar a sus usuarios a cambiar sus contraseñas.
Ejemplo con bcrypt (nodejs)
Si alguna vez ha trabajado en una aplicación de backend que requería autenticación de contraseña, es probable que haya visto algo similar a esto:
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) { // Store hash in your password DB. });
El hash anterior se realiza mediante una biblioteca npm llamada bcrypt. También se realizan formas similares de hash utilizando otras bibliotecas.
Sin embargo, lo que algunos de nosotros quizás no recordemos es la necesidad de crear una columna en nuestra tabla de base de datos para almacenar la sal.
Esto se debe a que bcrypt almacena toda la información sobre el valor hash.
$[algorithm]$[cost]$[salt][hash]$2b$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa | | | | | | | hash-value = K0kSx... | | | | | salt = nOUIs5kJ7naTuTFkBy1veu | | | cost-factor => 10 = 2^10 rounds | hash-algorithm identifier => 2b = BCrypt
El factor de costo aquí se refiere al parámetro saltRounds en la función hash bcrypt anterior. Cuanto mayor sea el factor de costo, más tiempo tardará la función en realizar el hash. Esto significaría que el atacante necesitará aún más tiempo para descifrar las contraseñas con un factor de costo más alto. Sin embargo, no deberíamos usar un factor de costo demasiado alto porque podría afectar la experiencia del usuario de la aplicación. Un buen número para usar sería de alrededor de 10 a 12, lo que resultará en un tiempo de espera de 60 a 250 ms. Esta cantidad de tiempo es insignificante para los usuarios individuales, pero para los atacantes, un incremento en el factor de costo provoca un aumento exponencial en el tiempo de ataque.