Édition Nº22 du 9 octobre 2012 Retour au sommaire

Le stockage des mots de passe

Entrons plus précisément dans les problématiques de stockage des mots de passe de vos utilisateurs, avec cette semaine un article de Mathieu Pillard.

Conducteur


Mathieu Pillard

Arrêts prévus :

Quel est le point commun entre Yahoo!, Gawker, Sony et LinkedIn ? Ces entreprises ont toutes ont été victimes de vols de mots de passe à grande échelle ces deux dernières années.

Les conséquences sont désastreuses, que ça soit pour leur réputation, leurs revenus, et bien sûr pour nous-mêmes, leurs utilisateurs. Nombreux sont ceux qui réutilisent leurs mots de passe d’un site à un autre (un problème déjà soulevé par Goulven Champenois dans un article précédent).

Nous allons donc voir comment un développeur peut éviter de se retrouver dans la même posture.

Principe

Le principe fondamental du stockage de mots de passe est le suivant : une fois rentré par l’utilisateur, vous ne devez jamais être capable de le récupérer. Vous avez uniquement besoin de le comparer à la chaîne de caractères que l’utilisateur saisit pour se logguer, rien de plus. Au-delà de ça, votre but doit être de compliquer la vie de toute personne tentant de le retrouver.

Pourquoi ne jamais être capable de le récupérer ? Parce que vous n’êtes pas à l’abri d’une fuite. Et il n’y a pas que les vilains pirates qui peuvent en être à l’origine : pensez aux personnes ayant les droits administrateur sur vos machines, votre support technique, vos développeurs, etc. S’il existe un moyen de lire le mot de passe en clair, alors il existe une faille de sécurité.

Il peut être tentant de stocker le mot de passe en clair pour pouvoir le renvoyer à un utilisateur qui l’aurait perdu, mais c’est une mauvaise solution d’un point de vue sécurité : non seulement vous êtes obligé de conserver une version en clair de votre côté, mais en plus, en le renvoyant, vous augmentez les chances qu’il soit intercepté par quelqu’un d’autre. Préférez donc une solution permettant de le changer plutôt que celle qui consiste à le renvoyer automatiquement par mail.

Le seul moment où le mot de passe doit être accessible en clair dans votre code est donc celui de l’identification, dans les données POST. Idéalement, ce processus devrait être effectué en HTTPS afin que la version en clair ne circule jamais sur le réseau.

Chiffrement symétrique

S’il paraît donc évident qu’il faille absolument éviter de stocker les mots de passe en clair, il est important de noter que le chiffrement symétrique pose le même genre de problèmes. On parle de chiffrement ou de cryptographie symétrique lorsque l’algorithme fonctionne dans les deux sens : il permet de chiffrer mais aussi de déchiffrer le mot de passe. Dans le cas qui nous occupe, cela revient à dire que vous disposez d’une clé secrète pour chiffrer le mot de passe, mais que vous pouvez utiliser cette même clé pour le déchiffrer et retrouver ainsi la version en clair pour la comparer à la chaîne de caractères que rentre l’utilisateur lorsqu’il tente de s’identifier. Le problème avec cette technique, c’est que vous vous retrouvez dans la même situation que s’il était en clair, puisqu’il finit toujours par être déchiffrable à un moment donné.
Quelle que soit votre solution pour cacher cette clé, vous êtes potentiellement vulnérable. Certaines des entreprises mentionnées précédemment ont cru que cette solution les mettrait à l’abri. L’histoire leur a donné tort : au même titre qu’une base de données, une clé de chiffrement peut être volée.

Pour simplifier, rappelez-vous cette règle d’or du stockage de mots de passe : toujours partir du principe que l’attaquant a déjà réussi à accéder à l’ensemble de votre architecture et à télécharger l’ensemble de vos données. Même dans ce cas de figure, vos mots de passe doivent rester inviolables.

Chiffrement à sens unique : les fonctions de hash

Il faut donc utiliser un système qui ne permette pas de garder le mot de passe d’origine. Pour cela, on dispose d’algorithmes de chiffrement à sens unique. Ces fonctions dites « de hachage » sont généralement utilisées pour vérifier l’intégrité de données, calculer une empreinte unique aussi rapidement que possible, etc. Les algorithmes les plus connus dans ce domaine sont MD5 et SHA1.

