• 30 minutes
  • Moyenne
Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Introduction du cours

C'est arrivé à tous les développeurs PHP de terminer leurs sites web, et de se dire "Comment je stocke les mots de passe ?" Faut-il les stocker en clair ? Est-ce qu'il suffit de les hasher ? Quels sont mes droits ? Quelles sont mes obligations ?

C'est ce que nous allons voir dans ce cours ! Les solutions proposées sont avancées, mais gardez en tête que le risque zéro n'existe pas.

Comme PHP est le langage de référence de cet article, je vous conseille d’avoir suivi le cours de Mathieu Nebra avant de le commencer.

Pourquoi sécuriser les mots de passe ?

Beaucoup de développeurs amateurs se demandent pourquoi sécuriser les mots de passe des utilisateurs. Pourquoi prendre la peine de hasher/chiffrer ce genre d'informations alors que ce ne sont pas les nôtres ?

La réponse peut être donnée sous plusieurs points de vue.

D'un point de vue sécuritaire

Un utilisateur utilise en général deux à trois mots de passe sur Internet. Il est probable que le mot de passe de M. Bob qui utilise votre site de partage de photos de voitures soit le même que celui qu'il utilise à son travail pour gérer sa centrale nucléaire. Imaginez les conséquences si un pirate récupère son mot de passe dans la base de données de votre site !

D'un point de vue déontologique

La déontologie et la bonne conduite nous poussent à préserver nos créations et leurs utilisateurs contre toutes menaces extérieures.

D'un point de vue légal

La loi Informatique et libertés demande que les données des utilisateurs aient une garantie de confidentialité. Si votre base de données est piratée et qu'aucune protection valable ne garantissait la sécurité et la confidentialité des données qui y étaient contenues, vous pouvez être tenu pour responsable devant un tribunal.

Nous allons donc maintenant voir comment sécuriser nos mots de passe grâce au chiffrement et au hashage.

Un petit mot à propos du chiffrement

Introduction

Le chiffrement permet de dissimuler des informations pour garantir leur sécurité. Le chiffrement prend toujours en compte les trois éléments suivants :

  • Le message en clair : c'est le texte ou l'information que l'on doit chiffrer ;

  • La clé : c'est la particule qui va permettre d'avoir un résultat unique, et qui permettra de déchiffrer l'information ;

  • L'algorithme : c'est la manière dont on va traiter les deux premiers éléments pour générer l'information chiffrée.

Voilà ce que ça donne en schéma :

Chiffrage
Chiffrement

Le message "Bonjour" est envoyé à l'algorithme "Arcfour Stream" qui va le chiffrer avec la clé "MaCle" et nous renvoyer le message chiffré "TKWbl/p3DA==". Si l'on voulait décrypter ce message, il suffirait de l'envoyer à l'algorithme avec la même clé et il nous renverrait "Bonjour".

En PHP

Pour chiffrer du texte en PHP, il existe l'extension mcrypt qui est très complète mais qui demande beaucoup de connaissances en cryptographie pour être maîtrisée correctement. Je vous propose donc de prendre deux fonctions trouvées sur Internet qui utilisent cette extension mais qui en proposent une utilisation beaucoup plus simple :

<?php
// Sources www.stackoverflow.com

function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>

Comme vous le voyez, ces fonctions représentent l'algorithme dans mon schéma et prennent les mêmes input (un message à chiffrer/déchiffrer et une clé).

On utilise ce code comme ceci :

<?php
$messageAChiffrer = "Coucou je suis Guillaume";
$cleSecrete = "MaCleEstIncassable";


// On chiffre le message
$messageChiffre = encrypt($messageAChiffrer, $cleSecrete);

// Pour le lire
$messageDechiffrer = decrypt($messageChiffre, $cleSecrete);
?>

Un petit mot à propos du hashage

Introduction

Le hashage permet de vérifier l'intégrité d'une information sans avoir à vérifier toute cette information.

En plus simple, un hash a les propriétés suivantes :

  • On ne peut pas déchiffrer un hash car lors de sa génération, on perd volontairement des données.

  • Peu importe ce qui rentre dans l'algorithme, la taille finale sera toujours la même (on peut hasher un fichier en md5 ou une simple chaîne de texte, ils finiront tous les deux en une série de 128 bits soit 16 caractères).

  • Le hashage d'une même donnée renverra toujours la même valeur.

Le même schéma que tout à l'heure pour le hash :

Hashage
Hashage

Le message "Bonjour" est envoyé à l'algorithme de hashage MD5 qui va lui faire subir des modifications mathématiques et des pertes de données pour le formater en une chaîne unique de 16 caractères. 

