Sécuriser les mots de passe avec les hashs et les salts

Rechercher
Boutique en ligne, solution e-commerce, script PHP et PERL : RAYNETTE

Sécuriser les mots de passe avec les hashs et les salts

  • Par Emacs
  • 35 commentaires
  • 28838 lectures
  • RSS -  Atom

Comme nous le savons tous, le but d'un mot de passe est avant tout de rester connu d'une personne ou d'un groupe de personne. Sa divulgation entraine alors la perte complète de son efficacité et de sa sécurité. Lorsque le mot de passe devient connu d'une personne tierce, alors cette dernière peut par exemple s'approprier des droits sur une application et compromettre son fonctionnement normal. Un mot de passe est une donnée extrêmement sensible et convoitée par des pirates, quelle que soit l'application qui l'utilise.

Qu'est-ce qu'un mot de passe ?

Métaphoriquement, un mot de passe peut être perçu comme une clé ouvrant une / des porte(s) à celui qui le détient. Ainsi, cet utilisateur s'approprie des droits supplémentaires que d'autres personnes n'ont pas. Il ne tient alors qu'à lui de ne pas le divulguer pour que les privilèges qui lui sont conférés ne soient pas détournés par une tierce personne mal intentionnée.

Lorsqu'un / des mots de passe doivent être sauvegardés dans un système d'information (base de données, fichiers de configuration...), cela devient plus compliqué. En effet, la sécurité ne tient plus qu'à une personne mais repose à présent sur la sécurité du système d'information lui même (accès physique, identifiants pour se connecter dessus) ainsi que sur la manière dont sont stockés ces mots de passe dans le SI.

Sécurité des mots de passe dans un système d'information

Il est évident qu'une protection accrue des mots de passe dans le système d'information doit être mise en place. Le stockage des mots de passe en clair dans le système d'information devient alors impossible. Pourquoi ? Prenons le cas typique d'une base de données dans laquelle sont stockés les identifiants des utilisateurs d'un extranet d'une entreprise. Cela sous-entend qu'il y'ait une politique de sécurité à plusieurs niveaux de droits.

Un technicien n'aura pas les mêmes privilèges sur l'application que son supérieur hiérarchique. Ce dernier n'aura également pas les mêmes droits que le directeur des ressources humaines ou le PDG. Dans ce genre d'application, le mot de passe est le garant de la sécurité des données. Il faut donc le protéger assidument. Un recours au chiffrement devient alors indispensable.

Pourquoi crypter les données sensibles dans un SI ?

La réponse est simple. Il s'agit de garder confidentiel le mot de passe qui a été attribué à l'utilisateur en dehors de l'application. Il y'a aussi une part de déontologie dans la mesure où même le responsable de l'application ne devrait pas à connaître les identifiants personnels des utilisateurs. Cela ne le regarde pas. Revenons à notre exemple.

Qui dit extranet dit aussi accès à l'application depuis Internet. Il convient alors de chiffrer les données par une connexion sécurisée HTTPS et de protéger l'application contre d'éventuels accès pirates. Admettons que cet extranet ait mal été écrit et qu'il comporte une faille d'injection SQL. Un pirate pourrait alors récupérer les mots de passe enregistrés dans la base de données et pénétrer l'application sans problème avec les identifiants du PDG. Si les mots de passe sont cryptés, le pirate aura bien plus de mal à retrouver leur correspondance en clair. Dans cet exemple, l'attaque provient de l'extérieur mais qu'en est-il si la faiblesse du système se trouve à l'intérieur même de celui-ci ?

En effet, supposons qu'il faille maintenir la base de données en se connectant directement dessus. La société qui a édité l'application envoie son administrateur de base de données en intervention. Ce technicien intervient sur place mais ne fait aucunement partie de la société qui utilise l'application. Pourtant elle va manipuler la base de données. C'est à dire qu'elle pourra très probablement consulter tout ce qui s'y trouve à l'intérieur... y compris les identifiants de connexion. Si les mots de passe avaient été enregistrés en clair, il aurait pu s'approprier les accès de n'importe quel utilisateur sur l'application extranet... Malgré tout, rien ne l'empêche de se créer un nouvel utilisateur avec tous les droits directement dans la base de données. Nous verrons donc par la suite qu'un cryptage traditionnel ne suffit pas pour renforcer la sécurité d'un mot de passe.

Les méthodes de cryptage

