J'ai fini par trouver un source qui correspondait presque parfaitement à ce que je cherchais. Je l'ai un peu modifié, et je me propose ici de vous l'expliquer. J'espère que l'auteur dudit source ne m'en tiendra pas rigueur. Malheureusement, après avoir enregistré ce source, j'ai oublié de mettre la page en favoris, ce qui fait que j'en ai perdu l'adresse, et il m'a été impossible, jusqu'à présent, de la retrouver.
Pour tout ceci, je n'ai pas utilisé de programmation orientée objet, même si le sujet s'y prêtait à merveille. Le code est cependant très facilement adaptable.
Pour tout ce qui va suivre, se considère que la connexion à la base de donnnée comprenant notre
table est déjà ouverte.
CREATE TABLE Users ( ID bigint(20) unsigned NOT NULL auto_increment, Nick varchar(40) NOT NULL, Password varchar(50) NOT NULL, Email varchar(50) NULL, ... PRIMARY KEY (ID) };
Commençons par déclarer la variable qui nous servira pour le checksum :
<? $hidden_hash_var='Cette chaine de caractères servira à notre hash et je vous défie de la trouver';
Ensuite, par mesure de sécurité, nous allons retirer toute possibilité à une personne malveillante
de tenter de se logger sans en avoir le droit :
$LOGGED_IN=false; $G_USER_RESULT=false; unset($LOGGED_IN);
$LOGGED_IN
soit déclarée via l'URL,
ou envoyée par un formulaire bidon. Nous verrons le cas de $G_USER_RESULT
par
la suite.
Une fois ces précautions élémentaires prises, voyons quelles sont les fonctions que notre module
doit proposer.
function user_register($user_name, $password1, $password2, $email) { global $feedback, $hidden_hash_var; if ($user_name && $password1 && ($password1==$password2)) { // Le nom existe déjà ? $sql="SELECT * FROM Users WHERE Nick='$user_name'"; $result=mysql_query($sql); if ($result && mysql_num_rows($result) > 0) { $feedback .= "ERREUR - Le nick existe déjà.\n"; return false; } else { $sql = "INSERT INTO Users (Nick, Password, Email) ". "VALUES ('$user_name', password('$password1'), '$email')"; $result = mysql_query($sql); if (!result) { $feedback .= "ERREUR - (DB) : ".mysql_error() .".\n"; return false; } else { $feedback .= "Vous êtes enregistré.\n<br>"; return true; } } } else { $feedback .= "ERREUR - Vous devez entrer votre nick et deux fois votre password.\n"; return false; } }
password()
de
MySQL.
La variable globale $feedback
sera utilisée dans chaque fonction et permet de
retourner une chaine de caractères.
function user_login($user_name, $password) { global $feedback; if (!$user_name || !$password) { $feedback .= " ERREUR - Nick ou password manquant.\n"; return false; } else { $sql = "SELECT * FROM Users WHERE Nick='$user_name' AND Password=password('$password')"; $result = mysql_query($sql); if (!$result || mysql_num_rows($result) < 1) { $feedback .= " ERREUR - Nick ou password incorrect.\n"; return false; } else { user_set_tokens($user_name); return true; } } }
En cas de réussite, on a cet appel à une fonction étrange : user_set_tokens()
.
Cette fonction (que voici) ...
function user_set_tokens($user_name_in) { global $hidden_hash_var, $user_name, $id_hash; if (!$user_name_in) { return false; } $id_hash = md5($user_name_in.$hidden_hash_var); setcookie('user_name',$user_name_in,(time()+2592000),'/','',0); setcookie('id_hash',$id_hash,(time()+2592000),'/','',0); }
Comme vous le voyez, les cookies ont une durée de vie de 30 jours (2592000 secondes). Libre à vous d'adapter cette valeur, voire même de l'omettre si vous préférez que l'identification ne dure que le temps où le navigateur reste ouvert.
Pour ceux qui se poseraient la question, la somme md5 d'une phrase ne permet pas
de retrouver la phrase d'origine.
Il est donc impossible de créer un faux id_hash, puisqu'un éventuel "pirate" ne connait pas
notre variable de hash cachée, mais il est également impossible, par la nature même du md5, de
faire le chemin inverse pour retrouver cette variable cachée en partant de $id_hash
.
De plus, cette fonction devra être appelée au début de chaque page qui requiert un
utilisateur enregistré (comme la page d'édition de préférences), pour éviter les abus.
function user_isloggedin() { global $user_name, $id_hash, $hidden_hash_var, $LOGGED_IN; if (isset($LOGGED_IN)) { return $LOGGED_IN; } if ($user_name && $id_hash) { $hash = md5($user_name.$hidden_hash_var); if ($hash == $id_hash) { $LOGGED_IN=true; return true; } else { $LOGGED_IN = false; return false; } } else { $LOGGED_IN = false; return false; } }
Si vous êtes observateur, peut-être aurez-vous apperçu que cette fonction ne prend aucun argument. Elle ne sert pas à vérifier si tel ou tel utilisateur est loggé, mais bien si l'utilisateur qui demande la page est loggé.
La première chose que nous faisons est de vérifier l'état de la variable $LOGGED_IN
.
Même si celle-ci est "effacée" au début du script, elle peut très bien avoir été recréée a un
endroit quelconque dans la page, lors d'un appel précédent à la fonction.
Si elle n'existe pas, il nous faut vérifier l'existance des deux cookies. Si ceux-ci sont
absent, vous pouvez être certain que l'utilisateur n'est pas loggé. S'ils existent, nous
recréons la somme md5 sensée se trouver dans le cookie $id_hash
et nous comparons
leurs valeurs respectives (rappelez-vous, il n'y a aucun moyen de retrouver le texte original
depuis son md5, tout ce que nous pouvons faire est de calculer un autre md5 et de le comparer
avec l'ancien).
function user_getnick() { if (user_isloggedin()) { return $GLOBALS['user_name']; } else { return ' ERROR - Not Logged In '; } }
function user_getemail() { global $G_USER_RESULT; //On vérifie si on a déjà récupéré les infos de l'utilisateur, si non, on le fait if (!$G_USER_RESULT) { $G_USER_RESULT=mysql_query("SELECT * FROM Users WHERE Nick='" . user_getnick() . "'"); } if ($G_USER_RESULT && mysql_num_rows($G_USER_RESULT) > 0) { return mysql_result($G_USER_RESULT,0,'email'); } else { return false; } }
$G_USER_RESULT
contient toute la ligne (au sens SQL du
terme) des infos de
l'utilisateur. Il suffit de mettre ce test (le premier 'if') dans chaque fonction du genre pour,
une fois de plus, éviter les requêtes inutiles : la requête n'est effectuée qu'une seule fois.
Pour le reste de la page, c'est la variable globale qui est utilisée.
Voici quelques autres fonctions que je ne commenterai pas. Si vous êtes arrivé jusqu'ici, c'est
que vous connaissez suffisemment le PHP pour comprendre la suite tout seul comme un grand :-)
function user_getid() { global $G_USER_RESULT; //On vérifie si on a déjà récupéré les infos de l'utilisateur, si non, on le fait if (!$G_USER_RESULT) { $G_USER_RESULT=mysql_query("SELECT * FROM Users WHERE Nick='" . user_getnick() . "'"); } if ($G_USER_RESULT && mysql_num_rows($G_USER_RESULT) > 0) { return mysql_result($G_USER_RESULT,0,'ID'); } else { return false; } }
function user_change_email($password1, $new_email, $user_name) { global $feedback, $hidden_hash_var; $sql="UPDATE Users SET Email='$new_email' WHERE Nick='$user_name' AND Password=password('$password1')"; $result=mysql_query($sql); if (!$result || (mysql_affected_rows() <1)) { $feedback .= "ERREUR - Nick ou password incorrect.\n"; return false; } else { return true; } }
function user_change_password($new_password1, $new_password2, $change_user_name, $old_password) { if ($new_password1 && ($new_password1 == $new_password2)) { if ($change_user_name && $old_password) { $sql="SELECT * FROM Users " . "WHERE Nick='$change_user_name' AND Password=password('$old_password')"; $result = mysql_query($sql); if (!$result || mysql_num_rows($result) <1) { //> $feedback .= " ERREUR - Nick inexistant ou password incorrect\n."; return false; } else { $sql="UPDATE Users SET Password=password('$new_password1') ". "WHERE Nick='$change_user_name' AND Password=password('$old_password')"; $result=mysql_query($sql); if (!$result || mysql_affected_rows() <1 ) { //> $feedback .= "ERREUR - (DB) : " . mysql_error() . "\n"; return false; } else { return true; } } } else { $feedback .= "ERREUR - Vous devez entrer un Nick et un password.\n"; return false; } } else { $feedback .= "ERREUR - Les password ne correspondent pas.\n"; return false; } }
function user_logout() { setcookie('user_name','',(time()+2592000),'/','',0); setcookie('id_hash','',(time()+2592000),'/','',0); }
N'oubliez pas, avant d'appeler certaines fonctions comme user_getid()
ou
user_getemail()
, que vous devez vous assurer que l'utilisateur est loggé (grâce à
user_isloggedin()
).
Si vous respectez les règles d'usages pour les fichiers PHP, il me semble que ce
script offre une sécurité relativement grande pour les utilisateurs (tiens, la sécurité de base
pour les scripts PHP, voilà une bonne idée de tutorial :p ).
Si vous désirez plus d'informations ou si vous avez des questions, n'hésitez pas à me contacter par mail (allergy@alrj.org) ou sur le forum de http://www.alrj.org.