Chiffrement et hash en PHP contre l'attaque Man in the middle


prcdentsommairesuivant

III. Protection contre le vol de mot de passe

Pour viter le vol du mot de passe, il faut limiter, voire proscrire, sa transmission et son stockage en clair. la place, on va manipuler un hash, beaucoup scuris car il est (thoriquement) impossible inverser. En pratique, c'est "rversible". Le hacker utilisera par exemple des dictionnaires pour obtenir le mot de passe en clair. Nous verrons comment lui compliquer la vie avec la technique du grain de sel.

III-A. Hash du mot de passe

III-A-1. Utilit du hash

Le hash est un procd de chiffrement irrversible. Ceci implique qu'on ne peut appliquer la transformation inverse, et qu'on ne peut donc que comparer les hashs entre eux. L'avantage est que personne, y compris le webmaster, n'a accs aux mots de passe des membres.

Il existe deux principaux algorithmes de hash, le MD5 et le SHA1.

Le MD5 est dprci et NE DOIT PLUS ETRE UTILISE. En effet, une faille dans l'algorithme a t trouve, permettant de diminuer considrablement le nombre d'oprations ncessaires un inversement par rapport une attaque force brute. De plus, et c'est le plus inquitant, il a t montr qu'il tait possible de crer volontairement des collisions avec un document donn. En effet, les hashs MD5 sont constitus de 32 caractres alphanumriques, soit 3632= 6.3349 combinaisons possibles. Ainsi, le nombre de hashs possibles est limit. Or, nous ralisons des hashs de chane de caractres (strings, fichiers...) de taille indetermine et pour la plupart bien plus grandes que 32 caractres. Par exemple, nous pouvons constituer 21556 chanes de 1000 caractres alphanumriques.
Nous avons donc une infinit de chane de caractres pouvant donner un hash prcis et il est maintenant possible de dterminer instantanment les autres chanes de caractres partir d'une des chanes et du hash.

Il est donc recommand d'utiliser des algorithmes plus robustes, comme le SHA1 ou le SHA-256. Mais il faut bien garder en tte qu'un algorithme est reconnu comme fiable uniquement car il n'a pas encore t craqu. Ainsi, le SHA1 est dj sur la pente descendante, le nombre d'oprations ncessaires l'inversion ayant t ramen de 2^80 2^63.

Maintenant que nous avons prsent le principe du hash et les principaux algorithmes, nous allons voir un exemple pratique de mise en place ct serveur et ct client.

Vous pouvez tester un code de ce type sur votre serveur pour avoir la liste des hashs supports par votre installation de PHP.

Afficher la liste des hashs supports
Sélectionnez

print_r(hash_algos());
Rsultat
Sélectionnez

Array
(
    [0] => md4
    [1] => md5
    [2] => sha1
    [3] => sha256
    [4] => sha384
    [5] => sha512
    [6] => ripemd128
    [7] => ripemd160
    ...
);

III-A-2. Mise en place

III-A-2-a. Cot serveur

Le hash cot serveur empche, en cas de vol de la base de donne par intrusion sur le serveur, de fournir au hacker tous les mots de passe en clair des membres du site. Il faut savoir que la plupart des membres ne possdent que 2 ou 3 mots de passe qu'ils utilisent sur presque tous les sites. Le webmaster doit donc s'assurer de son ct de l'intgrit des mots de passe de ses visiteurs. Pour effectuer un hash cot serveur, il existe la fonction SHA1 incorpore PHP. On l'appelle lors de l'inscription du membre puis, chaque connexion, on compare les 2 hashs.

Manipulation du hash lors de l'inscription
Sélectionnez

