Nous n'aborderons ici que l'identification gérée par le navigateur.
19.1 Préliminaires : la fonction header
La fonction header permet de spécifier une en-tête
HTTP lors de l'envoi des fichiers HTML.
Reportez-vous à :
http://www.w3.org/Protocols/rfc2616/rfc2616
(HTTP 1.1 Specification) pour plus d'informations sur les en-têtes
HTTP.
Note : La fonction header doit être appelée avant
la première balise HTML, et avant n'importe quel envoi de commande
PHP.
Nous allons utiliser ici cette fonction pour demander la vérification
d'une identité.
Pour cela il suffit de faire commencer votre programme PHP par la
commande :
header("WWW-Authenticate: Basic realm='private'");
qui a pour effet de faire demander (et retenir) un nom et un mot de
passe par le navigateur.
Vous pouvez ainsi tester le programme élémentaire suivant :
(http://www.grappa.univ-lille3.fr/polys/reseaux-DG/exemples/ident-exple01.php)
<?php
if (!isset($PHP_AUTH_USER) || !isset($PHP_AUTH_PW)) {
header("WWW-Authenticate: Basic realm='private'");
} else {
echo "<HTML><BODY>";
echo "les données connues sont $PHP_AUTH_USER et $PHP_AUTH_PW\n";
echo "</BODY></HTML>";
}
?>
Vous pouvez remarquer que la demande n'est faite que lors de la
première exécution de la page. La seule façon, avec le
programme tel qu'il est écrit, de devoir répondre à nouveau à la
question est de sortir de votre navigateur (fermer toutes les fenêtres
du navigateur, oui !) et de le relancer.
C'est cette propriété que nous allons utiliser pour gérer
l'identification des utilisateurs.
19.2 Contrôle des mots de passe
Il ne manque qu'une seule chose (ou presque !) au programme précédent
pour être parfait : tester les valeurs des variables
$PHP_AUTH_USER (qui contient le nom) et $PHP_AUTH_PW
(qui contient le mot de passe) pour vérifier qu'on a affaire à la
bonne personne.
Une version simple pourrait ressembler à ça :
(http://www.grappa.univ-lille3.fr/polys/reseaux-DG/exemples/ident-exple02.php)
<?php
if (($PHP_AUTH_USER=="marcel") && ($PHP_AUTH_PW=="bidule")) {
echo "<HTML><BODY>";
echo "Bravo, identification réussie.\n";
echo "</BODY></HTML>";
} else {
header("WWW-Authenticate: Basic realm='private'");
}
?>
19.3 Amélioration - plusieurs utilisateurs
Il est rare, quand l'accès à une page est contrôlée par un mot de
passe, que tous les utilisateurs aient les mêmes droits.
Il faut donc pouvoir détecter l'identité de la personne concernée au
moment de la saisie du mot de passe. Pour cela on donnera un mot de
passe différent à chaque utilisateur. Cela nous permet d'avoir
l'exemple suivant
(http://www.grappa.univ-lille3.fr/polys/reseaux-DG/exemples/ident-exple03.php)
dans lequel les renseignements sont rangés dans un tableau de chaînes
de caractères , une chaîne par personne, présentée sous la forme
« vrai nom/nom d'utilisateur/mot de passe » :
<?php
//la liste des informations
$liste=array("Jean Némar/nemar/jjjj",
"Sophie Fonfec/fonfec/ssss",
"Yves Adrouille-Toultan/adrouille/yyyy");
//création des tableaux
for ($i=0;$i<count($liste);$i++) {
$l=explode("/",trim($liste[$i]));
$nom[$i]=$l[0]; //identité réelle de l'utilisateur n°i
$user[$i]=$l[1]; //identifiant de l'utilisateur n°i sur le système
$pass[$i]=$l[2]; //mot de passe de l'utilisateur n°i
}
$nbusers=count($liste);
//contrôle de l'identité
$ok=-1; //on démarre sans connaître l'utilisateur
for ($i=0;$i<$nbusers;$i++) {
if (($PHP_AUTH_USER==$user[$i]) && ($PHP_AUTH_PW==$pass[$i])) {
//on a reconnu un utilisateur -> on garde son numéro
$ok=$i;
}
}
//si l'identification a raté, $ok contient toujours -1
//on demande le nom et le mot de passe
if ($ok==-1) {
header("WWW-Authenticate: Basic realm='private'");
return; //On notera l'utilisation de l'instruction return
//pour sortir du programme.
}
//si on arrive ici, c'est que l'identification a réussi
//et que $ok contient le numéro de l'utilisateur
echo "<HTML><BODY>";
echo "Bravo, identification réussie.<BR>\n";
echo "Vous êtes <EM>nom[$ok]</EM>,\n";
echo "votre identifiant est <EM>$user[$ok]</EM>,\n";
echo "votre mot de passe est <EM>$pass[$ok]</EM>.<BR>\n";
//À vous d'ajouter ici tout ce qui concerne l'utilisateur
//...
//...
echo "</BODY></HTML>";
?>
19.4 Séparer les données du traitement
Il est tout à fait raisonnable d'imaginer qu'on va avoir besoin de
changer les mots de passe, ainsi qu'ajouter des nouveaux utilisateurs
ou en supprimer des anciens.
Il n'est par contre pas raisonnable du tout d'imaginer que celui qui
va le faire aura envie d'aller manipuler à chaque fois le fichier du
programme. (Ce n'est même d'ailleurs pas souhaitable...)
Il est donc préférable de garder ces informations dans un fichier
séparé.
Qu'est-ce que cela change ? Pas grand chose : il suffit par exemple
de remplacer les premières lignes :
//la liste des informations
$liste=array("Jean Némar/nemar/jjjj",
"Sophie Fonfec/fonfec/ssss",
"Yves Adrouille-Toultan/adrouille/yyyy");
par la lecture d'un fichier contenant les mêmes informations :
//lire la liste des informations
$liste=file("mettre ici le chemin d'accès au fichier");
Le reste du programme ne change pas.
Le fichier à lire sera alors un simple fichier texte, qui contiendra
dans le cas présent :
Jean Némar/nemar/jjjj
Sophie Fonfec/fonfec/ssss
Yves Adrouille-Toultan/adrouille/yyyy
19.5 Cacher les mots de passe
La solution précédente a un très gros inconvénient si
vous placez le fichier de mots de passe n'importe où... N'oubliez pas
que les pages web (HTML et PHP) sont faites pour être vues par tout le
monde, depuis n'importe où. Pour permettre cela les droits d'accès de
l'arborescence web sont positionnés à lisible par tous (chmod a+r*) et il ne faut surtout pas changer cela.
Ça ne posera aucun problème, tant que vous n'y entreposez pas de
données confidentielles.
Mais les mots de passe, ça c'est confidentiel !
Alors, comment faire ?
Il suffit de ne pas mettre votre fichier de mot de passe dans
l'arborescence web, mais de lui choisir un emplacement quelconque dont
la seule obligation est d'être accessible pour l'utilisateur
apache (c'est lui qui exécute les programmes PHP).
Son chemin d'accès dans l'instruction
$liste=file("mettre ici le chemin d'accès au fichier");
devra être absolu, c'est-à-dire partir de la racine et commencer par
un slash (/).
19.6 Crypter les mots de passe
Afin de ne pas rendre la vie trop facile à un éventuel pirate, il peut
aussi être intéressant de crypter les mots de passe qui sont dans le
fichier.
Cela se fait avec la fonction crypt.
Il n'y a pas d'algorithme de décryptage.
Mais il ne faut pas croire que cela puisse être une protection
suffisante. En effet si les mots de passe, même cryptés, sont
accessibles, il suffit de recopier chez soi les versions cryptées, et
de les décoder tranquillement par la force brute.
Vous pourrez trouver un exemple de ce que cela signifie en exécutant
le programme suivant :
http://www.grappa.univ-lille3.fr/polys/reseaux-DG/exemples/decrypt.php
Il décrypte tout mot de passe dont vous fournissez la version cryptée
d'une manière totalement inintelligente, voire même plutôt bestiale :
il essaie toutes les chaînes de caractères possibles (en commençant
par 1 caractère, puis 2, etc.) en les énumérant systématiqument (par
exemple pour 2 caractères : aa, ab, ac,
ad, ae, ... , az, ba, bc,
...). Pour chaque chaîne il calcule sa version cryptée (avec la
fonction crypt) et la compare à celle qu'on lui a fournie.
Quand elles sont identiques, il sait qu'il vient de trouver le mot de
passe.
Idiot, d'accord, mais ça marche.
Remarque : pour ne pas encombrer inutilement le serveur
le temps de calcul a été limité à 2 minutes, mais cela permet quand
même de trouver à coup sûr (si le serveur n'a pas trop de travail par
ailleurs) n'importe quel mot de passe de 4 lettres au plus.
Et tout cela avec un programme qui n'est pas vraiment optimisé, et
dans un langage qui n'est pas le plus rapide...
Il faut donc vraiment cacher les mots de passe...