Blog
Stocker ses mots de passes de façon securisée avec PHP

Partager

Stocker les mots de passes de nos utilisateurs de façon sécurisée est primordiale. Cela peut paraitre évident mais dans la pratique, de nombreux développeurs décident de faire l’impasse sur le chiffrement des mots de passes ou choisissent une solution inadaptée.

Constat

En quelques minutes de recherche sur pastebin, on peut trouver de nombreuses bases de données issues de divers hacks contre de grosses entreprises (Blizzard, LinkedIn, Yahoo, Adobe mais aussi une multitude de plus petits sites).

Ce qui nous interpelle, c’est que la plupart du temps, les mots de passes utilisent des algorithmes de chiffrement trop faibles au vue de la puissance de calcul des machines actuelles/des clouds

Deux algorithmes de chiffrement très populaires peuvent souffrir de ce problème : md5 et sha-1.

Nous allons voir pourquoi ces solutions ne sont pas souhaitables pour un environnement en production et les solutions qui s’offrent à nous. Nous écartons les problèmes liés à l’éducation des utilisateurs quant aux choix des mots de passes pour nous concentrer uniquement sur la partie technique.

Le problème

A rainbow table, literally

Au début des années 2000, des chercheurs chinois ont prouvé qu’il était possible de trouver des collisions avec MD5. C’est-à-dire que des chaines de caractères différentes peuvent produire un même hash (empreinte).

Aujourd'hui, un seul GPU (relativement récent) permet de générer des milliards de hashs MD5 ou SHA-1 par seconde. On comprend donc pourquoi ces algorithmes ne doivent plus être utilisés : ils sont trop facilement brutes forcables.

Les rainbow tables mettent également les mots de passes à mal. Pour faire simple, c’est une structure de données permettant de remonter à un mot de passe à partir de son hash et utilisant bien moins d’espace et de temps qu’une attaque par brute force. Lire cet article pour plus d’explications sur le sujet.

Pour ne rien arranger à la chose, vous n’avez pas plus besoin de télécharger les programmes Rainbowcrack/Ophcrack et les centaines de Go que représentent les rainbow tables, certains sites s’en chargent pour vous et vous propose une cherche clé en main.

On constate donc que les outils pour casser les mots de passes se multiplient et se simplifient mais il existe des parades simples pour se protéger.

Solutions possibles

Passe moi le salt

L’utilisation d’un salt d’une longueur suffisante permet de limiter l’efficacité des rainbow tables et des attaques par dictionnaire car ils augmentent la taille des mots de passes et les renforcent. C’est encore mieux si le salt est unique pour chaque utilisateur.

Exemple :

$salt = '©¤¡thisIsAUniqueSaltWithARandomNumberAtTheEnd¡¤©'.rand(); //rand is not truly random but it’s enough for the purpose of this article
//store the salt in your database
....
$encrypted_password = md5($password . $salt) ;

Le blowfish et son implémentation en PHP

A blowfish, literally

Il existe d’autres solutions qui ont chacune leurs avantages mais nous nous concentreront uniquement sur l’algorithme blowfish.

La majorité des backends dévéloppés à BigInt sont en PHP et PHP dispose d’une implémentation native à partir de la version 5.3.7. Ce qui signifie que nous n’avons pas beaucoup de code à écrire.

Blowfish est un algorithme de chiffrement symétrique libre qui à ce jour, n’est pas consideré comme faillible.

Voici une fonction permettant de chiffrer les mots de passes avant de les insérer en base de données. Elle se base sur la fonction PHP crypt :

<?php 
// Original PHP code by Chirp Internet: www.chirp.com.au 
// Please acknowledge use of this code by including this header.
function bcrypt($input, $rounds = 9) {
	if (!defined('CRYPT_BLOWFISH') || !CRYPT_BLOWFISH)
		die("Please activate the 'CRYPT_BLOWFISH’ module");
	if ($rounds < 4 || $rounds > 31)
		die("$rounds parameter out of bounds (4-31)");
	$salt = "";
	$salt_chars = array_merge(range('A','Z'), range('a','z'), range(0,9));
	for($i=0; $i < 22; $i++) {
		$salt .= $salt_chars[array_rand($salt_chars)];
	}
	return crypt($input, sprintf('$2y$%02d$', $rounds) . $salt);
}
?>

Chaque salt pour le blowfish est composé :

  • d’un algorithme : $2y$ dans notre cas.
  • d’un coût : 9 par défaut. Plus la valeur sera grande, plus le hash générè sera robuste. Néanmoins cette opération à un coût important en terme de ressource (temps).
  • d’une chaine aléatoire de 22 caractères.

L’intérêt d’un chiffrement en blowfish et qu’un même mot de passe produira 2 hashs différents.

La structure conditionnelle permettant de vérifier la validité du mot de passe posté avec le mot de passe enregistré :

if (crypt($_POST('password'), $blowfish) === $blowfish)
//Auth successful
else
//Invalid password

Pour conclure.

Il existe de meilleurs algorithmes de chiffrements en PHP disponible en utilisant l’extension mcrypt mais cela requiert beaucoup plus de travail et des connaissances plus poussées en cryptographie.

Et vous, comment stockez-vous vos mots de passes ?

comments powered by Disqus