web-dev-qa-db-fra.com

HMAC - Pourquoi pas HMAC pour le stockage des mots de passe?

Nota bene: Je suis conscient que le bonne réponse pour sécuriser le stockage des mots de passe est soit scrypt ou bcrypt . Cette question n'est pas pour l'implémentation dans un logiciel réel, c'est pour ma propre compréhension.

Supposons que Joe Programmer soit chargé de stocker en toute sécurité les mots de passe des utilisateurs finaux dans une base de données pour une application Web; ou stocker des mots de passe sur disque pour les connexions à un logiciel. Il sera très probablement:

  1. Obtenez $password Auprès de l'utilisateur final.
  2. Créez $nonce Comme une valeur aléatoire d'environ 64 ou 128 bits.
  3. Créez $hash = SHA256($nonce$password) et stockez $nonce Avec $hash Dans la base de données.

Première question:

Pourquoi les éléments suivants ne sont-ils pas sensiblement meilleurs que les précédents?

  1. Créez $long_string Une seule fois. Stockez-le en tant que constante dans le code d'application. $long_string Pourrait f.x. être de 2 kilo-octets de caractères aléatoires.
  2. Obtenez $password Auprès de l'utilisateur final.
  3. Créez $mac = HMAC-SHA256($long_string)[$password] (c'est-à-dire créez un MAC en utilisant le mot de passe de l'utilisateur final comme clé) et stockez ce $mac Dans la base de données.

J'imagine que le HMAC a les avantages suivants?

  • Les collisions sont moins fréquentes?
  • C'est un peu plus cher sur le plan informatique que le hachage ordinaire? (Mais pas du tout près de scrypt, bien sûr.)
  • Pour réussir une attaque par force brute dans un délai raisonnable, l'attaquant devrait avoir accès à deux choses: 1) la base de données, où $mac Est stocké, et 2) le code de l'application, où le $long_string d'origine est stocké. C'est mieux qu'une fonction de hachage, où l'attaquant n'a besoin que d'accéder à la base de données?

Mais encore, personne ne semble suggérer d'utiliser un HMAC, donc je dois mal comprendre quelque chose?

Deuxième question:

Quelles seraient les implications de l'ajout d'une valeur de sel $nonce?

  1. Créez $long_string Une seule fois. Stockez-le en tant que constante dans le code d'application.
  2. Obtenez $password Auprès de l'utilisateur final.
  3. Créez $nonce Comme une valeur aléatoire d'environ 128 bits.
  4. Créez $mac = HMAC-SHA256($long_string)[$nonce$password] et stockez $nonce Et $mac Dans la base de données.
46
Jesper M

Le but du sel est d'empêcher le partage des coûts d'attaque: si un attaquant veut attaquer deux mots de passe, cela devrait être deux fois plus cher que d'attaquer un mot de passe.

Avec votre proposition (votre "question 1"), deux utilisateurs avec le même mot de passe finiront par utiliser le même MAC. Si un attaquant a un accès en lecture à votre base de données, il peut "essayer" des mots de passe (en recalculant le MAC) et rechercher une correspondance dans la base de données. Il peut alors attaquer tous les mots de passe en parallèle, pour le prix d'en attaquer un. Si votre long_string est une constante codée en dur dans le code source de l'application, puis toutes les instances installées de l'application partagent cette constante, et cela vaut la peine (pour l'attaquant) de précalculer un grand dictionnaire de paires mot de passe-MAC, également appelé "a Table arc-en-ciel ".

Avec un nonce (votre "question 2"), vous évitez le partage des coûts. Le nonce est généralement appelé "sel". Votre long_string et l'utilisation de HMAC ne vous rapporte pas grand-chose ici (et vous n'utilisez pas HMAC pour ce pour quoi il a été conçu, d'ailleurs, vous êtes donc sur des fondations fragiles, cryptographiquement). L'utilisation d'un sel est une très bonne idée (enfin, pas l'utilisation d'un sel est au moins une très mauvaise idée) mais cela ne fait que la moitié du travail. Vous devez également avoir une procédure de hachage lente. Le point ici est que le sel empêche le partage des coûts, mais n'empêche pas d'attaquer un seul mot de passe. Attaquer un mot de passe signifie essayer des mots de passe possibles jusqu'à ce qu'une correspondance (c'est "l'attaque du dictionnaire"), et, étant donné l'imagination de l'utilisateur humain moyen, les attaques par dictionnaire ont tendance à fonctionner: les gens simplement aiment utilisant des mots de passe qui peuvent être deviné. La solution consiste à utiliser un processus de hachage qui est intrinsèquement lent, généralement en itérant la fonction de hachage plusieurs milliers de fois. L'idée est de rendre la vérification des mots de passe plus coûteuse: faire patienter 1 ms au lieu de 1 µs n'est pas une difficulté (l'utilisateur ne le remarquera pas), mais cela rendra également l'attaque du dictionnaire 1000 fois plus chère. Votre long_stringpeut être utilisé pour cela, à condition qu'il soit vraiment long (pas 2 kilo-octets, plutôt 20 mégaoctets).

HMAC peut être utilisé à la place d'une fonction de hachage brute pour renforcer un système de vérification de mot de passe, mais dans une configuration différente. Étant donné un système qui vérifie les mots de passe avec des sels et des fonctions de hachage itérées, vous pouvez remplacer la fonction de hachage par HMAC, en utilisant une clé secrète K . Cela empêche les attaques par dictionnaire hors ligne tant que vous pouvez garder K secret. Garder une valeur secrète n'est pas facile, mais il est toujours plus facile de garder un secret à 128 bits K qu'une base de données complète.

35
Thomas Pornin

HMAC ne vous achète pas beaucoup ici, mais je suppose qu'il agit comme une fonction de hachage remplaçable (remplaçable en changeant la clé). Quoi qu'il en soit, il semble juste exagéré ici.

Cependant, le premier schéma que vous proposez ci-dessus échoue de façon catastrophique si $ long_string devient connu d'un attaquant, ce qui sera probablement le cas car le code n'est probablement pas considéré comme secret (et il est généralement peu pratique de s'appuyer de toute façon sur la confidentialité du code).

Vous devez utiliser le deuxième schéma car $ nonce est nécessaire pour vous protéger contre les attaques précalculées.

3
frankodwyer