Stocker un mot de passe

Par Philippe Plantier

Ça peut arriver. Après tout, ça arrive à beaucoup de monde. Même aux plus grands. Une intrusion sur votre serveur, un attaquant qui en prend le contrôle, ou bien un vol de backup. Il est possible d’analyser une intrusion, de restaurer un backup, de patcher les vulnérabilités de sécurité, de remettre tout sur pied, plus solide et robuste qu’avant, mais il est impossible de faire disparaître les données privées qui ont été compromises. Et c’est un problème.

Votre site web aura généralement des utilisateurs, qui ont généralement un login, et un mot de passe. Et ces utilisateurs ont souvent un Facebook / Twitter / Google+, un blog, un Paypal, un mail sur Gmail, Yahoo, Hotmail, une banque accessible par Internet, une foultitude de comptes sur une foultitude de blogs, sites de presse, de recettes de cuisine, sur iTunes, sur Amazon, et que sais-je d’autre. Et pour pouvoir se connecter à cette foultitudes de sites, ils auront des foultitudes de logins, avec des foultitudes de mots de passe.

Et, soyons réalistes, ils ré-utiliseront toujours le même.

Et si votre site se fait pirater, et que leur mot de passe se retrouve dans la nature, et qu’ils se font, à la suite, pirater leur mail, leur Facebook, leur compte en banque, ou leurs recettes de cuisine, ce sera de votre faute. Même si on dit que ce n’est pas bien que de ré-utiliser le même mot de passe sur plusieurs sites. Soyons clair: stocker en clair des informations confidentielles, comme un mot de passe, sur un serveur accessible par Internet est une faute professionnelle, qui peut avoir des conséquences importantes, y compris légales.

Comment va-t-on vérifier que l’utilisateur a le bon mot de passe, si on ne stocke pas ce dernier sur le serveur ? Le problème, et sa solution, sont connus depuis longtemps: le hachage cryptographique.


À la base, une fonction de hachage est une fonction qui transforme une donnée d’entrée, de taille quelconque, en une donnée de sortie, nommée empreinte, de taille connue, et fixe. Une fonction de hachage triviale, par exemple, est de faire la somme de tous les octets d’un message un par un. Une fonction de hachage cryptographique (du grec kruptos (« caché ») et graphein (« écrire ») — autrement dit, permettant de cacher ce qui est écrit) a, idéalement, les propriétés suivantes:

  1. il est relativement simple de calculer la valeur de l’empreinte à partir des données initiales,
  2. il est infaisable de générer des données dont l’empreinte correspondra à une empreinte pré-déterminée,
  3. il est infaisable de modifier un message sans faire changer son empreinte,
  4. il est infaisable de trouver deux messages différents qui ont la même empreinte.

(ici, « infaisable » signifie « impossible dans un temps raisonnable (disons, moins d’un siècle) par les ordinateurs les plus puissants qui existent »)
Puisque nous parlons ici de cryptographie, il faut également rappeler une base essentielle de la cryptographie: ne jamais tenter d’inventer soi-même son algorithme (à moins d’être un expert international en cryptographie – et encore). La cryptographie est une discipline complexe, de nombreux algorithmes de chiffrement et de hachage, y compris des algorithmes développés par des experts, se sont avérés vulnérables à des attaques, et les seuls algorithmes qui peuvent être considérés comme incassables sont ceux que personne n’a jamais réussi à casser (ça semble évident). (Voir également: principe de Kerckhoffs)

Prenons donc, ici, l’algorithme SHA-1. C’est, dans l’état actuel des connaissances, un bon algorithme de hachage cryptographique, utilisé par les banques et les services secrets ; publié en 1995 par la NSA américaine, personne n’a jamais réussi à mettre en défaut les points 2, 3 et 4 cités précédemment, et personne n’a jamais réussi à trouver des méthodes qui faciliteraient la recherche de solutions au points 2, 3 et 4. Bref, c’est un algorithme qui peut être utilisé sans crainte pour votre site web.

Voilà ce que donnerait l’algorithme SHA-1 si on l’appliquait à plusieurs messages :


On voit ici que :

  • la taille de l’empreinte est toujours la même, indépendamment de la taille du message,
  • changer un seul caractère change complètement l’empreinte.

Pour répondre à notre problème, la manière de procéder est alors la suivante:

  • Stocker, sur le serveur, le SHA-1 du mot de passe.
  • Lorsqu’un utilisateur veut se connecter, il fournit un mot de passe.
  • Prendre le SHA-1 du mot de passe fourni.
  • Le comparer au SHA-1 du mot de passe stocké.
  • Si les empreintes SHA-1 sont comparables, le mot de passe est correct. Sinon, il ne l’est pas.

