J’ai récemment voulu récupérer des mots de passe depuis une base de donnée MySQL (alimentée par PHP) pour les mettre dans un annuaire LDAP, et comme je n’ai pas trouvé immédiatement la solution à ce problème, je consigne ici la procédure que j’ai suivie, pour qu’elle puisse être utile à un autre (cet autre pouvant être celui que je serai dans un an ou deux).
Les données du problème
- J’ai une base de données MySQL, associée à une application en PHP, Ilias Open Source, qui contient des mots de passe. Naturellement, ces mots de passe ne sont pas stockés en clair dans la base de données, ils sont hachés en utilisant MD5.
- J’ai un annuaire openLDAP qui contient des données utilisateurs et notamment des mots de passe. LDAP n’est pas très exigent en ce qui concerne les mots de passe : il lui suffit que la chaîne correspondant au mot de passe commence par le nom de l’algorithme de hachage utilisé pour produire la chaîne qui suit. Bien sûr, il faut que l’algorithme choisi soit connu d’openLDAP (il est possible d’ajouter des modules à la compilation pour le faire fonctionner avec tous les algorithmes couramment utilisés pour hacher les mots de passe).
- Je veux que les mots de passe des utilisateurs présents dans l’annuaire soient ceux qu’ils ont dans la base de données relationnelle.
Le problème
A première vue, il n’y a pas de problème : je prends le mot de passe haché dans ma base de données et je le mets dans l’annuaire en le faisant précéder de {MD5}. C’est d’ailleurs ce que j’ai essayé de faire tout d’abord et cela ne fonctionne pas.
En cherchant un peu, on trouve rapidement l’explication de cela et elle est assez simple : l’algorithme MD5 produit une empreinte sous la forme d’une séquence de 128 bits, soit 16 octets. Naturellement, une telle suite de 16 octets ne peut pas être représentée en texte “normal” (ascii), il faut donc lui faire subir un traitement pour pouvoir l’imprimer ou même simplement l’afficher sur un écran.
C’est là que le problème se pose : la fonction MD5 de php crée, par défaut, une représentation de cette séquence sous la forme d’une suite de 32 chiffres hexadécimaux (en Python un tel résultat est obtenu avec la fonction hexdigest() du module md5 (ce module est déconseillé dans les versions de Python supérieures à la 2.5, mais le programme que j’ai écrit était en Python 2.3) ), openLDAP (je suppose que c’est le fonctionnement standard de toutes les implémentations du protocole LDAP), en revanche, prend directement la séquence d’octets (équivalent de la fonction Python digest() du module md5) et l’encode en base64, un algorithme destiné à représenter des données binaires en texte, utilisé, par exemple, pour encoder les pièces jointes des messages électroniques.
La solution
Après beaucoup de tâtonnements et (trop) de recherches, j’ai réussi à trouver la formule qui me donne satisfaction. La voici (en Python) :
En supposant que pw_php est le mot de passe que j’ai obtenu de la base MySQL et pw_ldap le mot de passe que je vais inscrire dans l’annuaire LDAP.
pw_ldap = "{MD5}" + base64.encodestring(pw_php.decode("hex"))
La méthode decode() de l’objet chaîne, avec le paramètre “hex” décode l’empreinte encodée en caractères hexadécimaux, pour obtenir à nouveau la séquence de 16 octets. Cette séquence est alors encodée en base64 grâce à la fonction encodestring() du module base64. Comme je l’ai indiqué plus haut, le début de la chaîne de caractères du mot de passe indique l’algorithme utilisé pour hacher le mot de passe (ici MD5), entre accolades.
Blogged with Flock
Tags: ldap, php, python, MySQL