Hélas, il existe deux problèmes majeurs avec ces fonctions. Le premier gros défaut vient de l’existence des Rainbow Tables. Ces tables, générées à l’avance, permettent à partir d’un hash de retrouver très rapidement la valeur en clair correspondante, pour un coût en puissance de calcul et un temps très faibles. Elles demandent en contrepartie une taille de mémoire conséquente… mais parfaitement accessible de nos jours (même au commun des mortels), du moins pour les mots de passe de huit caractères et moins.

Le principe du « salage » (salt en anglais) a été inventé pour combattre ce problème. L’idée consiste à ajouter un préfixe aléatoire pour chaque mot de passe avant de chiffrer le tout. Stocké en clair avec le hash, il sera nécessaire lors de la vérification du mot de passe. On suppose que l’attaquant peut le connaître, mais ce n’est pas un problème : on veut seulement l’empêcher de prégénérer et de réutiliser une table de correspondance.

Malheureusement, cela ne suffit plus. En effet, le deuxième défaut est plus grave : les fonctions de hash sont conçues et optimisées pour pouvoir fournir une réponse très rapidement. Or, pour le stockage de mots de passe, c’est exactement l’inverse qu’il nous faut. Si les processeurs modernes permettent de calculer plusieurs millions de hash classiques par seconde, les GPU dont sont équipés les cartes graphiques 3D peuvent carrément calculer plusieurs milliards de hash par seconde ! Tester toutes les combinaisons possibles, technique connue sous le nom de « brute force », est maintenant à la portée de tous.

Note : il peut être tentant d’imposer des restrictions aux utilisateurs, par exemple les obliger à mettre au minimum un chiffre ou une majuscule dans leurs choix de mots de passe. Mais en le faisant, vous donnez une arme précieuse à un attaquant, qui pourra éliminer un certain nombre de combinaisons lors de sa tentative de brute force. De même, il est important de ne pas limiter le nombre de caractères (ou alors à un nombre très important, pour limiter la taille du POST) : la taille est la meilleure arme dont dispose un utilisateur pour rendre son mot de passe difficile à craquer.

Résister au brute force

La bonne nouvelle, c’est qu’il existe des fonctions à sens unique conçues et optimisées… pour être lentes. La plupart fonctionnent avec une boucle dont le nombre d’itérations est réglable. Le temps pris par chaque itération est constant et dépend juste de la puissance de la machine. Donc, en choississant un nombre d’itérations on choisit le délai qui sera appliqué à chaque vérification de mot de passe. En choississant un délai de l’ordre de la seconde, vous freinez considérablement un éventuel attaquant qui devra tester énormément de possibilités, mais vous ne gênez pas trop vos utilisateurs. Le nombre d’itérations sera stocké à côté du salt et du mot de passe hashé. Au fur et à mesure que la puissance des machines augmente, vous pouvez augmenter ce nombre. Vos utilisateurs existants n’en bénéficieront pas immédiatement, mais vous pourrez leur générer un nouveau hash lors de leur identification, puisque à ce moment-là, vous disposez temporairement du mot de passe en clair.

Notez que le salt reste présent. Par sécurité, la plupart des implémentations de ces algorithmes fournissent un moyen de générer un salt automatiquement de manière vraiment aléatoire, et il est recommandé de passer par cette méthode plutôt que de réinventer la roue. En fait, cette recommandation s’applique à toute la chaîne de la gestion des mots de passe : en réinventant ce qui existe déjà, vous vous exposez à des failles potentielles, alors qu’en réutilisant un système éprouvé par d’autres et relu par des experts dans ce domaine, vous limitez significativement les risques de laisser subsister une faille.

Les solutions

Il existe trois algorithmes qui répondent aux critères que nous venons de voir :

  • PBKDF2 est l’un des plus utilisés. Il a fait l’objet d’une RFC (RFC 2898) et est recommandé aux États-Unis par le National Institute of Standards and Technology (NIST) depuis 2010. Relativement simple à implémenter, il réutilise une fonction cryptographique au choix de l’implémenteur et l’appelle en boucle.
  • bcrypt a été inventé spécifiquement pour le stockage de mots de passe. Plus complexe à implémenter, il est aussi un peu plus difficile à craquer, notamment par les GPU. Également disponible dans de nombreux langages, c’est l’alternative de choix face à PBKDF2.
  • scrypt est une troisième alternative encore plus vicieuse. Conçue pour consommer beaucoup plus de mémoire que les autres et ainsi gêner davantage un attaquant, elle ne dispose pas encore de beaucoup d’implémentations. Cela dit, la situation pourrait vite évoluer car il est prévu d’en faire un standard IETF.