Il est toujours possible d’authentifier les utilisateurs, mais aucun mot de passe n’est stocké en clair. Un attaquant qui pénètrerait sur votre serveur, ou qui volerait une sauvegarde de votre base de données, n’a donc plus aucun moyen de connaître les mots de passe de vos utilisateurs.
Non ?
Eh bien si, justement, ça ne suffit pas.

Ne pas faire ce qui est indiqué précédemment

L’attaquant dispose des empreintes SHA-1 de tous les mots de passe de tous vos utilisateurs. Il n’y a aucun moyen, à partir d’une empreinte SHA-1, de revenir au mot de passe correspondant. Mais que se passe-t-il si l’attaquant dispose des hash SHA-1 de tous les mots de passe possibles ?

Des listes d’empreintes SHA-1 de tous les mots de passe possibles (c’est-à-dire, de tous les mots de passe d’une taille déterminée, avec un jeu de caractère déterminé) existent, jusqu’à 8, 9, 10 caractères. Ces listes ont été pré-calculées par des réseaux distribués de CPU, parfois en utilisant les capacités de calcul supérieures des processeurs graphiques (GPU). En utilisant la techniques des “tables arc-en-ciel” (rainbow tables), il est possible de réduire d’un facteur de plusieurs millions la taille que peut prendre ce genre de listes, de sorte à ce qu’elles puissent tenir, selon leur taille, sur un DVD, ou sur un (gros) disque dur (je ne décrirai pas en détail ici le principe derrière les tables arc-en-ciel qui est un peu complexe à expliquer. L’article sur Wikipédia contient une bonne explication – surtout celui sur la Wikipedia anglophone, plus complet que l’article francophone).
Et grâce à celles-ci, l’attaquant pourra, si vous avez employé l’approche naïve décrite dans le chapitre précédent, obtenir quasiment instantanément les mots de passe de tous vos utilisateurs. Du moins de ceux qui ont un mot de passe de mois de 11 caractères.

La solution à ce problème est également bien connue : il suffit de “saler” les empreintes.
Le principe du salage est le suivant : si l’empreinte d’un mot de passe suffisamment commun se retrouvera forcément dans les base de données pré-calculées d’empreintes de mots de passe, la concaténation du mot de passe, et d’une chaîne de caractères suffisamment longue et complètement aléatoire n’y sera pas. On nomme cette chaîne de caractères aléatoire le “sel” (salt).


 

On procède alors de la manière suivante :

  • Avant de stocker le mot de passe de l’utilisateur, on génère un sel aléatoire.
  • On stocke, sur le serveur, à la fois l’empreinte de (sel + motdepasse), et le sel lui-même.
  • Lorsque l’utilisateur fournit un mot de passe pour tenter de s’authentifier, on concatène le sel (stocké sur le serveur), et le mot de passe (fourni par l’utilisateur)
  • Puis on calcule l’empreinte du tout
  • Enfin, on compare l’empreinte à celle stockée sur le serveur. Le mot de passe est correct si et seulement si les deux correspondent.

 

Cette fois, les empreintes stockées sur notre serveur ne peuvent plus se retrouver dans une base de données d’empreintes de mots de passe connus. Celles-ci sont donc en sécurité... non ?
Non, toujours pas.

Ne pas faire (non plus) ce qui est indiqué précédemment

L’utilisateur qui a compromis le serveur, ou volé une sauvegarde, aura accès à la fois au sel et à l’empreinte du mot de passe. Il ne pourra pas utiliser de base de données d’empreintes précalculées pour déterminer les mots de passe correspondant à une empreinte donnée, mais rien ne l’empêche de tenter de calculer l’empreinte de tous les mots de passe possibles, un par un.
D’ailleurs, combien y a-t-il de mots de passe possibles ? Faisons le calcul :