// A l'inscription
$pass = $_POST['pass'];
$login = $_POST['login'];
mysql_query("
    INSERT INTO membres (login, pass) 
    VALUES ('".mysql_real_escape_string($login)."', '".sha1($pass)."')");
Manipulation du hash la connexion
Sélectionnez

// A la connexion
$pass = $_POST['pass'];
$login = $_POST['login'];
$result = mysql_query("
    SELECT 1 
    FROM membres 
    WHERE (login='".mysql_real_escape_string($login)."')
    AND (pass='".sha1($pass)."')");
if (mysql_num_rows($result) > 0)
{
    // on connecte
}

III-A-2-b. Cot client

L'utilisation du hash cot client garanti l'intgrit du mot de passe lors de son transit depuis le navigateur du client vers le serveur. Il faut donc encoder le mot de passe avant de l'envoyer. Ceci peut tre fait par un langage de script comme le Javascript. La mise en place de cette technique est plus lourde que le hash cot serveur (car ces langages de script n'implmentent pas de fonction MD5 ou SHA1), et peut ne pas fonctionner si le client a dsactiv Javascript. Il faudra alors prvoir une mthode alternative (transmission du mot de passe en clair). La premire tape sera donc d'crire, ou de rcuprer une fonction de hash pour le langage de script que vous aurez choisi. Nous utiliserons ici une implmentation en Javascript de l'algorithme SHA1.

Raliser un hash cot client n'empche absolument pas le vol de session, il suffira au hacker d'envoyer le hash au lieu du mot de passe. Il faut bien comprendre que l'utilisation du hash n'est faite que pour protger le mot de passe.

Plusieurs sites rputs utilisent cette technique. Par exemple, si vous fouillez un peu sur la page d'identification de Yahoo!, vous trouverez un lien vers ce fichier. Cette mme implmentation est utilise dans d'autres scripts, notamment de forum.
Le fichier Javascript que nous utiliserons pour le sha1 provient de la mme source dont le lien est disponible en fin d'article.

Un fichier oprationnel est disponible ici (sha1hash.zip)

Formulaire cot client
Sélectionnez

<script type="text/javascript" src="sha1hash.js"></script>
<form method="post" action="" id="connexion-form" name="connexion-form" onsubmit="sha1hash(this.mdp, this.sha1mdp);">
	Pseudo : <input type="text" name="login" id="login" size="20" autocomplete="off">
	Mot de passe : <input type="password" name="mdp" size="20" />
	<input type="hidden" name="sha1mdp" value="" />
	<input value="Valider" id="Valider" type="submit" class="bouton">
</form>
  • L'utilisateur rempli les 2 champs textes login et mdp.
  • Lorsqu'il envoie le formulaire, la fonction sha1hash est appele. Cette fonction prend en argument les objets correspondant aux champs de formulaire mdp et sha1mdp.
  • La fonction remplit le champ sha1mdp avec le mot de passe hash et vide le champ correspondant au mot de passe en clair.
  • Le formulaire est envoy au serveur.
Recption du formulaire cot serveur
Sélectionnez

// Javascript activ
if ( ! empty ($_POST['sha1mdp']) )
{
	$sha1mdp = $_POST['sha1mdp'];
}
// Javascript dsactiv
elseif ( ! empty($_POST['mdp']) )
{
	$sha1mdp = sha1($_POST['mdp']);
}

III-B. Technique du Grain De Sel (GDS)

III-B-1. Utilit du GDS

Le but premier du GDS est de protger le hash du mot de passe lors de son transfert et de son stockage. En effet, il est de plus en plus facile d'inverser un hash, nous utilisons donc le GDS afin de compliquer cette inversion.
Le grain de sel a 2 intrts trs distincts suivant s'il est utilis ct client ou serveur.

Ct serveur

Cela signifie en pratique que l'on a hash le mot de passe de l'utilisateur, pralablement concatn avec le GDS, avant de l'enregistrer dans la base de donnes.
Ceci empche le hacker, en cas de vol de la base de donnes, d'utiliser la force brute ou un dictionnaire pour inverser les hashs. En effet, comme le GDS est secret, le hacker ne pourra pas constituer de dictionnaire adapt. De plus, la force brute sera vraiment plus longue, car si le GDS est une chane de 32 caractres et le mot de passe une chane de 8 caractres alors il faudra tester 2.812 combinaisons sans GDS ou 1.862 avec GDS.
Le fondement de cette protection est que le hacker ne connat pas la valeur du GDS.

Cette utilisation du GDS n'empche absolument pas la force brute provennant d'un formulaire utilisateur. Pour empcher cela, il faudra mettre un timer entre chaque tentative ou un bloqueur au bout de x tentatives.

Ct client

Le GDS ct client suppose que le GDS ne soit pas secret. Il ne peut donc pas protger des attaques par force brute. Par exemple, le mot de passe est "lol" et le GDS "azerty". On appliquera le hash sur toutes les combinaisons possibles de "aaa" "zzz" pour comparer chaque rsultat au hash qu'on souhaite inverser. Avec un GDS connu, il suffira de tester les combinaisons de "azertyaaa" "azertyzzz". Cela ne complique en rien notre recherche.

Ce type de GDS protge contre les attaques par dictionnaire. En effet, depuis quelques temps, des dictionnaires de hash sont disponibles sur le net, voire directement utilisables en ligne. On peut trouver des dictionnaires en ligne contenant plus de 500 millions de hashs rfrencs. Grce ces dictionnaires, il est possible de retrouver instantanment un mot de passe partir du hash s'il est dans le dictionnaire.
Toute l'utilit de ce GDS est contenu dans ces mots "s'il est dans le dictionnaire", car si notre mot de passe "lol" y est trs certainement, il y a peu de chances que "azertylol" y soit, et encore moins "skndgqzmsrgze6r4gb5q1fde5bhs3df4b3q4df3b4sdfblol".

Avoir un site utilisant une technique de GDS ne change rien au fait que le choix du mot de passe est crucial pour la scurit. Il est donc conseill d'utiliser des mots hors dico avec chiffres / majuscules / minuscules / caractre spciaux.

Protge Contre
Base de donnes Transfert du mdp Attaques par force brute Attaques par Dictionnaire
Ct server X X X
Ct client X X



On voit dans ce tableau rcapitulatif la faiblesse du "GDS ct client". Si le hacker arrive recueillir le GDS et le hash ct client, il pourra utiliser une technique de force brute traditionnelle, sans que nous lui ayons compliqu le travail. Le GDS ct serveur et ct client sont deux techniques complmentaires.

III-B-2. Prsentation des 3 techniques possibles

Il existe en ralit beaucoup plus de mthodes, mais elles impliquent toutes - au moins l'une des tapes- le stockage ou la transmission du mot de passe en clair.
Nous noterons ici pour chaque technique l'efficacit de la protection correspondante :

  • 0 = pas protg
  • 1 = peu protg
  • 2 = bien protg
  • 3 = trs bien protg

III-B-2-a. GDS global

Le principe ici est d'avoir un GDS gnral pour tout le site et pour tous les utilisateurs. L'avantage est la facilit de mise en oeuvre ; cependant, c'est galement la moins sre. En pratique, on a un GDS donn. l'inscription on enregistre le mot de passe en base concatn avec ce GDS et hash en SHA1. Lorsqu'un visiteur se loggue, on envoie le GDS au client, on hash en SHA1 le mot de passe concatn avec ce GDS, on envoie au serveur. Celui-ci compare simplement les deux valeurs.

Inconvnients
Il suffit de constituer un seul dictionnaire pour attaquer tous les membres du site. De plus, le GDS n'est pas secret, donc le temps des attaques par force brute n'est pas augment.

Avantages
La technique est simple, la base est protge contre les attaques par dictionnaire et mot de passe ne circule pas en clair.

Protection contre force brute Protection contre dictionnaires
Ct serveur 1 2
Ct client 0 2

III-B-2-b. GDS par utilisateur

C'est la technique que j'ai adopte. Elle est similaire la prcdente, sauf qu' l'inscription, on gnre un GDS qui sera propre l'utilisateur. Ce GDS sera conserv dans la base et envoy au client chaque demande de connexion.

Mise en place
  • l'inscription, on gnre le GDS et on le place en base
  • la connexion, le visiteur entre son login
  • On va chercher le GDS, soit par Ajax, soit en envoyant le login par formulaire, et en renvoyant le GDS
  • On demande le mot de passe, on chiffre cot client et on envoie au serveur

Inconvnients
La rcupration du GDS aprs la saisie du login implique soit deux formulaires (comme dans SPIP), soit un script Ajax

Avantages
Tout est protg contre les attaques par dictionnaire et le hacker doit constituer un dictionnaire par membre, ce qui est fastidieux et dcourageant.
Le GDS de l'utilisateur est enregistr dans la base de donnes et transite chaque connexion. Le hacker y a donc accs facilement et peut donc utiliser la force brute pour inverser le hash.

Protection contre force brute Protection contre dictionnaires
Ct serveur 0 3
Ct client 0 3

III-B-2-c. GDS par session

Cette technique consiste gnrer un GDS propre la session. On ne pourra donc pas stocker le mot de passe en base grce ce GDS. Il faudra donc prvoir une deuxime technique (GDS global ou par utilisateur) pour protger les mot de passes enregistrs.

Inconvnients
Difficile mettre en oeuvre, car elle ncessite une deuxime technique pour protger le stockage en base car on ne peut pas enregistrer en base un mot de passe protg par un GDS qui changera. Il faudrait grer 2 GDS, et effectuer ct client l'opration lourde

 
Sélectionnez

protected_mdp = SHA1(SHA1(mdp+GDS1)+GDS2)

Dans laquelle SHA1(mdp+GDS1) reprsente le mot de passe tel qu'il est stock en base.

Ainsi, toute la base est protge par le mme GDS, et en outrepassant le GDS de session ct client (rendu possible car on doit prendre en compte la possibilit que Javascript ne soit pas activ), on peut raliser une attaque par dictionnaire dans lequel le dictionnaire serait constitu pour ce GDS spcifique.
On revient au final au principe du GDS global.

Avantages
Probablement la technique la plus sre si on ne prend pas en compte la possibilit d'avoir le Javascript dsactiv, car mme si le hacker russit trouver le GDS, celui-ci changera pour chaque session (et ainsi pour chaque membre).

Protection contre force brute Protection contre dictionnaires
Ct serveur 1 2
Ct client 0 3

III-B-3. Mise en place du GDS par utilisateur

Nous allons ici approfondir la technique de protection par GDS propre un utilisateur car c'est la seule protgeant rellement d'une attaque par dictionnaire, tant donn qu'il faut constituer un dictionnaire par membre. Pour la raliser, je vais utiliser un peu d'Ajax. Comme je l'ai dit plus haut, il est possible de raliser 2 formulaires au lieu du script Ajax, c'est le choix qu'a fait le projet SPIP (cf. rubrique lien la fin de l'article). Le script Ajax utilis s'appuie sur le tutoriel de Denis Cabasson disponible ici Tutoriel Ajax
Le principe consiste lire le champ de login et appeler une page distante chaque changement observ. Ainsi, chaque saisie d'un caractre dans le champ login, nous vrifierons si un utilisateur existe avec ce login et si oui, nous renverrons son GDS.