Petit tour d’horizon des implémentations

Attention : si les implémentations présentes dans les frameworks ont normalement toutes fait l’objet de vérifications et de relectures par des experts en sécurité, ce n’est pas forcément le cas de celles qui traînent sur le net. Comme mentionné précédemment, mieux vaut réutiliser une implémentation connue, testée et éprouvée par la communauté.

Le stockage des mots de passe

Note de cet article : 4 / 5

Pour pouvoir noter les articles, vous devez voyager avec un billet (c'est gratuit !).
Toutes les infos sur la page d'abonnement !
Déjà inscrit ? Connectez-vous.

Couteau-suisse du web, Mathieu est accroc aux standards, à l'Open Web et un fan de la première heure du projet Mozilla. Après être passé par Netscape, Mozilla Europe et Skyrock, il travaille maintenant à Libération, où il a entre autres participé à la refonte complète du moteur du site.

3 réactions à cet article (RSS)

iManu

#1

En plus des recommandations le protocole https et sur le cryptage qui sont fort utiles, j’aurais aimé avoir des conseils sur le stockage en base de données. Quel type de table, doit-elle être séparée des autres et comment, ou bien hors de la base dans un fichier dédié et crypté par exemple ? Bref cet aspect du stockage des mots de passe m’aurait été utile à connaitre.
Merci Mathieu !

David Bruant

#2

Il est des sujets que l’on peut aborder avec légèreté, la sécurité n’en est pas un.

Le seul moment où le mot de passe doit être accessible en clair dans votre code est donc celui de l’identification, dans les données POST. Idéalement, ce processus devrait être effectué en HTTPS afin que la version en clair ne circule jamais sur le réseau.

HTTPS n’est pas « idéalement ». C’est absolument nécéssaire si on prétend être un peu sérieux sur la sécurité ! Sinon, n’importe qui avec un téléphone et une app qui sniffe les paquets wifi peut piquer les mots de passe ! Un téléphone, tout le monde en a un, une app comme ça, ça se télécharge partout (peut-être pas dans l’Apple Store ?), donc c’est une attaque dangereuse !

Malheureusement, cela ne suffit plus. En effet, le deuxième défaut est plus grave : les fonctions de hash sont conçues et optimisées pour pouvoir fournir une réponse très rapidement. Or, pour le stockage de mots de passe, c’est exactement l’inverse qu’il nous faut.

Vu qu’il faut hasher le mot de passe à chaque connexion, on est en droit d’espérer que cette opération est assez rapide. Je serai le premier à me moquer d’un site qui me fait attendre une minute en me disant « nous hashons votre mot de passe pour être sûr que votre mot de passe est correct ». Une fonction assez rapide (ce qui est subjectif, évidemment) est une bonne chose pour une fonction de hashage !

Si les processeurs modernes permettent de calculer plusieurs millions de hash classiques par seconde, les GPU dont sont équipés les cartes graphiques 3D peuvent carrément calculer plusieurs milliards de hash par seconde ! Tester toutes les combinaisons possibles, technique connue sous le nom de « brute force », est maintenant à la portée de tous.

Millions ? Milliards ? Ca impressionne les gens qui n’y connaissent rien. L’explosion combinatoire nous protège des « milliards par seconde ».

Note : il peut être tentant d’imposer des restrictions aux utilisateurs, par exemple les obliger à mettre au minimum un chiffre ou une majuscule dans leurs choix de mots de passe. Mais en le faisant, vous donnez une arme précieuse à un attaquant, qui pourra éliminer un certain nombre de combinaisons lors de sa tentative de brute force.

Ca sera toujours mieux que de laisser les gens mettre « azerty » ou « 12345″ comme mot de passe (ce que les gens font si on les laisse faire !), ce qui les laisserait hyper sensible à des attaques par dictionnaire.

Par sécurité, la plupart des implémentations de ces algorithmes fournissent un moyen de générer un salt automatiquement de manière vraiment aléatoire

Peut-être 5-6 ans que je m’intéresse à la question et je cherche toujours comment « générer un salt automatiquement de manière vraiment aléatoire ». Des indices ?
A ma connaissance, le mieux qu’on fasse sur les machines grand public, c’est utiliser des temps d’accès disque ou des trucs comme ça comme source d’entropie. La raison pour laquelle c’est ce qu’on fait de mieux est qu’on n’a pas encore prouvé que ces trucs ne sont pas suffisamment aléatoires.
Pour tout le reste, « aléatoire » se résume souvent aux PRNG (Pseudorandom number generator), ce qui à aucun moment ne peut être qualifier de « vraiment aléatoire ».

Si je peux me permettre, je souhaite réécrire cet article de manière plus succinte:
Le stockage des mots de passe

Si vous n’y connaissez rien, déléguez votre sécurité à des experts et utilisez des services comme Mozilla Persona, ça ne pourra être que mieux.
Si vous vous y connaissez, allez embêter les gens qui font Persona avec des questions pointues. Il y a 90% de chances que ce qu’ils proposent vous suffise. Dans le cas contraire, vous en savez assez pour ne pas avoir besoin de cet article ;-)

