Aprendí que los chicos de Sun usaron el nombre de inicio de sesión como salt para el hashing de contraseñas. ¿Es este un enfoque común? ¿Cuáles son los valores de sal más comunes?
Aprendí que los chicos de Sun usaron el nombre de inicio de sesión como salt para el hashing de contraseñas. ¿Es este un enfoque común? ¿Cuáles son los valores de sal más comunes?
El uso del nombre de inicio de sesión como salt es común, pero no se recomienda: hace solo una parte del trabajo. El objetivo de un salt es ser lo más único posible entre todas las instancias de contraseñas con hash, para frustrar cualquier intento como ataques paralelos (atacar varias contraseñas con hash simultáneamente, usar tablas precomputadas como tablas arco iris ... son ataques paralelos). En distintos sistemas, varios usuarios pueden compartir el mismo nombre de inicio de sesión (hay muchos Jones y Smiths por ahí). Además, un usuario determinado puede cambiar su contraseña mientras mantiene su nombre; pero incluso una contraseña antigua es valiosa para un atacante (aunque solo sea porque una contraseña antigua suele ser una contraseña que el usuario reutilizará la próxima vez que se le indique que cambie su contraseña; además, muchos usuarios usarán contraseñas idénticas en otros sistemas) .
La forma recomendada para generar una sal es usar un buen generador de números aleatorios (idealmente criptográficamente fuerte RNG) y hacer que produzca un montón de bytes aleatorios. Si la sal es lo suficientemente larga (16 bytes o más), esto asegura la singularidad requerida con una probabilidad abrumadora y sin muchas complicaciones (no es necesario buscar en una base de datos de sales existentes, por ejemplo).
Solo podemos esperar que esta forma recomendada sea también, o pronto se convierta, en la forma "más común".
El siguiente es un sistema decente para contraseñas de salado y hash. Hay una serie de puntos a tener en cuenta, por lo que parece largo. Este es un ejemplo típico de lo que uno debería hacer cuando se trata del hashing de sal; No voy a abordar lo que Sun hace.
Las sales y las contraseñas se tratan mejor como bytes sin procesar ( byte[]
, unsigned char*
, etc.), en caso de que el idioma que se usa se trate de cadenas de texto de alguna otra forma.
Generemos una nueva sal utilizando un generador de números pseudoaleatorios criptográficamente fuerte para generar la sal. En Ruby, esa biblioteca se llama convenientemente securerandom
. La mayoría de los idiomas también vienen con un generador de números aleatorios, pero eso no es suficiente para una sal.
Vamos a utilizar SHA256 como nuestro algoritmo de hash subyacente. Todos los algoritmos hash tienen un tamaño de salida, que es el tamaño en bits de la salida de la función hash. Todos los algoritmos hash también tienen un tamaño de bloque interno característico, que no es el mismo que el tamaño de salida. Usemos una sal tan grande como el tamaño de bloque del algoritmo hash. Hacemos esto porque es la mayor cantidad de entropía que podemos agregar a la contraseña antes de modificarla. Si usamos una sal más grande que el tamaño de bloque del algoritmo de hash subyacente, entonces HMAC simplemente procesará la sal primero para obtener una sal más pequeña, reduciendo su entropía, antes de mezclarla con la contraseña. El tamaño de bloque de SHA256 es de 512 bits (= 64 bytes), por lo que vamos a usar una sal de 64 bytes.
Usemos HMAC -SHA256 en lugar de SHA256 directamente, lo que hace un poco más de trabajo para mantener la contraseña segura. Usaremos la sal como el parámetro clave para HMAC-SHA256 en lugar de añadirla a la contraseña. HMAC-SHA256 envuelve SHA256 y hace un poco más de trabajo para combinar el salt con la contraseña.
Finalmente, los algoritmos hash como SHA-256 están diseñados para ser rápidos. Independientemente de lo que haga nuestro código en general, es probable que esté gastando el 1% de su tiempo en autenticar a los usuarios y el 99% de otras cosas. Si un atacante obtiene una copia del archivo de contraseña, es probable que su código esté gastando el 100% de su tiempo forzando las contraseñas. Así que hagamos que el algoritmo general sea muy lento: no demasiado lento para que incida en el resto de nuestro código, pero lo suficientemente lento para desalentar al atacante. Tomemos un parámetro llamado factor de trabajo (que podría ser 16) e iteremos HMAC-SHA256 2 ^ tiempos de factor de trabajo (65536 veces en el ejemplo). Si el factor de trabajo fuera 2, entonces se vería como
HMAC-SHA256(salt,
HMAC-SHA256(salt,
HMAC-SHA256(salt,
HMAC-SHA256(salt,
password
)
)
)
)
Una solución buena pero simple en Ruby:
require "securerandom"
require "openssl"
# method for generating a salt and computing a hashed
# version of a password
def salt_hash(password, work_factor)
# get raw bytes from the password string; only needed in Ruby >= 1.9
password = password.force_encoding(Encoding::ASCII_8BIT) if defined?(Encoding)
# use SHA256 as the underlying hash algorithmn
hash = OpenSSL::Digest::SHA256.new
# generate a salt as long as the hash algorithm's block size
# using a cryptographically strong pseudo-random number generator
salt = SecureRandom.random_bytes(hash.new.block_length)
# use HMAC, feeding it two parameters: the salt and the
# underlying hash; since the underlying hash is SHA256,
# this turns it into HMAC-SHA256
hmac = OpenSSL::HMAC.new(salt, hash)
# iterate running HMAC-SHA256 on the password 2^work-factor
# times to make this function slow enough to discourage an
# attacker, but not too slow as to make the service unresponsive
iterations = 2 ** work_factor
iterations.times do
password = hmac.digest(password)
end
# return the salt and the hashed password to whoever called
# this method
{:salt => salt, :hash => password, :work => work_factor}
end
# how to call the method
salt_hash("seekrit", 16)
# and the output would be
=> {:salt => "raw bytes", :hash => "raw bytes", :work => 16}
Lea otras preguntas en las etiquetas passwords authentication hash salt cryptography