Un fichier JS pour raliser ceci est disponible ici (getGDS.zip).
Il nous manque maintenant le formulaire et un fichier PHP renvoyant le GDS d'un utilisateur.

Ct client

Il est ncessaire dans la page d'insrer et d'initialiser les scripts de GDS et de hash, puis nous affichons le formulaire.

Insertion du code Javascript dans l'entte de l'HTML
Sélectionnez

<script type="text/javascript">
	var _adresseRecherche = "gds.php"   // l'adresse  interroger pour trouver le GDS
</script>
<script type="text/javascript" src="sha1hash.js"></script>
<script type="text/javascript" src="getGDS.js"></script>
<script type="text/javascript">
	window.onload = function()
	{
		initGetGDS(
			document.getElementById('connexion-form'),
			document.getElementById('login'),
			document.getElementById('Valider'),
			document.getElementById('gds'),
			document.getElementById('info_gds')
			);
	};
</script>

La variable JS _adresseRecherche dfinit la page distante qu'il faudra appeler pour rcuprer le GDS.
Pour initialiser le listener du champ login, nous appelons la fonction initGetGDS en lui passant comme paramtres les objets DOM

  • Du formulaire
  • Du champ login
  • Du bouton de submit
  • Du champ hidden dans lequel placer le GDS
  • Du div dans lequel on pourra indiquer si le GDS est bien rceptionn. (optionnel)