Mathieu

#3

David Bruant: Je suis bien d’accord avec vous sur le fait de ne pas prendre le sujet à la légère ! Sur les points mentionnés:

- HTTPS est indispensable, on est bien d’accord. En fait, si on veut être vraiment sérieux, il faudrait d’ailleurs passer tout le site en HTTPS pour aussi éviter tout vol de cookie (ce qu’est en train de faire Etsy, ce qu’a déjà fait ou est en train de faire Google, Facebook, Twitter, etc). Mais soyons réalistes: une énorme majorité des sites, parfois très gros, n’est déjà pas en HTTPS, même juste pour le login. C’est hors-sujet pour cet article qui ne s’intéresse qu’au stockage, et je ne voulais pas déclencher de polémique ou faire peur aux lecteurs dès la première partie :-) Mais j’accorde volontiers que j’aurais du être plus agressif sur cette partie.

- Sur la rapidité, je mentionne justement un délai de l’ordre de la seconde, pas de la minute. La plupart des gens qui choisissent bcrypt et ses amis prennent un délai variant entre une demi seconde et deux secondes, j’ai coupé la poire en 2 dans ma suggestion.

- Sur le nombre d’opérations par seconde, et millions et milliards: j’ai volontairement été imprécis et juste donné des ordres de grandeur pour ne pas rentrer dans des spécifications techniques liés au matériel. Mais ces chiffres n’étaient pas inventés! J’aurais du ajouter les sources par contre, que l’on peut trouver assez facilement en recherchant « GPU password cracking » ou autre. Les outils classiques pouvant obtenir ces performances sont Cryptohaze Multiforcer, Extreme GPU Bruteforcer ou oclHashcat-plus, et bien d’autres encore (c’est à la mode, il en sort des nouveaux tous les matins).

- Sur les restrictions utilisateur, il sera trivial pour un attaquant de construire un dictionnaire avec des permutations lettres-chiffres classiques. Si il sait que votre site en impose, cela peut très facilement réduire son champ de recherche, dépendant des restrictions. Je suis d’accord sur le fait que les utilisateurs rentreront des mots de passes faciles à craquer en général, mais mon argument est de dire que des simples permutations comme ça ne changeront rien. Mieux vaut les éduquer que leur imposer des restrictions.

- « Vraiment aléatoire » était clairement mal choisi, mais je n’ai pas trouvé mieux pour expliquer le problème. Je n’ai jamais prétendu que cet article s’adressait à des experts en crypto cela dit :-)

En ce qui concerne Mozilla Persona, je suis assez fan et j’ai failli le mentionner dans l’article, mais c’est un sujet beaucoup plus large que juste les mots de passe. A noter par ailleurs que Mozilla utilise bcrypt dans tous leurs projets récents pour stocker des mots de passe et ils ont publié un ou deux articles sur le sujet. J’ai même fait quelques contributions à l’appli qu’ils utilisent, django-sha2 :-)

Réagir à cet article

XHTML : Vous pouvez utiliser ces balises : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>