Quel est le meilleur type pour stocker un mot de passe dans une table MySQL ?

D’abord, il va de soi (mais ça va mieux en le disant) que les mots de passe d’une application web ne doivent pas être stockés en clair, mais sous forme d’un hash. Lors de la procédure d’authentification, il suffit de calculer le hash du mot de passe saisi par l’utilisateur, et de le comparer avec celui stocké en base.

L’algorithme le plus répandu pour calculer un hash est MD5. Néanmoins, depuis 2004 plusieurs vulnérabilités ont été découvertes, aussi il est préférable d’utiliser SHA1. Quel est meilleur type pour stocker ces hashs dans une table MySQL ?

Un hash SHA1 mesure 160 bits ou 40 caractères hexadécimaux. Un hash MD5 mesure 128 bits soit 32 caractères hexadécimaux.

Stocker sous forme de chaine

La longueur du hash est fixe : il est donc préférable d’utiliser le type CHAR (CHAR(40) pour du SHA1, CHAR(32) pour du MD5). En effet, le type VARCHAR prendrait plus d’espace car il utilise un octet supplémentaire pour stocker la longueur de la chaine.

Ensuite, il faut faire attention à l’encodage. En effet, si la table est en UTF-8, les caractères seront probablement stockés sur 3 octets (ça peut varier selon le moteur de stockage). Il vaut mieux spécifier l’encodage de cette colonne à ASCII, grâce à CHARACTER SET ascii.

Enfin, la colonne est évidemment NOT NULL, pour économiser un bit (la valeur NULL est stockée dans un bit supplémentaire), à moins que l’application en ait besoin (ça existe des utilisateurs sans mot de passe ?).

Exemple :

mysql> create table t (
    -> password char(40) character set ascii not null
    -> ) engine=MyISAM character set utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(SHA1('monSuperMotDePasse'));
Query OK, 1 row affected (0.00 sec)

mysql> select * from t;
+------------------------------------------+
| password                                 |
+------------------------------------------+
| 35ea5a462298bd78c1648fbea2195988c46d103f |
+------------------------------------------+
1 row in set (0.00 sec)

L’espace utilisé est donc 40 octets pour un hash SHA1, car le type CHAR avec le jeu de caractère ASCII stocke chaque caractère sur un octet (8 bits).

Stocker sous forme binaire

Vous l’aurez surement remarqué, 40 octets ça fait 320 bits, c’est à dire le double de la taille du hash SHA1 brut (idem pour MD5 : 32×8 = 256). Pour faire simple, c’est le prix à payer pour avoir un mot de passe lisible et “imprimable” (c’est à dire que vous pouvez copier/coller sans problème) en base. L’autre solution consiste à stocker le hash brut directement sous forme binaire.

Le type a utilisé n’est donc plus CHAR, mais BINARY qui stocke des chaines binaires. Il faut lui préciser la longueur en octets, donc BINARY(20) pour du SHA1 (160/8 = 20), et BINARY(16) pour du MD5 (128/8 = 16). Il n’y a plus à se soucier du jeu de caractères.

Pour convertir une chaine de caractère en chaine binaire, il faut utiliser UNHEX. Pour l’opération inverse, il faut utiliser HEX. Note pour les utilisateurs de PHP : la fonction hash accepte un troisième paramètre optionnel $raw_output qui, s’il vaut true, permet de récupérer le résultat binaire directement (voir http://fr.php.net/manual/fr/function.hash.php).

Exemple :

mysql> create table t2 (
    -> password binary(20) not null
    -> ) engine=MyIsam character set utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t2 values(UNHEX(SHA1('monSuperMotDePasse')));
Query OK, 1 row affected (0.00 sec)

mysql> select HEX(password) from t2;
+------------------------------------------+
| HEX(password)                            |
+------------------------------------------+
| 35EA5A462298BD78C1648FBEA2195988C46D103F |
+------------------------------------------+
1 row in set (0.00 sec)

L’espace utilisé est donc 20 octets pour hash SHA1, soit 2 fois moins que la version en chaine de caractères. L’inconvénient est qu’il faut penser à utiliser UNHEX ou HEX dès qu’on veut le hash sous une forme imprimable. C’est rarement le cas, et comme les opérations de comparaisons (utilisées pour l’authentification de l’utilisateur) peuvent s’effectuer avec les version binaires, ça ne me dérange pas.

En savoir plus

3 thoughts on “Quel est le meilleur type pour stocker un mot de passe dans une table MySQL ?

  1. devlop78

    Pourquoi 3 octets pour UTF-8, puisque c’est de l’hexadecimal, donc de l’unicode < 128 et donc sur 1 octet (parce que correspondant à ASCII).

  2. Rémi

    @devlop78 Parce qu’avec le type CHAR, MySQL réserve 3 bytes par caractères, quels qu’ils soient.

    “Tip: To save space with UTF-8, use VARCHAR instead of CHAR. Otherwise, MySQL must reserve three bytes for each character in a CHAR CHARACTER SET utf8 column because that is the maximum possible length. For example, MySQL must reserve 30 bytes for a CHAR(10) CHARACTER SET utf8 column.”

    Cf la documentation ici:
    http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-utf8.html

Comments are closed.