Code HTML du formulaire
Sélectionnez

<form method="post" action="" id="connexion-form" name="connexion-form" onsubmit="sha1hash(this.mdp, this.sha1mdp, this.gds);">
	Pseudo : <input type="text" name="login" id="login" size="20" autocomplete="off" />
	<div id="info_gds">Grain de sel</div><br />
	Mot de passe : <input type="password" name="mdp" size="20" /><br />
	<input type="hidden" name="sha1mdp" value="">
	<input type="hidden" name="gds" id="gds" value="">
	<input value="Valider" id="Valider" type="submit" class="bouton">
</form>

Le reste est identique au modle prsent dans le chapitre consacr au hash, avec le GDS en plus. Le hash se fera ici selon la formulaire :
sha1mdp = sha1(GDS + mdp + GDS)

Ct serveur

Il s'agit d'un script trs simple qui recherche dans la base de donnes ce login, slectionne le GDS et en fait un echo. On pourrait tablir un flux XML plus propre, mais pour l'exemple, un simple flux texte est suffisant.

Code php du fichier gds.php
Sélectionnez

if(isset($_GET['login']))
{
   $login = urldecode($_GET['login']);
}
else
{
   exit 0;
}
 
$gds = $db->get_var("SELECT me_gds FROM membres WHERE login='".mysql_real_escape_string($login)."'");
if ( !empty($gds) )
{
    echo $gds;
}

Le login est envoy par GET, on le rcupre, on applique un urldecode car on a utilis urlencode cot Javascript (dans le fichier getGDS.js).


prcdentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 25/01/2007 Guillaume Affringue. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.