En PHP

Alors là c'est tout simple. Les algorithmes de hash sont déjà implémentés dans PHP.

Pour récupérer le hash correspondant à un string, il suffit de faire :

<?php
$bonjourEnMD5 = md5("Bonjour");
// Ou
$bonjourEnSHA1 = sha1("Bonjour");
?>

Les utilisations des hash

Les hash sont utilisés dans plusieurs cas. Dans le Web, ils sont souvent utilisés dans les systèmes d'authentification pour éviter d'avoir à traiter les mots de passe directement. Si vous avez suivi le cours PHP d'OpenClassrooms, voilà comment fonctionnent en général vos systèmes d'authentification.

Système d'authentification avec hash
Système d'authentification avec hash

C'est déjà une bonne base car le mot de passe n'est pas stocké en clair dans la base de données (DB). En revanche, cela n'est pas encore assez sécurisé.

La faille des hash

Nous avons vu que le hashage d'une donnée donnera toujours le même résultat. Ainsi, peu importe l'environnement dans lequel je vais hasher "Bonjour", en md5 le résultat sera toujours "ebc58ab2cb4848d04ec23d83f7ddf985".

Les pirates ont donc vite compris que s'ils utilisaient un programme pour hasher tous les mots du dictionnaire et remplir une base de données avec les relations "mots" -> "hash", ils pourraient rapidement "déchiffrer" les hash.

Dans un cas pratique, si un pirate récupère cette chaîne : ebc58ab2cb4848d04ec23d83f7ddf985 il pourra aller sur des sites comme md5online et en déduire le mot de passe d'Alice.

Déchiffré !
Déchiffré !

Évidemment, si le mot de passe d'Alice était "àA$xDrA", jamais le pirate n'aurait pu défaire le hash. Mais en sécurité, nous devons partir du principe que le mot de passe de l'utilisateur peut être faible.

Les grains de sel (salt)

Pour se défendre contre ce genre d'attaque, nous allons utiliser ce que l'on appelle un salt. Un salt, c'est tout simplement un suffixe ou un préfixe que l'on va ajouter au mot de passe avant de le hasher. Ainsi, il y a beaucoup moins de chance pour que notre composition soit indexée dans une base de données. En effet, s'il est logique qu'un bot ait indexé le mot "Bonjour", il est beaucoup moins probable qu'il l'ait fait pour "1t5MyH45h_Bonjour".

Ainsi, notre nouveau système d'authentification avec salt fonctionnerait ainsi :

Système d'authentification avec SALT
Système d'authentification avec salt

Désormais, si un pirate récupère notre base de données, il ne pourra pas décrypter le mot de passe des utilisateurs sans connaître le salt. On commence à avoir un système sécurisé.

Mais, si par un moyen ou un autre (bruteforce par exemple) le pirate arrive à récupérer le salt, il pourra à nouveau accéder à tous les mots de passe faibles de la base. Il manque donc encore une dernière sécurité.

Un salt dynamique

Pour éviter une attaque contre le salt par bruteforce, on va placer un salt différent pour chaque utilisateur. Beaucoup de développeurs ont l'habitude de prendre l'ID comme salt et on n'en parle plus. Le problème c'est que les pirates sont loin d'être stupides et que c'est la première chose qu'ils vont essayer. Il est plus sûr d'utiliser par exemple le pseudo chiffré par un algorithme et une clé commune à tous les utilisateurs.

Par exemple :

Système d'authentification avec SALT + Cryptage
Système d'authentification avec salt + chiffrement

Avec ce système, si un pirate récupère notre DB et qu'il veut récupérer les mots de passe de nos utilisateurs, il devrait attaquer chaque salt par bruteforce pour chaque utilisateur. Dans le meilleur des cas (pour lui !), il ne pourrait récupérer que les mots de passe faibles. Ce vecteur d'attaque est trop gourmand en temps et en énergie, le pirate aura donc plutôt tendance à essayer une attaque par bruteforce directement sur le login du site... Mais dans ce cas de figure la responsabilité revient à l'utilisateur qui a un mot de passe trop faible et non pas à l'administrateur du site qui n'a pas subi de fuite de données.

Le cadeau bonus, c'est qu'avec un salt dynamique, tout est sécurisé par le mot de passe se trouvant sous la protection du .htaccess. Vous pouvez donc partager votre code source car tant que le mot de passe, lui, n'est pas partagé, le tout reste sécurisé.

Sur le même sujet

Concevez votre site web avec PHP et MySQL

Exemple de certificat de réussite
Exemple de certificat de réussite