Le «double hachage» est-il un mot de passe moins sûr qu'un simple hachage une fois?

293

Le hachage d'un mot de passe deux fois avant le stockage est-il plus ou moins sûr qu'un simple hachage une fois?

Je parle de cela:

$hashed_password = hash(hash($plaintext_password));

au lieu de cela:

$hashed_password = hash($plaintext_password);

S'il est moins sécurisé, pouvez-vous fournir une bonne explication (ou un lien vers celle-ci)?

De plus, la fonction de hachage utilisée fait-elle une différence? Cela fait-il une différence si vous mélangez md5 et sha1 (par exemple) au lieu de répéter la même fonction de hachage?

Remarque 1: Lorsque je dis "double hachage", je parle de hacher deux fois un mot de passe pour essayer de le rendre plus obscur. Je ne parle pas de la technique de résolution des collisions .

Remarque 2: Je sais que je dois ajouter un sel aléatoire pour vraiment le sécuriser. La question est de savoir si le hachage deux fois avec le même algorithme aide ou blesse le hachage.

Bill le lézard
la source
2
Hash(password)et Hash(Hash(password))sont tout aussi précaires. Les deux n'ont pas la notion de sécurité sémantique . Autrement dit, la sortie peut être distinguée de aléatoire. Par exemple, MD5("password")est 5f4dcc3b5aa765d61d8327deb882cf99. Je sais que c'est le hachage MD5 password, et il se distingue du hasard. Au lieu de cela, vous devez utiliser un HMAC. C'est sûr et c'est un PRF.
2014

Réponses:

267

Le hachage d'un mot de passe une fois n'est pas sécurisé

Non, les hachages multiples ne sont pas moins sûrs; ils sont une partie essentielle de l'utilisation sécurisée des mots de passe.

L'itération du hachage augmente le temps nécessaire à un attaquant pour essayer chaque mot de passe de sa liste de candidats. Vous pouvez facilement augmenter le temps nécessaire pour attaquer un mot de passe de quelques heures à plusieurs années.

Une simple itération ne suffit pas

Le simple chaînage de la sortie de hachage en entrée n'est pas suffisant pour la sécurité. L'itération doit avoir lieu dans le contexte d'un algorithme qui préserve l'entropie du mot de passe. Heureusement, il existe plusieurs algorithmes publiés qui ont été suffisamment examinés pour donner confiance à leur conception.

Un bon algorithme de dérivation de clé comme PBKDF2 injecte le mot de passe dans chaque cycle de hachage, atténuant les problèmes de collisions dans la sortie de hachage. PBKDF2 peut être utilisé tel quel pour l'authentification par mot de passe. Bcrypt suit la dérivation de clé avec une étape de cryptage; de cette façon, si un moyen rapide d'inverser la dérivation de clé est découvert, un attaquant doit toujours terminer une attaque en texte brut connu.

Comment casser un mot de passe

Les mots de passe stockés doivent être protégés contre une attaque hors ligne. Si les mots de passe ne sont pas salés, ils peuvent être rompus avec une attaque de dictionnaire pré-calculée (par exemple, en utilisant une table arc-en-ciel). Sinon, l'attaquant doit passer du temps à calculer un hachage pour chaque mot de passe et voir s'il correspond au hachage stocké.

Tous les mots de passe ne sont pas également probables. Les attaquants peuvent rechercher de manière exhaustive tous les mots de passe courts, mais ils savent que leurs chances de succès en force brute diminuent fortement avec chaque personnage supplémentaire. Au lieu de cela, ils utilisent une liste ordonnée des mots de passe les plus probables. Ils commencent par "password123" et progressent vers des mots de passe moins fréquemment utilisés.

Disons qu'une liste d'attaquants est longue, avec 10 milliards de candidats; supposons également qu'un système de bureau puisse calculer 1 million de hachages par seconde. L'attaquant peut tester l'ensemble de sa liste en moins de trois heures si une seule itération est utilisée. Mais si seulement 2000 itérations sont utilisées, ce délai s'étend sur près de 8 mois. Pour vaincre un attaquant plus sophistiqué, capable de télécharger un programme capable d'exploiter la puissance de son GPU, par exemple, vous avez besoin de plus d'itérations.

Combien en faut-il?

Le nombre d'itérations à utiliser est un compromis entre la sécurité et l'expérience utilisateur. Le matériel spécialisé qui peut être utilisé par les attaquants est bon marché, mais il peut toujours effectuer des centaines de millions d'itérations par seconde. Les performances du système de l' attaquant déterminent le temps qu'il faut pour briser un mot de passe compte tenu d'un certain nombre d'itérations. Mais votre application n'est pas susceptible d'utiliser ce matériel spécialisé. Le nombre d'itérations que vous pouvez effectuer sans aggraver le nombre d'utilisateurs dépend de votre système.

Vous pouvez probablement laisser les utilisateurs attendre une extra seconde supplémentaire pendant l'authentification. Profilez votre plate-forme cible et utilisez autant d'itérations que possible. Les plates-formes que j'ai testées (un utilisateur sur un appareil mobile ou de nombreux utilisateurs sur une plate-forme serveur) peuvent confortablement prendre en charge PBKDF2 avec entre 60000 et 120000 itérations, ou bcrypt avec un facteur de coût de 12 ou 13.

Plus de fond

Lisez PKCS # 5 pour obtenir des informations faisant autorité sur le rôle du sel et les itérations dans le hachage. Même si PBKDF2 était destiné à générer des clés de chiffrement à partir de mots de passe, il fonctionne bien comme hachage unidirectionnel pour l'authentification par mot de passe. Chaque itération de bcrypt est plus coûteuse qu'un hachage SHA-2, vous pouvez donc utiliser moins d'itérations, mais l'idée est la même. Bcrypt va également au-delà de la plupart des solutions basées sur PBKDF2 en utilisant la clé dérivée pour crypter un texte brut bien connu. Le texte chiffré résultant est stocké en tant que «hachage», avec certaines métadonnées. Cependant, rien ne vous empêche de faire la même chose avec PBKDF2.

Voici d'autres réponses que j'ai écrites sur ce sujet:

erickson
la source
68
La création intentionnelle d'un algorithme lent est une pratique acceptée lorsque vous essayez d'empêcher les attaques de dictionnaire contre les magasins d'authentification compromis. La technique est appelée "renforcement des touches" ou "étirement des touches". Voir en.wikipedia.org/wiki/Key_stretching
17
@RoBorg: peu importe la lenteur de votre implémentation, mais la lenteur de l'implémentation d'un attaquant: si le hachage lui-même est des milliers de fois plus lent, il faudra un attaquant des milliers de fois plus long pour forcer le mot de passe par une force brute.
orip
5
Il est probable que vous souhaitiez des collisions dans l'espace de 128 bits 0 à 2 ^ 128-1. Si l'espace de sortie 2 ^ 128 de l'algorithme de hachage est parfait, alors théoriquement, vous n'avez qu'un chiffre de substitution avec un alphabet de 2 ^ 128 glyphes.
jmucchiello
13
@devin - ce n'est pas "ma solution", c'est une pratique largement acceptée, intégrée aux normes de cryptographie par mot de passe comme PKCS # 5, et recommandée par des experts comme Robert Morris. Il est extrêmement évolutif, car la fraction du temps passé à authentifier les utilisateurs est faible dans une application légitime. Cela ne devient difficile à mettre à l'échelle que lorsque votre application craque les mots de passe, d'où la recommandation. Certes, l'espace de recherche d'un hachage est plus petit que celui des mots de passe possibles, mais même un espace de 128 bits est trop énorme pour une recherche par force brute. La menace à défendre est une attaque par dictionnaire hors ligne.
erickson
6
Je ne parlais pas des inconvénients pour l'utilisateur individuel, mais plutôt du stress qui serait mis sur le serveur si vous aviez une grande base d'utilisateurs, car vous comptez sur la charge du processeur pour ralentir le nombre de demandes. Cela signifie que si vous ajoutez plus de puissance CPU, vous réduisez la restriction sur ces attaquants de force brute. - Cependant, vous avez tout à fait raison concernant l'évolutivité et la pratique largement acceptée. J'avais tort sur presque tout ce que j'ai dit dans mes commentaires précédents. Désolé :)
DevinB
227

Pour ceux qui disent que c'est sûr, ils ont raison en général . Le hachage "double" (ou l'expansion logique de cela, l'itération d'une fonction de hachage) est absolument sûr s'il est bien fait , pour une préoccupation spécifique.

Pour ceux qui disent que ce n'est pas sûr, ils ont raison dans ce cas . Le code affiché dans la question n'est pas sécurisé. Parlons pourquoi:

$hashed_password1 = md5( md5( plaintext_password ) );
$hashed_password2 = md5( plaintext_password );

