Mettre à jour les anciens mots de passe md5 stockés dans PHP pour augmenter la sécurité
au moment où j'ai une base de données avec des mots de passe md5 stockés, il y a quelques années cela a été considéré un peu plus sûr que ce qu'il est maintenant et il est arrivé au point où les mots de passe doivent être plus sûr.
j'ai lu beaucoup de billets ici sur crypt
, md5
, hash
, bcrypt
, etc et en sont venus à envisager d'utiliser quelque chose dans le genre de ce qui suit pour "sécuriser" les mots de passe mieux qu'ils ne le sont maintenant.
j'utiliserai une combinaison de hash("sha512"
et de deux sels, le premier étant un sel stocké à l'échelle du site dans un fichier tel que .htaccess et le second sel seront créés pour chaque utilisateur.
voici un exemple dans le sens de ce que je teste en ce moment:
.htaccess
SetEnv SITEWIDE_SALT NeZa5Edabex?26Y#j5pr7VASpuUheVaREj$yA*59t*A$EdRUqer_prazepreTr
exemple.151980920 php"
$currentpassword = //get password
$pepper = getenv('SITEWIDE_SALT');
$salt = microtime().ip2long($_SERVER['REMOTE_ADDR']);
$saltpepper = $salt.$pepper;
$password = hash("sha512", md5($currentpassword).$saltpepper);
le sel devrait évidemment être stocké dans une table séparée pour permettre la vérification des futurs mots de passe de connexion insérés, mais il ne serait jamais possible pour un utilisateur de voir. Pensez-vous que c'est une façon suffisante de procéder?
4 réponses
OK, passons en revue quelques points ici
-
ce que vous avez dans
$salt
n'est pas un sel. C'est déterministe (ce qui veut dire qu'il n'y a pas de hasard là-dedans). Si vous voulez un sel, utilisezmcrypt_create_iv($size, MCRYPT_DEV_URANDOM)
ou une autre source d'entropie aléatoire réelle. Le point est qu'il doit être à la fois unique et aléatoire. Notez qu'il n'a pas besoin d'être cryptographique sécurisé aléatoire... À l'absolu pire, j'avais faites quelque chose comme ceci:function getRandomBytes($length) { $bytes = ''; for ($i = 0; $i < $length; $i++) { $bytes .= chr(mt_rand(0, 255)); } return $bytes; }
-
@Anony-Mousse indiqué, jamais nourrir la sortie d'une fonction de hachage à une autre sans re-ajouter les données d'origine. Utilisez plutôt un algorithme itératif approprié tel que PBKDF2 , PHPASS ou CRYPT_BLOWFISH ($2a$).
Ma suggestion serait d'utiliser
crypt
avec blowfish, car C'est le meilleur disponible pour PHP en ce moment:function createBlowfishHash($password) { $salt = to64(getRandomBytes(16)); $salt = 'a$' . $salt; $result = crypt($password, $salt); }
et vérifier ensuite en utilisant une méthode comme celle-ci:
function verifyBlowfishHash($password, $hash) { return $hash == crypt($password, $hash); }
(notez que
to64
est une bonne méthode définie ici ). Vous pouvez également utiliserstr_replace('+', '.', base64_encode($salt));
...
je vous suggère aussi de lire les deux suivants:
- fondamental différence entre le hachage et le chiffrement
- de nombreuses itérations de hachage, ajoutent du sel à chaque fois?
Edit: pour Répondre À la Question Migratoire
Ok, donc je me rends compte que ma réponse n'a pas abordé l'aspect migratoire de la question originale. Voilà comment je le résoudrais.
tout d'abord, construire une fonction temporaire pour créer un nouveau hachage de poisson-lune à partir de la hachage md5 original, avec un sel aléatoire et un préfixe de sorte que nous pouvons le détecter plus tard:
function migrateMD5Password($md5Hash) {
$salt = to64(getRandomBytes(16));
$salt = 'a$' . $salt;
$hash = crypt($md5Hash, $salt);
return '$md5' . $hash;
}
maintenant, exécutez tous les hachages md5 existants à travers cette fonction et enregistrez le résultat dans la base de données. Nous mettons notre propre préfixe pour que nous puissions détecter le mot de passe original et ajouter l'étape md5 supplémentaire. Donc maintenant nous sommes tous émigrés.
ensuite, créer une autre fonction pour vérifier les mots de passe, et si nécessaire mettre à jour la base de données avec un nouveau hachage:
function checkAndMigrateHash($password, $hash) {
if (substr($hash, 0, 4) == '$md5') {
// Migrate!
$hash = substr($hash, 4);
if (!verifyBlowfishHash(md5($password), $hash) {
return false;
}
// valid hash, so let's generate a new one
$newHash = createBlowfishHash($password);
saveUpdatedPasswordHash($newHash);
return true;
} else {
return verifyBlowfishHash($password, $hash);
}
}
C'est ce que je suggérerais pour quelques raisons:
- il obtient le
md5()
hashes hors de votre base de données immédiatement. - éventuellement (connexion suivante pour chaque utilisateur) met à jour le hachage vers une meilleure alternative (une qui est bien comprise).
- c'est assez facile à suivre en code.
pour répondre aux commentaires:
-
un sel n'a pas besoin d'être aléatoire - je vous dirige à RFC 2898 - cryptographie basée sur Mot de passe . Plus Précisément, La Section 4.1. Et je cite:
S'il n'y a pas d'inquiétude au sujet des interactions entre utilisations multiples de la même clé (ou un préfixe de la clé) avec le mot de passe- des techniques de cryptage et d'authentification basées sur un le mot de passe donné, puis le sel peut être généré au hasard et n'a pas besoin d'être vérifiée pour un format particulier par la partie la réception du sel. Il doit être d'au moins huit octets (64 bits).
en plus,
Note. Si un générateur de nombres aléatoires ou pseudorandom n'est pas disponible, une alternative déterministe pour générer le sel (ou la partie aléatoire de celui-ci) est d'appliquer une clé basée sur le mot de passe dérivation fonction pour le mot de passe et le message M à traiter.
un générateur de PseudoRandom est disponible, alors pourquoi ne pas l'utiliser?
-
votre solution est-elle la même que bcrypt? Je ne trouve pas beaucoup de documentation sur ce qu'est en fait bcrypt? - je suppose que vous avez déjà lu l'Article bcrypt Wikipedia , et essayer de l'expliquer mieux.
BCrypt est basé sur le bloc de Blowfish. Il utilise l'algorithme key schedule setup du chiffrement, et utilise cela pour hacher les mots de passe. La raison pour laquelle il est bon, est que l'algorithme de configuration pour Blowfish est conçu pour être très cher (qui fait partie de ce qui rend blowfish si fort d'un cypher). Le processus de base est le suivant:
-
Une 18 de l'élément de matrice (P boîtes, 32 bits) et 4 à 2-dimensions des tableaux (appelées Boîtes S, chacune avec 256 entrées de 8 bits chacune) sont utilisées pour configurer le programme en initialisant les tableaux avec des valeurs statiques prédéterminées. De plus, un état 64 bits est initialisé à tous les 0.
-
la clé passée est Xorée avec toutes les boîtes de 18 P dans l'ordre (rotation de la clé si elle est trop courte).
-
les cases P sont alors utilisées pour chiffrer l'état qui a été précédemment initialisé.
-
le texte chiffré produit par l'étape 3 est utilisé pour remplacer P1 et P2 (les 2 premiers éléments du tableau P).
-
Étape 3 est répétée, et le résultat est mis en P3 et P4. Cela continue jusqu'à ce que P17 et P18 soient peuplés.
c'est le dérivé clé du chiffre Blowfish. BCrypt modifie cela à ceci:
-
l'état 64 bits est initialisé en une version cryptée du sel .
-
même
-
les cases P sont alors utilisées pour chiffrer la (état xor partie du sel ) qui a été précédemment initialisée.
-
même
-
Same
-
la configuration résultante est alors utilisée pour chiffrer le mot de passe 64 fois. C'est ce qui est retourné par BCrypt.
le point est simple: c'est un algorithme très coûteux qui prend beaucoup de temps CPU. C'est la vraie raison pour laquelle il doit être utilisé.
-
j'espère que ça éclaircira les choses.
la mise en œuvre de votre nouveau, plus sûr, le stockage de mot de passe devrait utiliser bcrypt ou PBKDF2 , comme c'est vraiment la meilleure solution là-bas en ce moment.
n'emboîtez pas les choses, car vous n'obtenez pas de réelle sécurité en raison de collisions comme @Anony-Mousse le décrit.
ce que vous voudrez peut-être faire mettre en œuvre une" routine de transition " où vos utilisateurs de transition de l'application à partir de L'ancienne base MD5 système vers le nouveau système plus sûr à mesure qu'ils se connectent. Lorsqu'une demande de connexion arrive, de voir si l'utilisateur est dans le nouveau, plus sécurisé, système. Si oui, bcrypt / PBKDF2 le mot de passe, comparer, et vous êtes bon à aller. Si ce n'est pas le cas (personne ne le sera au début), vérifiez-les en utilisant l'ancien système basé sur MD5. S'il correspond (mot de passe est correct), effectuer la transformation bcrypt/PBKDF2 du mot de passe (puisque vous l'avez maintenant), le stocker dans le nouveau système, et supprimer l'ancien enregistrement MD5. La prochaine fois qu'ils se connectent, ils il y a une entrée dans le nouveau système, vous pouvez y aller. Une fois que tous les utilisateurs se sont connectés une fois que vous avez mis en œuvre ceci, vous pouvez supprimer cette fonctionnalité de transition et juste vous authentifier contre le nouveau système.
Ne pas "nid d'151910920" à l'intérieur de votre sha512
hash . Une collision md5
implique alors une collision de hachage dans le hachage extérieur, aussi (parce que vous Hachez les mêmes valeurs!)
la façon courante de stocker les mots de passe est d'utiliser un schéma tel que
<method><separator><salt><separator><hash>
lors de la validation du mot de passe, vous avez lu <method>
et <salt>
à partir de ce champ, les appliquer de nouveau au mot de passe, puis vérifier que il produit le même <hash>
.
vérifiez les fonctions crypt
disponibles. Sur un système Linux moderne, crypt
devrait pouvoir utiliser sha512
mot de passe hashing d'une manière saine: PHP crypt manual . ne réinventez pas la roue , vous avez probablement tout simplement merder plus gravement que md5
, à moins que vous ne soyez un expert en matière de hachage cryptographique. Il va même s'occuper de scheme ci-dessus: le standard Linux est pour utiliser $
comme séparateur, et $
est la méthode ID pour sha512
, tandis que a$
indique que vous voulez utiliser blowfish
. Ainsi, vous pouvez même avoir plusieurs hashs en cours d'utilisation dans votre base de données. md5
les hashes sont préfixés avec $<salt>$
(à moins que vous n'ayez réinventé le hashing md5, alors vos hashes peuvent être incompatibles).
réutiliser la fonction crypt
. Il est bien vérifié par des experts, extensible, et compatible à travers de nombreux application.
j'ai examiné ce sujet il y a quelque temps et j'ai trouvé le lien suivant d'une grande utilité:
Secure hash et du sel pour PHP les mots de passe
j'utilise aussi ce qui suit pour créer un sel au hasard:
public static function getRandomString($length = 20) {
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$string = '';
for ($i = 0; $i < $length; $i++) {
$string .= substr($characters, (mt_rand() % strlen($characters)), 1);
}
return $string;
}