Il en existe beaucoup. Cela peut aller des algorithmes de cryptage (pouvant être décryptés avec l'algorithme et la clé adéquate) aux algorithmes de hashage. Ce sont plutôt ces derniers que l'on a tendance à utiliser aujourd'hui. En effet, un algorithme de hashage permet de chiffrer une chaine sans possibilité d'opération inverse. Le résultat du hash produit généralement une chaine unique et de longueur fixe. C'est le cas par exemple des algorithmes MD5 et SHA1. Ainsi, lors d'une phase d'authentification, on ne compare plus deux mots de passe en clair mais deux hashes du mot de passe.

Hasher un mot de passe avec MD5

Exemple de hash avec md5()
<?php
$md5 = md5('m0tD3P4ss3');
?>

La variable $md5 contient alors une chaine unique, composée de caractères hexadécimaux et d'une longueur de 32 caractères.

Hasher un mot de passer avec SHA1

Exemple de hash avec sha1()
<?php
$sha1 = sha1('m0tD3P4ss3');
?>

La variable $sha1 contient ici une chaine unique, composée de caractères hexadécimaux et d'une longueur de 40 caractères.

Pourquoi les hashs simples ne suffisent plus ?

Cette méthode permet de chiffrer les chaines efficacement mais restent « crackables » ! « Ah bon ? Pourtant il a été écrit plus haut que l'on ne pouvait réaliser l'opération inverse !!! » Effectivement ! Néanmoins il existe sur Internet des « rainbow tables » (dictionnaires) capables de vous retourner la chaine en clair d'un md5(), d'un sha1() ou d'un autre algorithme standard de hash. Nul besoin de rappeler que les mots de passe classiques du type root, superadmin, toto... existent dans ces dictionnaires. Pour peu que le mot de passe original soit un mot du dictionnaire, il est fort probable que l'on puisse le retrouver dans une rainbow table à partir de son hash.

Hasher les mots de passe avec des « salts »

Cette technique consiste en la concaténation d'une ou plusieurs clés (appelées aussi « salt », « seed » ou « graine ») au mot de passe, puis le hashage de la chaine ainsi créée. Bien entendu, la / les clés doivent rester secrètes dans un fichier de configuration de l'application. Prenons un exemple simple de hash de mot de passe à partir de deux graines.

Hash de mot de passe avec des salts
<?php
// Déclaration des constantes
define('PREFIX_SALT', 'prison');
define('SUFFIX_SALT', 'break');
$hashSecure = md5(PREFIX_SALT.'m0tD3P4ss3'.SUFFIX_SALT);
?>

Dans cet exemple, nous allons finalement hasher avec l'algorithme MD5 la chaine suivante : prisonm0tD3P4ss3break

Ainsi le MD5 de cette chaine sera complètement différent du MD5 du mot de passe seul.

Quel est alors l'intérêt de cette technique ?

Cette technique permet de ne pas pouvoir récupérer facilement le mot de passe d'origine en clair dans une rainbow table à partir du MD5. La sécurité du mot de passe réside alors dans la complexité et la confidentialité des clés choisies.

Revenons à notre exemple de départ en considérant que les mots de passe sont cette fois-ci hashés avec cette méthode avant d'être enregistrés en base de données. Le technicien malintentionné en intervention sur la base de données pourra essayer de cracker les mots de passe à partir d'une rainbow table, il n'y parviendra pas car il ne connait pas les graines utilisées et la méthode de cryptage employée. De même, s'il reconnait le format MD5 des mots de passe et qu'il enregistre son mot de passe sous ce format, celui-ci ne fonctionnera pas au moment de l'identification. En effet, le hash de la concaténation de son mot de passe saisi et des deux graines ne correspondra pas au hash qu'il a enregistré juste avant dans la base de données.

Cette technique peut-elle servir en dehors des mots de passe ?

La réponse est oui ! Cette technique est employée pour chiffrer des informations placées dans un champ caché de formulaire ou bien dans un cookie. La technique du seed permet notamment de vérifier l'intégrité d'une donnée enregistrée entre chaque page. Cela revient à dire que si son hash ne correspond pas avec le hash attendu, c'est que le client a modifié l'information de son côté pour tenter de gruger votre application. Il sera donc possible d'appliquer les traitements adéquats : message d'erreur, bannissement de l'utilisateur pendant un certain temps...



Les commentaires

1. Par Xireus le 14/01/2008 19:50

Pas mal le tuto ! Comme je te l'ai dit, tu devrais parler de crypt() et de son utilisation pour blowfish. Pourquoi ne pas aussi introduire OpenSSL et mcrypt ?

Ca en reste quand même un bon tuto .

2. Par Emacs le 14/01/2008 19:54

Merci Xireus pour le commentaire. Effectivement Crypt() permet en plus d'utiliser un salt en paramètre facultatif. Je vais songer à ajouter ça en plus au tutoriel.

3. Par chubie le 02/02/2008 19:04

Un tutoriel très intéressant. Je connaissais md5 et sha1 mais pas la méthode des graines.

4. Par Divx le 07/02/2008 22:36

Il est vrai que le crypt() je l'utilise tout le temp je connaissai la technique des graines une technique peut rependut mais trés efficace !

5. Par Chrisvip le 09/02/2008 22:33

Mais si tu mets "prison" et "break" dans le mot de passe, le MD5 change mais pas le mot de passe quand on l'entre ? C'est-à-dire quand je me connecte sur un espace membres...

6. Par Emacs le 10/02/2008 00:36

Je ne comprends pas où tu veux en venir. Les salts doivent obligatoirement restées secrètes et être concaténées à chaque fois que tu veux tester l'intégrité du mot de passe.

7. Par Huraken le 11/02/2008 03:52

La comparaison du mot de passe se fera toujours avec la concaténation des graines et le md5 de la base de données, çà ne va pas changer le mot de passe de départ.
Sympa ce tutoriel, je connaissais pas les graines non plus.

8. Par Chrisvip le 11/02/2008 18:13

Je sais si j'utilise ça dans un de mes sites, lorsque les membres vont taper leurs mots de passe, comment ils vont faire ?

9. Par Emacs le 11/02/2008 21:41

Et bien lorsqu'ils tapent le password, tu concatènes les salts puis tu calcules le hash md5(). Il ne te reste plus qu'à le comparer avec celui enregistré en BDD. Si les deux hashes sont identiques, c'est que la concaténation des salts+PWD est identique et donc que le password tapé est correct.

10. Par Chrisvip le 12/02/2008 20:01

Ah d'accord, mais on m'a dit que le MD5 est indécryptable, pourquoi utiliser ça alors ?

11. Par Emacs le 12/02/2008 20:44

Tu n'as pas lu le tutoriel correctement. Certes md5() ne permet pas d'être décrypté. C'est le principe du hash. Mais il existe plusieurs solutions pour cracker un password en md5() comme par exemple :

* l'attaque par brute force qui va tester toute une série de combinaisons possibles jusqu'à trouver la bonne.

* l'utilisation du "rainbow table" (dictionnaire) qui va te retourner la correspondance en clair à partir d'une chaine md5(). Si le mot de passe et son md5 sont référencés dans une rainbow table, alors il n'y a plus de sécurité par rapport au password.

Avec la méthode du salt, ça permet de renforcer la sécurité des passwords car il devient plus difficile de retrouver la chaine originale.

En somme : La complexité de la salt + la complexité du password + la sécurité de la salt (connue de personne) = sécurité du password

12. Par Maxime le 01/03/2008 22:47

Tuto intéressant, mais j'ai une question à vous soumettre. Supposons qu'une personne à réussi à découvrir les mots de passe cryptés dans le système d'information avec une sécurité maximum. Supposons que pour vérifier le mot de passe des membres, le site utilise un système de cookie et qu'un cookie contient le mot de passe crypté du membre, au cas où celui-ci serait volé. Le mot de passe du cookie correspond donc au mot de passe du système d'information. Imaginons maintenant que la personne qui a réussi à découvrir les mots de passe dans le système d'information créé le cookie contenant le mot de passe des membres, qu'il y place le mot de passe d'un membre prélevé dans le système d'information, vous serez d'accord qu'il jouira des mêmes droits de celui de qui il a pris la place.

Comment parer ce problème ?

13. Par Paul Henri Bonnement le 02/03/2008 00:11

Tutoriel sympa, première fois que je vois cette méthode des graines, et je dois avouer qu'elle me plait bien
Pourquoi ne pas pousser le vice en passant plusieurs algo de cryptage sur les mot de passe ? :o

14. Par Emacs le 02/03/2008 00:42

@Maxime : pour parer ce problème c'est tout simple. Il y'a une règle d'or à respecter : ne jamais transmettre de mot de passe (même crypter) dans un cookie. Il vaut mieux y stocker une chaine cryptée qui a par exemple été construite avec la concaténation du login, email et chaine aléatoire au moment de l'inscription. Cette nouvelle chaine est placée dans le cookie et en base de données. Le problème c'est que malgré tout, si le pirate arrive à voler ce cookie, alors il pourra se logguer sur le compte de la personne. Donc la meilleure manière de se protéger à se niveau là est de ne pas implémenter de système de reconnaissance automatique et d'obliger l'utilisateur à se logguer à chaque passage. C'est certes moins ergonomique et "user-friendly" mais c'est aussi la méthode la plus "secure".

@Paul Henri : on pourrait très bien pousser le vice en multipliant les algorithmes de cryptage. Par exemple faire un sha1() sur un md5(). Il n'appartient qu'au développeur de choisir la stratégie de sécurité à appliquer à son application et à ses passwords. Cette méthode des salts avec un seul md5() ou sha1() s'avère déjà très efficace dans la mesure où les graines sont bien évidemment gardées secrètes.

15. Par Nicolas c'est moi le 03/04/2008 16:37

En français, on dit chiffrer et pas crypter qui est un angliscisme.

Sinon le fait d'appliquer plusieurs algorithme de chiffrement successivement n'améliore pas la sécurité.

16. Par Emacs le 03/04/2008 18:50

Oh Nicolas ! Merci pour ton passage et ton commentaire

Je note ta remarque concernant l'abus de langage. Je vais mettre l'article à jour.

Par contre, pourrais-tu nous expliquer pourquoi le fait d'appliquer plusieurs algorithmes de chiffrement n'améliore pas la sécurité stp ? Serait-ce parceque ça renforce les risques de collision ?

17. Par dinette le 10/04/2008 20:02

Tuto très sympa et fort intéressant mais ce qui m'intrigue, c'est que si le membre perd son mot de passe comment le récupérer et le lui renvoyer? Moi cela m'est déjà arrivé plusieurs fois. Il est crypté dans la db donc irrécupérable.

18. Par Emacs le 10/04/2008 21:27

Bonsoir Dinette,

On ne renvoie pas le password d'origine puisque l'on ne peut pas le retrouver. Dans ce cas, on est obligé de générer un nouveau password et de l'envoyer à l'utilisateur pour qu'il puisse se relogguer et le changer.

19. Par pagesdiv le 03/05/2008 12:49

Crypt est une racine grecque tres ancienne signifiant caché.
Donc NE FAIT ABSOLUMENT PAS partie du patrimoine parlé celte/saxon.
Crypter n'est donc pas un anglicisme. C'est une appropriation historique.

20. Par Sithran le 23/05/2008 21:00

Ce qui est intéressant, c'est de faire en sorte d'utiliser un salt différent pour chaque utilisateur, ça garantit encore plus la sécurité.

21. Par Emacs le 24/05/2008 10:33

Oui c'est exact Sithran! C'est ce que fait d'ailleurs le framework Symfony et son plug-in sfGuard de gestion des utilisateurs. Avoir une salt propre à chaque user c'est plus sécurisé mais ça implique aussi de la stocker en base de données et donc de faire un appel supplémentaire sur la BDD. Dans ce tutoriel, j'ai juste présenter le principe de fonctionnement des mots de passe hashés avec une salt

22. Par Occurrence Web le 31/07/2008 11:30

"Le technicien malintentionné en intervention sur la base de données [...] il ne connait pas les graines utilisées et la méthode de cryptage employée"

Petit B-mol : s'il est technicien, il y a toutes les chances qu'il puisse obtenir les graines utilisées en récupérant le bon script.

En tout cas les tutos sont bien conçus ici, bravo ! Niveau sécurité, j'emploie la même technique de Hash/salt à peu de chose près. Selon les projets, je rajoute une 2ème clé qui contient le hash/salt de l'IP+version du navigateur de l'internaute, uniquement utile pour un contrôle permanent PENDANT la navigation de l'internaute.

23. Par JeremyB9 le 15/09/2008 13:09

Bonjour,

J'avais lu il y a quelques jours cette méthode de graine, et il faut dire que cela peut être très intéressant.
Dans le tutoriel, on dit qu'on peut trouver un mot de pass (notament lorqu'il est hashé avec md5() ) par Brute force ou en utilisant les "raibow tables".. mais j'ai lu qu'il existait des collisions avec certains mots. Par exemple, si on hash le mot "poule" avec md5(), idem pour "frite", les deux hash peuvent donner la même chaine unique (d'où la collision) !
Alors voilà, je sais pas si il est intéressant de le rajouter dans le tutoriel ou pas, mais je l'aurai au moins dit ici.

24. Par Emacs le 15/09/2008 14:00

Salut Jérémy,

Effectivement des risques de collisions existent, c'est pourquoi il vaut mieux éviter la solution présentées telle qu'elle dans ce tutoriel. Certes, elle fonctionne bien et est efficace mais elle a surtout pour objectif de présenter le principe des salts. Dans l'idéal, la génération de la salt devrait être laissée à la portée de PHP via sa fonction crypt() qui gère nativement plusieurs algorithmes de chiffrage dont MD5, SHA1 ou Blowfish pour ne citer qu'eux.

++

25. Par OSteEL le 23/09/2008 14:38

Excellent tutoriel, clair, précis, orthographe et syntaxe soignées.

Suffisamment rare pour être souligné.

Je vais faire le tour voir ce que vous proposez d'autre

26. Par Chantal.C le 10/05/2009 17:09

Merci pour ce tuto excellent,je vais l'étudier de plus près.
A++

27. Par bruno le 18/05/2009 08:07

Une bonne idée est d'utiliser substr pour compliquer le haschage , sur un sha256 , on peut ne garder que 20 caractères , autant dire que pour retrouver le hasch complet ...

28. Par SenseiGirl57 le 23/05/2009 19:13

Il se passe quoi si on trouve grâce à des rainbow tables 2 des mots de passes saltés ? On va vite se rendre compte que dans les 2 y'a prison et break, et savoir que le mot de passe c'est ce qu'il y a entre, non ?

@bruno beaucoup plus de risques de colisions donc non ?

29. Par bruno le 25/05/2009 10:07

Bonjour Sensei .

Non , tout repose sur l'id principal qui lui doit étre vraiment unique , a partir de la , il n'y a pas de collision .


En règle générale , il n'y a pas beaucoup d'attaques sur les sites moyens , a part les attaques automatisées qui ciblent des failles courantes , qui commencent par chercher des form.php ou login.php, et les applications populaires telles phpbb et autres .

Déja , le salt est très bien et suffit . On peut d'ailleurs mettre un salt différent pour chaque membre .

Non , c'est vraiment très bien si vous n'étes pas directeur de banque .

Quand a mon commentaire , c'était juste une idée .

En fait , j'utilise sha256 et javascript pour l'authentification d'espace membre , 128 carctères ça fait beaucoup , pour mes sessions j'utilise md5 et salt .


Mais c'est vrai que couper un hash rend la tache quasi impossible , du moins excessivement longue , car il faut retrouver la partie manquante plus le salt .

30. Par Florian le 08/06/2009 10:27

Bonjour à tous.
Juste pour ceux qui confondraient...
On ajoute les grains de sable avant de crypter les données.
echo md5('prison'.'m0tDeP4sSe'.'break';
// donne 59041b3344975b7abc04b1b636c75f95

Donc on ne voit plus apparaitre les grains de sables, et sur plusieurs mots de passes, il n'y a pas le même début ni la même fin. C'est l'ensemble qui se retrouve crypté...

@+

31. Par JC le 26/06/2009 18:23

Hello,
Merci pour cette excellente article !

Sinon, le salt ne devrait pas plutôt être appliqué après avoir hashé le mdp ?
Comme 'salt'.md5(mdp) ?

Car sur le site de php on peut remarquer à l'exemple 3 que le salt est appliqué après hashage du mdp:

http://fr3.php.net/manual/fr/function.crypt.php

++

32. Par SenseiGirl57 le 19/07/2009 02:02

@Florian, oui mais on s'en rendra compte dans la rainbow table.

33. Par sceptique le 03/08/2009 20:16

Je voudrais savoir comment on peut encrypter AVANT un post method. Car peu importe le hash les données doivent être envoyer à une page de réponse. Exemple
<form action="login.php" method="post">
<input type="password" id="pass" name="pass" value="" />

la valeur $_POST['pass'] contient le mot de passe en clair, Alors comment y remedier SANS utiliser de Javascript.

34. Par Emacs le 04/08/2009 11:20

@sceptique : le seul moyen ici c'est de passer par une connexion HTTPS

35. Par julien le 09/09/2009 11:20

je me demandais comment il était possible de générer un "salt" différent pour chaque mot de passe enregistrer. L'idée me semble génial car cela signifie que même si un mot de passe devait être déchiffré, les autres restent relativement sécurisés. Mais si le "salt" est généré aléatoirement, comment faire le contrôle sur le mot de passe originel lors de la connexion d'une personne (par exemple)

Merci d'avance