Il y a deux propriétés fondamentales d'une fonction de hachage qui nous préoccupent:

  1. Résistance à la pré-image - Étant donné un hachage $h, il devrait être difficile de trouver un message $mtel que$h === hash($m)

  2. Deuxième résistance à la pré-image - Étant donné un message $m1, il devrait être difficile de trouver un message différent $m2tel quehash($m1) === hash($m2)

  3. Résistance aux collisions - Il devrait être difficile de trouver une paire de messages ($m1, $m2)tels que hash($m1) === hash($m2)(notez que cela est similaire à la résistance de la deuxième pré-image, mais différent en ce sens que l'attaquant a le contrôle sur les deux messages) ...

Pour le stockage des mots de passe , tout ce qui nous importe vraiment, c'est la résistance à la pré-image . Les deux autres seraient sans objet, car $m1c'est le mot de passe de l'utilisateur que nous essayons de protéger. Donc si l'attaquant l'a déjà, le hash n'a rien à protéger ...

AVERTISSEMENT

Tout ce qui suit est basé sur la prémisse que tout ce qui nous intéresse est la résistance à la pré-image . Les deux autres propriétés fondamentales des fonctions de hachage peuvent ne pas (et ne le sont généralement pas) tenir de la même manière. Les conclusions de cet article ne sont donc applicables que lors de l'utilisation des fonctions de hachage pour le stockage des mots de passe. Ils ne sont pas applicables en général ...

Commençons

Pour les besoins de cette discussion, inventons notre propre fonction de hachage:

function ourHash($input) {
    $result = 0;
    for ($i = 0; $i < strlen($input); $i++) {
        $result += ord($input[$i]);
    }
    return (string) ($result % 256);
}

Maintenant, il devrait être assez évident de savoir ce que fait cette fonction de hachage. Il additionne les valeurs ASCII de chaque caractère d'entrée, puis prend le module de ce résultat avec 256.

Alors testons-le:

var_dump(
    ourHash('abc'), // string(2) "38"
    ourHash('def'), // string(2) "47"
    ourHash('hij'), // string(2) "59"
    ourHash('klm')  // string(2) "68"
);

Voyons maintenant ce qui se passe si nous l'exécutons plusieurs fois autour d'une fonction:

$tests = array(
    "abc",
    "def",
    "hij",
    "klm",
);

foreach ($tests as $test) {
    $hash = $test;
    for ($i = 0; $i < 100; $i++) {
        $hash = ourHash($hash);
    }
    echo "Hashing $test => $hash\n";
}

Cela génère:

Hashing abc => 152
Hashing def => 152
Hashing hij => 155
Hashing klm => 155

Hrm, wow. Nous avons généré des collisions !!! Essayons de voir pourquoi:

Voici la sortie de hachage d'une chaîne de chaque sortie de hachage possible:

Hashing 0 => 48
Hashing 1 => 49
Hashing 2 => 50
Hashing 3 => 51
Hashing 4 => 52
Hashing 5 => 53
Hashing 6 => 54
Hashing 7 => 55
Hashing 8 => 56
Hashing 9 => 57
Hashing 10 => 97
Hashing 11 => 98
Hashing 12 => 99
Hashing 13 => 100
Hashing 14 => 101
Hashing 15 => 102
Hashing 16 => 103
Hashing 17 => 104
Hashing 18 => 105
Hashing 19 => 106
Hashing 20 => 98
Hashing 21 => 99
Hashing 22 => 100
Hashing 23 => 101
Hashing 24 => 102
Hashing 25 => 103
Hashing 26 => 104
Hashing 27 => 105
Hashing 28 => 106
Hashing 29 => 107
Hashing 30 => 99
Hashing 31 => 100
Hashing 32 => 101
Hashing 33 => 102
Hashing 34 => 103
Hashing 35 => 104
Hashing 36 => 105
Hashing 37 => 106
Hashing 38 => 107
Hashing 39 => 108
Hashing 40 => 100
Hashing 41 => 101
Hashing 42 => 102
Hashing 43 => 103
Hashing 44 => 104
Hashing 45 => 105
Hashing 46 => 106
Hashing 47 => 107
Hashing 48 => 108
Hashing 49 => 109
Hashing 50 => 101
Hashing 51 => 102
Hashing 52 => 103
Hashing 53 => 104
Hashing 54 => 105
Hashing 55 => 106
Hashing 56 => 107
Hashing 57 => 108
Hashing 58 => 109
Hashing 59 => 110
Hashing 60 => 102
Hashing 61 => 103
Hashing 62 => 104
Hashing 63 => 105
Hashing 64 => 106
Hashing 65 => 107
Hashing 66 => 108
Hashing 67 => 109
Hashing 68 => 110
Hashing 69 => 111
Hashing 70 => 103
Hashing 71 => 104
Hashing 72 => 105
Hashing 73 => 106
Hashing 74 => 107
Hashing 75 => 108
Hashing 76 => 109
Hashing 77 => 110
Hashing 78 => 111
Hashing 79 => 112
Hashing 80 => 104
Hashing 81 => 105
Hashing 82 => 106
Hashing 83 => 107
Hashing 84 => 108
Hashing 85 => 109
Hashing 86 => 110
Hashing 87 => 111
Hashing 88 => 112
Hashing 89 => 113
Hashing 90 => 105
Hashing 91 => 106
Hashing 92 => 107
Hashing 93 => 108
Hashing 94 => 109
Hashing 95 => 110
Hashing 96 => 111
Hashing 97 => 112
Hashing 98 => 113
Hashing 99 => 114
Hashing 100 => 145
Hashing 101 => 146
Hashing 102 => 147
Hashing 103 => 148
Hashing 104 => 149
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 146
Hashing 111 => 147
Hashing 112 => 148
Hashing 113 => 149
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 147
Hashing 121 => 148
Hashing 122 => 149
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 148
Hashing 131 => 149
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 149
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 160
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 160
Hashing 179 => 161
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 160
Hashing 188 => 161
Hashing 189 => 162
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 160
Hashing 197 => 161
Hashing 198 => 162
Hashing 199 => 163
Hashing 200 => 146
Hashing 201 => 147
Hashing 202 => 148
Hashing 203 => 149
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 147
Hashing 211 => 148
Hashing 212 => 149
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 148
Hashing 221 => 149
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 149
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

Remarquez la tendance vers des nombres plus élevés. Cela s'avère être notre mort. L'exécution du hachage 4 fois ($ hash = ourHash ($ hash) `, pour chaque élément) nous donne:

Hashing 0 => 153
Hashing 1 => 154
Hashing 2 => 155
Hashing 3 => 156
Hashing 4 => 157
Hashing 5 => 158
Hashing 6 => 150
Hashing 7 => 151
Hashing 8 => 152
Hashing 9 => 153
Hashing 10 => 157
Hashing 11 => 158
Hashing 12 => 150
Hashing 13 => 154
Hashing 14 => 155
Hashing 15 => 156
Hashing 16 => 157
Hashing 17 => 158
Hashing 18 => 150
Hashing 19 => 151
Hashing 20 => 158
Hashing 21 => 150
Hashing 22 => 154
Hashing 23 => 155
Hashing 24 => 156
Hashing 25 => 157
Hashing 26 => 158
Hashing 27 => 150
Hashing 28 => 151
Hashing 29 => 152
Hashing 30 => 150
Hashing 31 => 154
Hashing 32 => 155
Hashing 33 => 156
Hashing 34 => 157
Hashing 35 => 158
Hashing 36 => 150
Hashing 37 => 151
Hashing 38 => 152
Hashing 39 => 153
Hashing 40 => 154
Hashing 41 => 155
Hashing 42 => 156
Hashing 43 => 157
Hashing 44 => 158
Hashing 45 => 150
Hashing 46 => 151
Hashing 47 => 152
Hashing 48 => 153
Hashing 49 => 154
Hashing 50 => 155
Hashing 51 => 156
Hashing 52 => 157
Hashing 53 => 158
Hashing 54 => 150
Hashing 55 => 151
Hashing 56 => 152
Hashing 57 => 153
Hashing 58 => 154
Hashing 59 => 155
Hashing 60 => 156
Hashing 61 => 157
Hashing 62 => 158
Hashing 63 => 150
Hashing 64 => 151
Hashing 65 => 152
Hashing 66 => 153
Hashing 67 => 154
Hashing 68 => 155
Hashing 69 => 156
Hashing 70 => 157
Hashing 71 => 158
Hashing 72 => 150
Hashing 73 => 151
Hashing 74 => 152
Hashing 75 => 153
Hashing 76 => 154
Hashing 77 => 155
Hashing 78 => 156
Hashing 79 => 157
Hashing 80 => 158
Hashing 81 => 150
Hashing 82 => 151
Hashing 83 => 152
Hashing 84 => 153
Hashing 85 => 154
Hashing 86 => 155
Hashing 87 => 156
Hashing 88 => 157
Hashing 89 => 158
Hashing 90 => 150
Hashing 91 => 151
Hashing 92 => 152
Hashing 93 => 153
Hashing 94 => 154
Hashing 95 => 155
Hashing 96 => 156
Hashing 97 => 157
Hashing 98 => 158
Hashing 99 => 150
Hashing 100 => 154
Hashing 101 => 155
Hashing 102 => 156
Hashing 103 => 157
Hashing 104 => 158
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 155
Hashing 111 => 156
Hashing 112 => 157
Hashing 113 => 158
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 156
Hashing 121 => 157
Hashing 122 => 158
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 157
Hashing 131 => 158
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 158
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 151
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 151
Hashing 179 => 152
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 151
Hashing 188 => 152
Hashing 189 => 153
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 151
Hashing 197 => 152
Hashing 198 => 153
Hashing 199 => 154
Hashing 200 => 155
Hashing 201 => 156
Hashing 202 => 157
Hashing 203 => 158
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 156
Hashing 211 => 157
Hashing 212 => 158
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 157
Hashing 221 => 158
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 158
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

Nous avons nous PRECISEE 8 valeurs ... C'est mauvais ... Notre fonction d' origine mappée S(∞)sur S(256). C'est-à-dire que nous avons créé un mappage de fonction surjective$input vers $output.

Puisque nous avons une fonction Surjective, nous n'avons aucune garantie que le mappage pour tout sous-ensemble de l'entrée n'aura pas de collisions (en fait, ils le feront).

Voilà ce qui s'est passé ici! Notre fonction était mauvaise, mais ce n'est pas pourquoi cela a fonctionné (c'est pourquoi cela a fonctionné si rapidement et si complètement).

La même chose se produit avec MD5. Il correspond S(∞)à S(2^128). Puisqu'il n'y a aucune garantie que l'exécution MD5(S(output))sera Injective , ce qui signifie qu'elle n'aura pas de collisions.

Section TL / DR

Par conséquent, étant donné que la réinjection md5directe de la sortie peut générer des collisions, chaque itération augmentera les risques de collisions. Cependant, il s'agit d'une augmentation linéaire, ce qui signifie que même si l'ensemble de résultats de 2^128est réduit, il n'est pas significativement réduit suffisamment rapidement pour être un défaut critique.

Alors,

$output = md5($input); // 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities

Plus vous répétez de fois, plus la réduction va loin.

The Fix

Heureusement pour nous, il y a un banal moyen de résoudre ce problème: Rétroagissez quelque chose dans les autres itérations:

$output = md5($input); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities    

Notez que les autres itérations ne sont pas 2 ^ 128 pour chaque valeur individuelle de $input. Cela signifie que nous pouvons être en mesure de générer des $inputvaleurs qui entrent toujours en collision le long de la ligne (et donc s'installent ou résonnent à 2^128des sorties bien inférieures aux possibilités). Mais le cas général $inputest toujours aussi solide qu'il l'était pour un seul tour.

Attendez, n'est-ce pas? Testons cela avec notre ourHash()fonction. Passage à $hash = ourHash($input . $hash);, pour 100 itérations:

Hashing 0 => 201
Hashing 1 => 212
Hashing 2 => 199
Hashing 3 => 201
Hashing 4 => 203
Hashing 5 => 205
Hashing 6 => 207
Hashing 7 => 209
Hashing 8 => 211
Hashing 9 => 204
Hashing 10 => 251
Hashing 11 => 147
Hashing 12 => 251
Hashing 13 => 148
Hashing 14 => 253
Hashing 15 => 0
Hashing 16 => 1
Hashing 17 => 2
Hashing 18 => 161
Hashing 19 => 163
Hashing 20 => 147
Hashing 21 => 251
Hashing 22 => 148
Hashing 23 => 253
Hashing 24 => 0
Hashing 25 => 1
Hashing 26 => 2
Hashing 27 => 161
Hashing 28 => 163
Hashing 29 => 8
Hashing 30 => 251
Hashing 31 => 148
Hashing 32 => 253
Hashing 33 => 0
Hashing 34 => 1
Hashing 35 => 2
Hashing 36 => 161
Hashing 37 => 163
Hashing 38 => 8
Hashing 39 => 4
Hashing 40 => 148
Hashing 41 => 253
Hashing 42 => 0
Hashing 43 => 1
Hashing 44 => 2
Hashing 45 => 161
Hashing 46 => 163
Hashing 47 => 8
Hashing 48 => 4
Hashing 49 => 9
Hashing 50 => 253
Hashing 51 => 0
Hashing 52 => 1
Hashing 53 => 2
Hashing 54 => 161
Hashing 55 => 163
Hashing 56 => 8
Hashing 57 => 4
Hashing 58 => 9
Hashing 59 => 11
Hashing 60 => 0
Hashing 61 => 1
Hashing 62 => 2
Hashing 63 => 161
Hashing 64 => 163
Hashing 65 => 8
Hashing 66 => 4
Hashing 67 => 9
Hashing 68 => 11
Hashing 69 => 4
Hashing 70 => 1
Hashing 71 => 2
Hashing 72 => 161
Hashing 73 => 163
Hashing 74 => 8
Hashing 75 => 4
Hashing 76 => 9
Hashing 77 => 11
Hashing 78 => 4
Hashing 79 => 3
Hashing 80 => 2
Hashing 81 => 161
Hashing 82 => 163
Hashing 83 => 8
Hashing 84 => 4
Hashing 85 => 9
Hashing 86 => 11
Hashing 87 => 4
Hashing 88 => 3
Hashing 89 => 17
Hashing 90 => 161
Hashing 91 => 163
Hashing 92 => 8
Hashing 93 => 4
Hashing 94 => 9
Hashing 95 => 11
Hashing 96 => 4
Hashing 97 => 3
Hashing 98 => 17
Hashing 99 => 13
Hashing 100 => 246
Hashing 101 => 248
Hashing 102 => 49
Hashing 103 => 44
Hashing 104 => 255
Hashing 105 => 198
Hashing 106 => 43
Hashing 107 => 51
Hashing 108 => 202
Hashing 109 => 2
Hashing 110 => 248
Hashing 111 => 49
Hashing 112 => 44
Hashing 113 => 255
Hashing 114 => 198
Hashing 115 => 43
Hashing 116 => 51
Hashing 117 => 202
Hashing 118 => 2
Hashing 119 => 51
Hashing 120 => 49
Hashing 121 => 44
Hashing 122 => 255
Hashing 123 => 198
Hashing 124 => 43
Hashing 125 => 51
Hashing 126 => 202
Hashing 127 => 2
Hashing 128 => 51
Hashing 129 => 53
Hashing 130 => 44
Hashing 131 => 255
Hashing 132 => 198
Hashing 133 => 43
Hashing 134 => 51
Hashing 135 => 202
Hashing 136 => 2
Hashing 137 => 51
Hashing 138 => 53
Hashing 139 => 55
Hashing 140 => 255
Hashing 141 => 198
Hashing 142 => 43
Hashing 143 => 51
Hashing 144 => 202
Hashing 145 => 2
Hashing 146 => 51
Hashing 147 => 53
Hashing 148 => 55
Hashing 149 => 58
Hashing 150 => 198
Hashing 151 => 43
Hashing 152 => 51
Hashing 153 => 202
Hashing 154 => 2
Hashing 155 => 51
Hashing 156 => 53
Hashing 157 => 55
Hashing 158 => 58
Hashing 159 => 0
Hashing 160 => 43
Hashing 161 => 51
Hashing 162 => 202
Hashing 163 => 2
Hashing 164 => 51
Hashing 165 => 53
Hashing 166 => 55
Hashing 167 => 58
Hashing 168 => 0
Hashing 169 => 209
Hashing 170 => 51
Hashing 171 => 202
Hashing 172 => 2
Hashing 173 => 51
Hashing 174 => 53
Hashing 175 => 55
Hashing 176 => 58
Hashing 177 => 0
Hashing 178 => 209
Hashing 179 => 216
Hashing 180 => 202
Hashing 181 => 2
Hashing 182 => 51
Hashing 183 => 53
Hashing 184 => 55
Hashing 185 => 58
Hashing 186 => 0
Hashing 187 => 209
Hashing 188 => 216
Hashing 189 => 219
Hashing 190 => 2
Hashing 191 => 51
Hashing 192 => 53
Hashing 193 => 55
Hashing 194 => 58
Hashing 195 => 0
Hashing 196 => 209
Hashing 197 => 216
Hashing 198 => 219
Hashing 199 => 220
Hashing 200 => 248
Hashing 201 => 49
Hashing 202 => 44
Hashing 203 => 255
Hashing 204 => 198
Hashing 205 => 43
Hashing 206 => 51
Hashing 207 => 202
Hashing 208 => 2
Hashing 209 => 51
Hashing 210 => 49
Hashing 211 => 44
Hashing 212 => 255
Hashing 213 => 198
Hashing 214 => 43
Hashing 215 => 51
Hashing 216 => 202
Hashing 217 => 2
Hashing 218 => 51
Hashing 219 => 53
Hashing 220 => 44
Hashing 221 => 255
Hashing 222 => 198
Hashing 223 => 43
Hashing 224 => 51
Hashing 225 => 202
Hashing 226 => 2
Hashing 227 => 51
Hashing 228 => 53
Hashing 229 => 55
Hashing 230 => 255
Hashing 231 => 198
Hashing 232 => 43
Hashing 233 => 51
Hashing 234 => 202
Hashing 235 => 2
Hashing 236 => 51
Hashing 237 => 53
Hashing 238 => 55
Hashing 239 => 58
Hashing 240 => 198
Hashing 241 => 43
Hashing 242 => 51
Hashing 243 => 202
Hashing 244 => 2
Hashing 245 => 51
Hashing 246 => 53
Hashing 247 => 55
Hashing 248 => 58
Hashing 249 => 0
Hashing 250 => 43
Hashing 251 => 51
Hashing 252 => 202
Hashing 253 => 2
Hashing 254 => 51
Hashing 255 => 53

Il y a encore un modèle rugueux là, mais note qu'il n'y a pas plus d'un modèle que notre fonction sous - jacente (qui était déjà assez faible).

Notez cependant que 0et sont 3devenus des collisions, même si elles n'étaient pas du seul coup. C'est une application de ce que j'ai dit auparavant (que la résistance aux collisions reste la même pour l'ensemble de toutes les entrées, mais des routes de collision spécifiques peuvent s'ouvrir en raison de défauts dans l'algorithme sous-jacent).

Section TL / DR

En réinjectant l'entrée dans chaque itération, nous brisons efficacement toutes les collisions qui peuvent s'être produites lors de l'itération précédente.

Par conséquent, md5($input . md5($input));devrait être ( théoriquement au moins) aussi fort que md5($input).

Est-ce important?

Oui. C'est l'une des raisons pour lesquelles PBKDF2 a remplacé PBKDF1 dans RFC 2898 . Considérez les boucles internes des deux:

PBKDF1:

T_1 = Hash (P || S) ,
T_2 = Hash (T_1) ,
...
T_c = Hash (T_{c-1}) 

cest le nombre d'itérations, Ple mot de passe et Sle sel

PBKDF2:

U_1 = PRF (P, S || INT (i)) ,
U_2 = PRF (P, U_1) ,
...
U_c = PRF (P, U_{c-1})

Où PRF n'est vraiment qu'un HMAC. Mais pour nos besoins ici, disons simplement que PRF(P, S) = Hash(P || S)(c'est-à- dire que le PRF de 2 entrées est le même, grosso modo, que le hachage avec les deux concaténés ensemble). Ce n'est pas du tout le cas , mais pour nous, c'est le cas.

PBKDF2 conserve donc la résistance aux collisions de la Hashfonction sous-jacente , contrairement à PBKDF1.

Attacher tout cela ensemble:

Nous connaissons des moyens sûrs d'itérer un hachage. En réalité:

$hash = $input;
$i = 10000;
do {
   $hash = hash($input . $hash);
} while ($i-- > 0);

Est généralement sûr.

Maintenant, pour expliquer pourquoi nous voudrions le hacher, analysons le mouvement d'entropie.

Un hachage prend l'ensemble infini: S(∞)et produit un ensemble plus petit et de taille cohérente S(n). L'itération suivante (en supposant que l'entrée est retransmise) correspond S(∞)à S(n)nouveau à:

S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)

Notez que la sortie finale a exactement la même quantité d'entropie que la première . L'itération ne "la rendra pas plus obscure". L'entropie est identique. Il n'y a pas de source magique d'imprévisibilité (c'est une fonction pseudo-aléatoire, pas une fonction aléatoire).

Il y a cependant un gain à itérer. Cela ralentit artificiellement le processus de hachage. Et c'est pourquoi itérer peut être une bonne idée. En fait, c'est le principe de base de la plupart des algorithmes de hachage de mot de passe modernes (le fait de faire quelque chose encore et encore le rend plus lent).

La lenteur est bonne, car elle combat la principale menace pour la sécurité: le forçage brutal. Plus notre algorithme de hachage est lent, plus les attaquants doivent travailler pour attaquer les hachages de mot de passe qui nous ont été volés. Et c'est une bonne chose !!!

ircmaxell
la source
1
$output = md5($output); // < 2^128 possibilities--- est-ce vraiment strict <, ou <=?
zerkms
2
@zerkms: Ce n'est strictement rien. Nous aurions besoin de connaître certains détails très spécifiques de la fonction sous-jacente ( md5()dans ce cas) pour vraiment savoir avec certitude. Mais en général , il sera <et non <=... Rappelez - vous, nous parlons de la taille de l'ensemble des $outputpour tous possible $inputs. Donc , si nous avons même une collision , il sera <donc <est le meilleur généralisateur.
ircmaxell
2
@ TomášFejfar Je pense que la question ne concerne pas les collisions en général, mais les collisions dans l'ensemble de sortie strict (2 ^ 128 sorties, chacune d'une largeur exacte de 128 bits). Cela pourrait être Injectif, mais pour autant que je sache, une preuve générique n'est pas possible (seulement une preuve par exemple d'une collision pour un algorithme spécifique). Considérez la fonction de hachage qui renvoie simplement l'entrée si elle est de 128 bits (et hache sinon). En général, ce serait surjectif, mais une fois alimenté, il serait toujours injectif ... C'est le point de discorde ...
ircmaxell
3
Continuons cette discussion dans le chat .
ircmaxell
6
Pour ceux qui voudraient gagner du temps en n'ayant pas à aller voir comment s'est terminée cette discussion entre Dan et ircmaxell, elle s'est bien terminée : Dan étant d'accord avec ircmaxell.
jeromej
51

Oui, le hachage réduit l'espace de recherche, mais non, cela n'a pas d'importance - la réduction effective est insignifiante.

Le ré-hachage augmente le temps nécessaire à la force brute, mais le faire seulement deux fois est également sous-optimal.

Ce que vous voulez vraiment, c'est hacher le mot de passe avec PBKDF2 - une méthode éprouvée d'utilisation d'un hachage sécurisé avec du sel et des itérations. Découvrez cette réponse SO .

EDIT : j'ai presque oublié - N'UTILISEZ PAS MD5 !!!! Utilisez un hachage cryptographique moderne tel que la famille SHA-2 (SHA-256, SHA-384 et SHA-512).

orip
la source
2
@DFTR - d'accord. bcrypt ou scrypt sont de meilleures options.
orip
N'utilisez pas non plus ceux-ci (famille SHA-2), ils peuvent désormais être facilement craqués, consultez crackstation.net pour en obtenir la preuve. Si quoi que ce soit, utilisez scrypt ou PBKDF2 qui sont des fonctions de hachage cryptographique basées sur des fonctions de dérivation de clés (KDF).
theodore
3
En 2016, Argon2 et scrypt sont ceux que tout le monde devrait s'efforcer d'utiliser
Silkfire
10

Oui - cela réduit le nombre de chaînes pouvant correspondre à la chaîne.

Comme vous l'avez déjà mentionné, les hachis salés sont bien meilleurs.

Un article ici: http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/ , tente de prouver pourquoi il est équivalent, mais je ne suis pas sûr de la logique. En partie, ils supposent qu'il n'y a pas de logiciel disponible pour analyser md5 (md5 (texte)), mais évidemment, il est assez trivial de produire les tableaux arc-en-ciel.

Je reste fidèle à ma réponse selon laquelle le nombre de hachages de type md5 (md5 (texte)) est inférieur à celui des hachages md5 (texte), ce qui augmente les risques de collision (même si cela reste improbable) et réduit l'espace de recherche.

Rich Bradshaw
la source
5

La plupart des réponses proviennent de personnes sans expérience en cryptographie ou en sécurité. Et ils ont tort. Utilisez un sel, si possible unique par enregistrement. MD5 / SHA / etc sont trop rapides, contrairement à ce que vous voulez. PBKDF2 et bcrypt sont plus lents (ce qui est bien) mais peuvent être vaincus avec des ASIC / FPGA / GPU (très abordables de nos jours). Un algorithme mémoire est donc nécessaire: entrez scrypt .

Voici une explication profane sur les sels et la vitesse (mais pas sur les algorithmes durs en mémoire).

alecco
la source
4

Je regarde cela d'un point de vue pratique. Quel est le pirate informatique après? Pourquoi, la combinaison de caractères qui, une fois passée par la fonction de hachage, génère le hachage souhaité.

Vous ne sauvegardez que le dernier hachage, par conséquent, le pirate n'a à brutaliser qu'un seul hachage. En supposant que vous avez à peu près les mêmes chances de tomber sur le hachage souhaité à chaque étape de bruteforce, le nombre de hachages n'est pas pertinent. Vous pourriez faire un million d'itérations de hachage, et cela n'augmenterait pas ou ne réduirait pas la sécurité d'un bit, car à la fin de la ligne, il n'y a qu'un seul hachage à casser, et les chances de le casser sont les mêmes que n'importe quel hachage.

Peut-être que les affiches précédentes pensent que la contribution est pertinente; ce n'est pas. Tant que tout ce que vous mettez dans la fonction de hachage génère le hachage souhaité, il vous permettra de passer à travers, une entrée correcte ou une entrée incorrecte.

Maintenant, les tables arc-en-ciel sont une autre histoire. Puisqu'une table arc-en-ciel ne contient que des mots de passe bruts, le hachage deux fois peut être une bonne mesure de sécurité, car une table arc-en-ciel qui contient chaque hachage de chaque hachage serait trop grande.

Bien sûr, je ne considère que l'exemple donné par l'OP, où il s'agit simplement d'un mot de passe en texte brut haché. Si vous incluez le nom d'utilisateur ou un sel dans le hachage, c'est une autre histoire; le hachage deux fois est totalement inutile, car la table arc-en-ciel serait déjà trop grande pour être pratique et contenir le bon hachage.

Quoi qu'il en soit, pas un expert en sécurité ici, mais c'est exactement ce que j'ai compris de mon expérience.

Tapoter
la source
Cette réponse est fausse à tous égards. 1. Connaître l'avant-dernier hachage n'apporte aucune valeur à un attaquant, car l'entrée d'un hachage itéré est le mot de passe , qui est ensuite haché plusieurs fois (pas une fois). 2. L'espace d'entrée est des mots de passe, l'espace de sortie est des mots de passe hachés. L'espace des mots de passe typiques est beaucoup plus petit que l'espace de sortie. 3. Les tables arc-en-ciel pour les mots de passe à double hachage non salés ne sont pas plus grandes que les tables arc-en-ciel pour les mots de passe à simple hachage non salés. 4. Les noms d'utilisateur sont à faible entropie, un bon sel est aléatoire. 5. Le salage ne remplace pas l'itération. Vous avez besoin des deux.
Clement Cherlin
3

D'après ce que j'ai lu, il peut être recommandé de ré-hacher le mot de passe des centaines ou des milliers de fois.

L'idée est que si vous pouvez prendre plus de temps pour encoder le mot de passe, il est plus difficile pour un attaquant de passer par de nombreuses suppositions pour casser le mot de passe. Cela semble être l'avantage du ré-hachage - non pas qu'il soit plus sécurisé cryptographiquement, mais cela prend simplement plus de temps pour générer une attaque par dictionnaire.

Bien sûr, les ordinateurs deviennent plus rapides tout le temps, donc cet avantage diminue avec le temps (ou vous oblige à augmenter les itérations).

Bill Karwin
la source
Je l'ai également mentionné dans un autre commentaire, mais en.wikipedia.org/wiki/Key_stretching
2

Personnellement, je ne m'embêterais pas avec plusieurs hachages, mais je m'assurerais également de hacher le nom d'utilisateur (ou un autre champ ID utilisateur) ainsi que le mot de passe afin que deux utilisateurs avec le même mot de passe ne se retrouvent pas avec le même hachage. De plus, je lancerais probablement une autre chaîne constante dans la chaîne d'entrée pour faire bonne mesure.

$hashed_password = md5( "xxx" + "|" + user_name + "|" + plaintext_password);
CodeAndCats
la source
13
En fait, il doit s'agir d'une chaîne générée aléatoirement pour chaque utilisateur, et non d'une constante.
Bill the Lizard
7
Un secret constant fonctionne (et est plus facile à travailler), si vous ajoutez le nom d'utilisateur comme suggéré. Cela produit essentiellement une clé aléatoire spécifique à l'utilisateur.
SquareCog
4
Un sel secret constant est la sécurité par l'obscurité. Si le "secret" sort que vous utilisez "xxx" + nom d'utilisateur + mot de passe, alors un attaquant n'a même pas besoin des données de vos tables pour lancer une attaque contre lui.
Bill the Lizard
8
Je ne pense pas que ce soit la sécurité par l'obscurité. La raison d'utiliser un sel est que vous ne pouvez pas calculer une table arc-en-ciel avec plusieurs hachages md5 simultanément. Construire un pour "xxx" + mot de passe (même sel) se produit une fois. Construire une table pour "xxx" + nom d'utilisateur + mot de passe est pire que le forçage brutal.
FryGuy
5
@Bill the Lizard: "l'attaque est réduite à la construction d'un dictionnaire pour attaquer un nom d'utilisateur spécifique" n'est qu'une attaque par force brute (en fait encore pire, car en plus de calculer tous les hachages, vous devez les stocker), donc le sel fonctionne parfaitement dans ce cas.
Kornel
2

Supposons que vous utilisez l'algorithme de hachage: calculez rot13, prenez les 10 premiers caractères. Si vous faites cela deux fois (voire 2000 fois), il est possible de faire une fonction plus rapide, mais qui donne le même résultat (à savoir prendre simplement les 10 premiers caractères).

De même, il peut être possible de créer une fonction plus rapide qui donne la même sortie qu'une fonction de hachage répétée. Votre choix de fonction de hachage est donc très important: comme avec l'exemple rot13, il n'est pas donné que le hachage répété améliore la sécurité. S'il n'y a aucune recherche disant que l'algorithme est conçu pour une utilisation récursive, il est plus sûr de supposer qu'il ne vous donnera pas une protection supplémentaire.

Cela dit: pour toutes les fonctions de hachage, à l'exception des plus simples, il faudra très probablement des experts en cryptographie pour calculer les fonctions les plus rapides.Par conséquent, si vous vous protégez contre les attaquants qui n'ont pas accès à des experts en cryptographie, il est probablement plus sûr dans la pratique d'utiliser une fonction de hachage répétée. .

Ole Tange
la source
1

En général, il n'offre aucune sécurité supplémentaire pour doubler ou chiffrer quelque chose. Si vous pouvez casser le hachage une fois, vous pouvez le casser à nouveau. Cependant, cela ne nuit généralement pas à la sécurité.

Dans votre exemple d'utilisation de MD5, comme vous le savez probablement, il y a des problèmes de collision. "Double hachage" n'aide pas vraiment à se protéger contre cela, car les mêmes collisions entraîneront toujours le même premier hachage, que vous pouvez ensuite MD5 à nouveau pour obtenir le deuxième hachage.

Cela protège contre les attaques par dictionnaire, comme ces "bases de données MD5 inversées", mais il en va de même pour le salage.

Sur une tangente, le chiffrement double ne fournit aucune sécurité supplémentaire car il ne fait que générer une clé différente qui est une combinaison des deux clés réellement utilisées. Ainsi, l'effort pour trouver la «clé» n'est pas doublé car deux clés n'ont pas réellement besoin d'être trouvées. Ce n'est pas vrai pour le hachage, car le résultat du hachage n'est généralement pas de la même longueur que l'entrée d'origine.

Caisse à savon
la source
1
Tout est correct, mais je veux juste noter que l'effet du compromis sur la résistance aux collisions fortes sur MD5 est un peu disproportionné - la plupart des scénarios qui utilisent des fonctions de hachage cryptographiques ne reposent pas sur une forte résistance aux collisions, juste une faible résistance. Ils ne sont pas affectés par cette vulnérabilité.
SquareCog
1

Le double hachage n'a de sens pour moi que si je hache le mot de passe sur le client, puis enregistre le hachage (avec un sel différent) de ce hachage sur le serveur.

De cette façon, même si quelqu'un a piraté son chemin vers le serveur (ignorant ainsi la sécurité offerte par SSL), il ne peut toujours pas accéder aux mots de passe clairs.

Oui, il disposera des données nécessaires pour pénétrer dans le système, mais il ne pourra pas utiliser ces données pour compromettre les comptes externes de l'utilisateur. Et les gens sont connus pour utiliser le même mot de passe pour pratiquement tout.

La seule façon pour lui d'obtenir des mots de passe clairs est d'installer un keygen sur le client - et ce n'est plus votre problème.

Bref:

  1. Le premier hachage sur le client protège vos utilisateurs dans un scénario de «violation de serveur».
  2. Le deuxième hachage sur le serveur sert à protéger votre système si quelqu'un a mis la main sur la sauvegarde de votre base de données, il ne peut donc pas utiliser ces mots de passe pour se connecter à vos services.
Vedran
la source
1
+1 J'attendais de voir une réponse comme celle-ci, car je pensais au même scénario où vous ne voulez pas stocker le mot de passe en texte brut sur le client, mais aussi ne pas envoyer le mot de passe crypté final sur le fil pour faire un comparaison simple avec la DB.
Mark
1
N'aide pas pour les applications Web. si votre serveur est compromis, le code que votre serveur envoie au client est également compromis. L'attaquant désactivera votre hachage côté client et capturera les mots de passe bruts.
Clement Cherlin
0

Le souci de réduire l'espace de recherche est mathématiquement correct, bien que l'espace de recherche reste suffisamment grand pour que, à toutes fins pratiques (en supposant que vous utilisiez des sels), à 2 ^ 128. Cependant, comme nous parlons de mots de passe, le nombre de chaînes de 16 caractères possibles (alphanumériques, majuscules, quelques symboles ajoutés) est d'environ 2 ^ 98, selon mes calculs au dos de l'enveloppe. La diminution perçue de l'espace de recherche n'est donc pas vraiment pertinente.

En dehors de cela, il n'y a vraiment aucune différence, cryptographiquement parlant.

Bien qu'il existe une primitive de chiffrement appelée "chaîne de hachage" - une technique qui vous permet de faire quelques astuces intéressantes, comme divulguer une clé de signature après son utilisation, sans sacrifier l'intégrité du système - avec une synchronisation temporelle minimale, cette vous permet de contourner proprement le problème de la distribution initiale des clés. Fondamentalement, vous pré-calculez un grand ensemble de hachages de hachages - h (h (h (h .... (h (k)) ...))), utilisez la nième valeur pour signer, après un intervalle défini, vous envoyez sortez la clé et signez-la à l'aide de la clé (n-1). Les destinataires peuvent désormais vérifier que vous avez envoyé tous les messages précédents, et personne ne peut truquer votre signature depuis la fin de la période pour laquelle elle est valide.

Re-hacher des centaines de milliers de fois comme le suggère Bill n'est qu'un gaspillage de votre processeur. Utilisez une clé plus longue si vous craignez que les gens ne cassent 128 bits.

SquareCog
la source
1
Le hachage consiste précisément à ralentir le hachage. Il s'agit d'une fonctionnalité de sécurité clé dans la cryptographie basée sur un mot de passe. Voir les liens pour PCKS5 et PBKDF2.
orip
0

Comme plusieurs réponses dans cet article le suggèrent, il y a des cas où cela peut améliorer la sécurité et d'autres où cela le blesse définitivement. Il existe une meilleure solution qui améliorera définitivement la sécurité. Au lieu de doubler le nombre de fois que vous calculez le hachage, doublez la taille de votre sel, ou doublez le nombre de bits utilisés dans le hachage, ou faites les deux! Au lieu de SHA-245, passez à SHA-512.

Stefan Rusek
la source
Cela ne répond pas à la question.
Bill the Lizard
1
Le double hachage ne vaut pas la peine, mais doubler la taille de votre hachage l'est. Je pense que c'est un point plus précieux.
Stefan Rusek
-1

Le double hachage est moche car il est plus que probable qu'un attaquant ait construit une table pour proposer la plupart des hachages. Mieux vaut saler vos hachis et mélanger les hachis ensemble. Il existe également de nouveaux schémas pour "signer" les hachages (essentiellement le salage), mais de manière plus sécurisée.

Sargun Dhillon
la source
-1

Oui.

N'utilisez absolument pas plusieurs itérations d'une fonction de hachage conventionnelle, comme md5(md5(md5(password))). Au mieux, vous obtiendrez une augmentation marginale de la sécurité (un schéma comme celui-ci n'offre pratiquement aucune protection contre une attaque GPU; il suffit de le canaliser.) Au pire, vous réduisez votre espace de hachage (et donc la sécurité) à chaque itération que vous ajoutez . En matière de sécurité, il est sage de supposer le pire.

N'utiliser un mot de passe qui a été conçu par un cryptographe compétent pour être un hachage de mot de passe efficace et résistant à la fois la force brute et les attaques d'espace de temps. Il s'agit notamment de bcrypt, scrypt et dans certaines situations PBKDF2. Le hachage basé sur la glibc SHA-256 est également acceptable.

Hobbs
la source
-1

Je vais sortir sur un membre et dire que c'est plus sûr dans certaines circonstances ... ne me vote pas encore cependant!

D'un point de vue mathématique / cryptographique, c'est moins sûr, pour des raisons que je suis sûr que quelqu'un d'autre vous donnera une explication plus claire que je ne pourrais.

Cependant , il existe de grandes bases de données de hachages MD5, qui sont plus susceptibles de contenir le texte "mot de passe" que le MD5 de celui-ci. Donc, en double-hachant, vous réduisez l'efficacité de ces bases de données.

Bien sûr, si vous utilisez un sel, cet avantage (inconvénient?) Disparaît.

Greg
la source