Nombre de mots de passe possibles
Taille Type de mot de passe Exemple Possibilités (formule) Possibilités (milliards)
(variable) mot du dictionnaire français empoisser ≈ 35000 0,000035
8 lettres minuscules iberallt 268 209
8 lettres minuscules + chiffres sgm0r51r 368 2821
8 lettres minuscules + majuscules + chiffres fJpo57SI 628 218 340
8 lettres minuscules + majuscules +chiffres + ponctuation ,zBca_y0 948 6 095 698
(variable) 4 mots courants du dictionnaire version loyauté prouvé services 100004 10 000 000
12 lettres minuscules + majuscules + chiffres + ponctuation 1Sju;{9e:Q!D 9412 475 920 314 814 253

Bien. SHA-1 est un algorithme de hachage très sûr. Incassable. L’un des plus sûrs du monde. Mais il a un gros défaut.

Il est rapide.

L'algorithme SHA-1 n'a pas été spécialement prévu pour hacher des mots de passe, mais plutôt comme algorithme générique de génération d'empreintes cryptographiques, et, à ce titre, a été prévu pour être peu coûteux à calculer. La performance, en informatique, est généralement un avantage, mais elle est là notre ennemie : elle simplifie grandement la vie à un attaquant potentiel. En 2011, en utilisant les capacités prodigieuses qu’ont les cartes graphiques grand public (ATI, NVidia) à effectuer à une cadence très élevée certains calculs très spécifiques, des logiciels tournant sur un PC grand public peuvent calculer 2300 millions d’empreintes SHA-1 par seconde, nous pouvons donc déduire du tableau précédent les temps de calcul suivants pour casser les mots de passe de nos utilisateurs :

temps de calcul pour casser un mot de passe
Taille Type de mot de passe Temps de calcul
(variable) mot du dictionnaire français 15,2 microsecondes
8 lettres minuscules 91 secondes
8 lettres minuscules + chiffres 20 minutes
8 lettres minuscules + majuscules + chiffres 36 heures
8 lettres minuscules + majuscules + chiffres + ponctuation 30 jours
(variable) 4 mots courants du dictionnaire 50 jours
12 lettres minuscules + majuscules + chiffres + ponctuation 6,5 millions d’années


Ce que nous venons de montrer là, c’est que tout mot de passe qui ne ressemble pas à 1Sju;{9e:Q!D peut, de toutes façons, être cassé dans un temps très raisonnable.
Il n’y a plus qu’une seule solution : il faut trouver un algorithme qui soit beaucoup plus long à calculer. Par exemple, si nous avions un algorithme qui était 50000 fois plus long à calculer que SHA-1, il pourrait toujours se calculer en 21 microsecondes sur notre logiciel de craquage de mots de passe bassé sur le GPU, mais nous aurions alors le tableau suivant:

temps de calcul pour casser un autre type de mot de passe
Taille Type de mot de passe Temps de calcul
(variable) mot du dictionnaire français 0.76 secondes
8 lettres minuscules 52 jours
8 lettres minuscules + chiffres 150 ans
8 lettres minuscules + majuscules + chiffres 4,2 millénaires
8 lettres minuscules + majuscules + chiffres + ponctuation 6,8 millénaires
(variable) 4 mots courants du dictionnaire 50 jours
12 lettres minuscules + majuscules + chiffres + ponctuation 328 milliards d’années

Ce qui serait, pour ce qui nous concerne, bien plus satisfaisant.

Ce n’est pas très compliqué d’avoir un algorithme 50000 fois plus long à calculer que SHA-1 : SHA1(sel, SHA1(sel, motdepasse)) est 2 fois plus long à calculer, SHA1(sel, SHA1(sel, SHA1(sel, motdepasse))) est 3 fois plus long, etc. Il suffit juste de réitérer, de manière récursive, l’opération le nombre de fois souhaitées.  On appelle ça de l’étirement de clé (key stretching). Il est également possible d'utiliser des algorithmes dédiés dont le comportement est similaire, comme bcrypt.

Considérant que, comme souvent lorsqu'il s'agit de sécurité, il vaut mieux utiliser des bibliothèques qui ont fait leurs preuves plutôt que de mettre au point son propre algorithme, les systèmes UNIX, ainsi que les versions récentes de PHP, fournissent une fonction « crypt » permettant d’obtenir des empreintes pour lesquelle le temps de calcul sera arbitrairement long, en utilisant un procédé similaire à celui décrit ci-dessus. Les versions modernes de crypt gèrent plusieurs algorithmes de hachage spécialisé: bcrypt, SHA-2 avec étirement de clé mais aussi, pour des raisons de compatibilité, des algorithmes basés sur DES et MD5 (à éviter d'utiliser sur de nouveaux systèmes car ils ont des faiblesses reconnues).

Il exist des bibliothèques permettant de simplifier l’utilisation de la fonction crypt, ainsi que de la simuler sur les systèmes trop anciens ne la supportant pas. Par exemple, en PHP, la bibliothèque phpass (http://www.openwall.com/phpass/) fait exactement cela.

Le code ci-dessous illustre un exemple d’utilisation de phpass :

require 'PasswordHash.php';

$hasher = new PasswordHash(16, false); # 2^16 = 65536 itérations

# Hachage du mot de passe :
$password = 'bonjour';
$hash = $hasher->HashPassword($password);
# => Stocker $hash sur le serveur
# (hash contient à la fois le sel, le nombre d’itérations, et
# l’empreinte)

# Vérification du mot de passe :
$check = $hasher->CheckPassword($password, $hash);
if ($check) {
    # Mot de passe OK
} else {
    # Mauvais mot de passe
}

Tags: 

Partager sur mes réseaux