Comment stocker correctement un PBKDF2 hachage de mot de passe
J'ai fait quelques recherches pour une bonne façons de hachage/crypter un mot de passe et de les stocker dans une base de données. Je savais à propos de Sel et de Hachage donc j'ai regardé autour et PBKDF2 semblait être un bon choix. J'ai donc trouvé ce site internet qui a donné un bon tutoriel sur elle ainsi que sur une adaptation de PBKDF2 pour PHP (qui est ce que j'utilise pour mon site).
Donc j'ai mis en place mon site web pour utiliser ces fonctions pour générer/créer des mots de passe, mais comme vous pouvez le voir dans le code suivant:
function create_hash($password) { //format: algorithm:iterations:salt:hash $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM)); return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" . base64_encode(pbkdf2( PBKDF2_HASH_ALGORITHM, $password, $salt, PBKDF2_ITERATIONS, PBKDF2_HASH_BYTES, true )); }
Le sel est généré dans le create_hash de la fonction, et est stocké dans le résultat de hachage qui finit par ressembler à sha256:1000:sel:hashed_password. C'est ce que j'avais à stocker dans ma base de données, et car le sel a été inclus dans le résultat de hachage, je n'ai pas besoin de l'ajouter à ma base de données. Cependant, suite à la production de quelques utilisateurs de test avec cela, je me demandais si le fait d'avoir le PBKDF2 paramètres à l'intérieur de mon hachage de mot de passe dans la base de données était en fait une bonne chose. Ils mon newbie auto voir c'est un hacker, après le craquage de ma base de données, serait de voir ces tas de sha256:1000:le sel:le mot de passe des choses, et de déterminer ce que chaque partie se, qui l'aiderait grandement dans ses tentatives, non?
Alors je l'ai modifié un peu pour avoir un externe de sel que je générer et stocker dans ma base de données, et d'inclure le sel dans le mot de passe avant de l'exécuter à travers PBKDF2. Je puis faire la même chose pour comparer le mot de passe donné avec ce que j'ai dans ma base de données pour la connexion, et ça fonctionne. Mon seul souci est que un avec 128bit le sel, le hachage de mot de passe est à peine plus de 50 caractères, cela ne semble pas juste pour moi.
Voici mon code actuel:
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 10000);
define("PBKDF2_SALT_BYTES", 128);
define("PBKDF2_HASH_BYTES", 24);
define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);
function create_hash($password, $salt)
{
//format: salthash
return
base64_encode(pbkdf2(
PBKDF2_HASH_ALGORITHM,
$password,
$salt,
PBKDF2_ITERATIONS,
PBKDF2_HASH_BYTES,
true
));
}
function validate_password($password, $salt, $good_hash)
{
$pbkdf2 = base64_decode($good_hash);
return slow_equals(
$pbkdf2,
pbkdf2(
PBKDF2_HASH_ALGORITHM,
$password,
$salt,
PBKDF2_ITERATIONS,
PBKDF2_HASH_BYTES,
true
)
);
}
//Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
die('PBKDF2 ERROR: Invalid hash algorithm.');
if($count <= 0 || $key_length <= 0)
die('PBKDF2 ERROR: Invalid parameters.');
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
//$i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
//first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
//perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
Est ma préoccupation que le format d'enregistrement du mot de passe du sens? Ou est-ce juste montant à la même chose de toute façon, puisque, de par le fait d'avoir mon sel dans ma base de données, une fois qu'il est fissuré, le hacker peut encore brute-forcer son chemin à travers?
Merci, et désolé pour la longue question.
C'est la réponse que je cherchais. Faits l'un pour l'instant mais n'hésitez pas à répondre à vous-même.
OriginalL'auteur Alex | 2012-07-31
Vous devez vous connecter pour publier un commentaire.
La taille du hachage est complètement étranger à la taille de la le sel, le mot de passe, et le nombre d'itérations. La sortie d'un moderne algorithme de hachage sécurisé (comme sha256) est toujours la même longueur quelle que soit l'entrée. D'une longueur de zéro entrée a la même longueur de sortie pouvant atteindre 25 to d'entrée.
En séparant le sel en deux pièces, vous coût accru la complexité du code (généralement une mauvaise chose). Selon la façon dont vous stockez le sel des morceaux, vous pouvez gagner un peu d'avantages dans certaines circonstances. Par exemple, si la statique sel fragment est stocké à l'extérieur de la base de données, puis un dump de la base de données ne sera pas donner à un attaquant de suffisamment d'informations pour effectuer une attaque hors connexion sur le hachage de mots de passe dans la base de données.
Le gain si le sel fragments sont stockés séparément l'un de l'autre est une petite quantité de la défense en profondeur. Si elle l'emporte sur le coût de complexité est un jugement d'appel, mais je dirais que les chances sont bonnes que le temps serait mieux dépensé à la recherche pour les XSS et les injections SQL (de la même façon que les attaquants souvent obtenir les dumps de la base de données mentionnée ci-dessus), et de la sécurisation des connexions entre les différents composants de votre système avec SSL et des certificats ou des mots de passe forts.
OriginalL'auteur Slartibartfast
Pour répondre à ma propre question avec un commentaire de @Romain.
Aussi, (puisque c'est un peu vieux), bcrypt est en fait "mieux" que pbkdf2, mais scrypt est encore mieux!
OriginalL'auteur
Une ou deux choses, l'une, au lieu de SHA256, vous devriez être en utilisant une lente fonction de hachage comme bcrypt, deux, vous devriez probablement pas stocker les mots de passe à tous (au lieu d'utiliser openId ou quelque chose de similaire).
Tout ce qui est dit, vous avez besoin d'un autre sel pour chaque utilisateur, vous pouvez les stocker dans la même ligne ou même (comme vous le faites) le même champ, ou dans un tout autre db. La quantité d'effort que vous êtes prêt à aller est vraiment à vous et à vos exigences de performances.
En plus à l'individu de sel pour chaque utilisateur/mot de passe, vous pouvez également envisager l'application par application de sel, ce qui est gardé à l'extérieur de la base de données.
oui, je voulais dire dans plus pour le sel pour le mot de passe. Va modifier pour que ce soit clair. Merci.
cette réponse est source de confusion. il n'y a rien de mal à utiliser SHA256 lorsque vous utilisez PBKDF2. le PBKDF2 ajoute de la construction de la slownesss (c'est l'équivalent de bcrypt; le "1000" signifie que SHA256 a été appelé 1000 fois par PBKDF2).
PBKDF2 est pas l'équivalent de bcrypt -- ils ont des similitudes, mais l'exécution SHA256 1000 fois n'est pas la même que l'exécution de bcrypt 1000 fois.
si vous comparez PBKDF2 avec bcrypt, il faut considérer que ceux 1000x SHA256-appels (ou quel que soit algo de hachage / nombre d'itérations que vous prenez) se produisent pendant 1 seul appel de PBKDF2.
OriginalL'auteur jmoreno