Ceci est la traduction d'un tutorial de Zend Company, traduit de l'anglais.
SESSIONS ET COOKIES SOUS PHP : LES SECRETS
************************************
Par : Vikram Vaswani (Zend company)
Traduit de l'anglais par Magidev (magidev@hotmail.com)
La première session
Un des exemples standards utilisés pour démontrer comment fonctionne un session, est une application d'incrémentation de compteur. C'est un simple compteur qui initialise
une variable la première fois que vous visitez une page web et qui s'incrémente a chaque fois que vous rechargez la page. La variable compteur est stocké dans une session,
ce qui signifie que si vous naviguez sur un autre site web et que vous retournez sur cette page, la valeur du compteur sera restaurée (aussi longtemps que vous ne détruisez
pas la session en fermant le navigateur.
Regardez ce code :
<?php
// démarrer une session
session_start();
// incrémenter le compteur
$_SESSION['counter']++;
// montrer le résultat à l'écran
echo "Vous avez vu " . $_SESSION['counter'] . " fois cette page";
?>
Pour voir comment ca marche, appelez le script plusieurs fois depuis votre navigateur. Vous remarquez que le compteur monte de 1 a chaque rechargement. Si vous ouvrez
deux fenetre de votre navigateur, et que vous ouvrez la meme page, PHP maintiendra et incrémentera la meme session individuelle pour les deux fenêtres. L'ID de session
est utilisé pour identifier quel client a fait quelle requête, et recréer la priorité de chaque environnement sauvegardé pour chaque session individuelle. Cela veut également
dire que si vous visitez un (ou plusieurs) autre sites durant la même session et que vous retournez à votre script sans fermer votre navigateur entre temps, votre ancienne
session sera retrouvée et récrée pour vous.
Chaque session en PHP commence avec la fonction d'appel de session session_start(). Cette fonction vérifie si une session n'existe pas déja et la restore (si elle la trouve) ou
en recréer une (si elle n'existe pas). Les variables session peuvent etre enregistrées en utilisant le tableau superglobal $_SESSION, et peut etre appelé a n'importe quel
moment durant la session en utilisant les spécification de tableau classiques. Dans l'exemple ci-dessus, une clé nommée counter a été ajoutée au tableau $_SESSION. La
première fois qu'une session est crée, cette clé à obtenu la valeur 0. A chaque appel de la même session, la valeur précédente du compteur est retrouvée et incrémentée de
1.
Si l'exemple ne fonctionne pas comme expliqué, vérifiez que la variabe session.save_path de votre fichier php.ini pointe sur un dossier temporaire valide sur votre système.
Cette valeur est attribuée par défaut sur /tmp, mais si vous désirez essayer cet exemple sous un système Windows, vous devrez editer cette valeur pour C:\Windows\temp\
(où un dossier temporaire sur votre système)
Rappelez-moi
Voici un autre exemple, celui-ci demande a l'utilisateur de s'identifier et stocke le login et l'heure de début de session dans deux variables. Cette information est utilisée pour
afficher le nombre total de minutes depuis lesquelles la session est active
<?php
// démarre la session
session_start();
?>
<html>
<head></head>
<body>
<?php
if (!isset($_SESSION['name']) && !isset($_POST['name'])) {
// si aucune donnée, affiche le formulaire
?>
<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">
<input type="text" name="name">
<input type="submit" name="submit" value="Entrez votre nom">
</form>
<?php
}
else if (!isset($_SESSION['name']) && isset($_POST['name'])) {
// Si la session n'existe pas mais que le formulaire a été envoyé
// Vérifie si le formulaire dispose de toutes les informations nécéssaires
// et crée une nouvelle session
if (!empty($_POST['name'])) {
$_SESSION['name'] = $_POST['name'];
$_SESSION['start'] = time();
echo "Bienvenue, " . $_POST['name'] . ". Une nouvelle session à été activée pour vous. Cliquez <a href=" . $_SERVER['PHP_SELF'] . ">ici</a> pour actualiser.";
}
else {
echo "ERREUR: Entrez votre nom!";
}
}
else if (isset($_SESSION['name'])) {
// Si un session existait déja
// Calcule le temps passé entre les deux visites
echo "Bienvenue a nouveau, " . $_SESSION['name'] . ". Cette session à été activée il y a " . round((time() - $_SESSION['start']) / 60) . " minute(s). Cliquez <a href=" .
$_SERVER['PHP_SELF'] . ">ici</a> pour afficher la page.";
}
?>
</body>
</html>
Dans cette exemple, la présence ou non de variable session est utilisée pour déterminer laquelle des trois options afficher. Le temps de départ de la session est aussi
enregistrée dans $_SESSION['start'] avec la fonction time(), qui retourne le nombre total de seconde entre le 1 Janvier 1970 et l'heure courante. La seconde étape calcule le
temps passé entre maintenant et le temps stocké dans $_SESSION['start'] et l'arrondi pour afficher le temps approximativement écoulé.
C'est important de noter que l'appel de session_start() DOIT apparaitre en premier, avant toute sortie générée par le scription (incluant que vous n'utilisez pas la fonction de
mise en cache des sorties PHP comme vous pouvez lire à http://www.php.net/manual/en/ref.outcontrol.php. Ceci est du fait que la fonction de maintient des sessions dans
PHP utilise des cookies mémoires pour gérer les sessions, et les en-tête de cookies doivent etre transmises avant quelque sortie client qu'il soit. Si vous voyez une erreur
comme celle-ci dans les pages utilisant vos sessions :
Warning: Cannot send session cache limiter - headers already sent (output started at ...)
c'est probablement que quelque part, d'un manière ou d'une autre, des sorties ont été trouvées avant l'appel du session_start(). Parfois un retour charriot ou un espace
blanc en dehors des balises php entourant le session_start() peut causer l'erreur, donc soyez vigilant.
Comme dit précédement, chaque session possède un ID de session unique, que PHP utilise pour garder les valeurs de chaque client. Cet ID de session est une longue chaine
alphanumérique automatiquement passée par PHP depuis la page qui utilise les sessions. Pour voir a quoi cela ressemble, utilisez la fonction session_id(), comme dans ce
simple exemple :
<?php
// démarrer la session
session_start();
// afficher l'ID de session
echo "Je traque votre ID de session : " . session_id();
?>
Quand l'utilisateur ferme son navigateur et détruit les sessions, le tableau $_SESSION est vidé de toutes ses valeurs et variables. Vous pouvez également détruire vous
même une session, par exemple quand l'utilisateur se déconnecte (ndlr: Il clique sur un lien Fermer la session) en appelant la fonction session_destroy(), comme dans
l'exemple suivant :
<?php
// démarrer la session
session_start();
// et la détruire
session_destroy();
?>
Dans ce cas, vous vous demandez si ce que vous lisez ci-dessus est exact... Oui ! Avant d'appeler la fonction session_destroy() vous devez d'abord rappeler la fonction
session_start() pour recupérer la session courante.
RAPPELEZ-VOUS : $_SESSION est un tableau superglobal, et vous pouvez utiliser toutes les fonctions internes et externes sans le déclarer comme global au préalable. Dans
le simple exemple suivant, vous allez voir :
<?php
// démarrer la session
session_start();
// cette fonction vérifie le contenu d'une valeur de la session
// et retourne true ou false
function isAdmin() {
if ($_SESSION['name'] == 'admin') {
return true;
}
else {
return false;
}
}
// attribue la valeur a $_SESSION['name']
$_SESSION['name'] = "guessme";
// appel la fonction en utilisant la variable session
// retourne faux ici
echo isAdmin()."<br />";
// attribue la valeur a r $_SESSION['name']
$_SESSION['name'] = "admin";
// appel la fonction en utilisant la variable session
// retourne vrai ici
echo isAdmin()."<br />";
?>
Vous pouvez en lire plus a propos des sessions et de leur maintient a http://www.php.net/manual/en/ref.session.php.
Règles du jeu
Une session fonctionne en utilisant un cookie mémoire, ce qui explique qu'il n'est disponible que tant que une fenetre de navigateur est toujours active. Quand le navigateur
est éteint, la mémoire allouée au cookie est vidée et la session détruitre. Si vous voulez utiliser des cookies de plus longue durée, vous devez utiliser les fonction PHP de
cookies pour écrire un cookie sur le disque dur de l'utilisateur, et le relire au besoin.
Avant de commence à utiliser les cookies, il y a quelques points sur lequels il faut etre vigilant :
Quand les cookies sont utilisée pour enregistrer les valeurs d'un certain domaine, ils ne peuvent etre relus que par le domaine qui les à crées (ndlr : Domaine = l'adresse
web, le domaine propriétaire)
Un même domaine ne peut créer plus de 20 cookies, et chaque cookie est limité à la taille de 4 KB
Un cookie possède habituellement six attributs, le premier est le mandataire. Il y a :
name : le nom du cookie
value : la valeur du cookie
expires : la date et l'heure a laquelle le cookie expire
path : le dossier dans lequel le domaine peut accéder au cookie
domain : le domaine dans lequel le cookie est valide
secure : un marqueur booléen indiquant si le cookie ne peut etre transmit que pas connexion sécurisée
Plus d'informations sur les cookies peuvent être obtenus depuis Netscape, les personnes qui les ont inventée. Visitez http://www.netscape.com/newsref/std/cookie_spec.html
pour connaitre les spécificiations Netscape.
C'est important de se rappeler que, quand le cookie est écrit chez l'utilisateur, vous êtes le programmeur qui a le plus petit contrôle de ceux-ci. Si un utilisateur décide de
désactiver les cookies dans son navigateur, vos cookies ne seront simplement pas sauvegardés.
Maintenant que vous savez cela, jetons un oeil sur l'utilisation des cookies dans PHP
Retrouver de vieux amis
PHP offre une seule fonction manipulant les cookies : setcookie(). Cette fonction vous autorise a lire et ecrire des cookies, comme dans l'exemple :
<?php
if (!isset($_COOKIE['visited'])) {
// si le cookie n'existe pas
// on le crée
setcookie("visited", "1", mktime()+86400, "/") or die("Impossible d'écrire le Cookie");
echo "C'est votre première visite aujourd'hui";
}
else {
// Si le cookie existe
echo "Heureux de vous revoir mon viel ami !";
}
?>
Pour voir comment cela fonctionne, appelez votre pages plusieurs fois, La premiere fois, parceque le cookie n'existait pas encore, le premier message est affiché, mais pour
la suite, le client reconnait le cookie déja écrit et affiche le second message.
NOTE : Ceci fonctionne meme si vous fermez le navigateur entre temps, voila la différence avec les sessions
La fonction setcookie() accepte 6 arguments : le nom du cookie, sa valeur, sa date d'expiration, le domaine, le dossier pour le quel c'est valable et une valeur booléene
indiquant si le cookie peut ou non etre transmis par le biais de protocoles HTTP non sécurisés. Comme expliqué précédement, seulement le nom et la valeur sont
nécéssaires, les autres sont facultatifs
La valeur des cookies est automatiquement envoyée au client et convertis en une paire de CLEF-VALEUR dans la variable $_COOKIE, comme un tableau superglobal identique
a $_SESSION. Les valeur peuvent etre retrouvées grace à la notation standard des tableaux, comme dans l'exemple ci-dessus. NOTE : Comme pour les sessions vous devez
appeler la fonction setcookie() avant toute sortie, sinon vous aurez l'erreur suivante :
Warning: Cannot add header information - headers already sent by (output started at ... )
Formulaire et Fonction
Voici un exemple un peu plus complexe:
<?php
if (!isset($_POST['email'])) {
// si le formulaire n'a pas ete envoye
// affiche le
// si le cookie existe, pre rempli le formulaire avec ses valeurs
?>
<html>
<head></head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">
Enter your email address: <input type="text" name="email" value="<?php echo $_COOKIE['email']; ?>">
<input type="submit" name="submit">
<?php
// calcule egalement le temps depuis le dernier envoi
if ($_COOKIE['lastsave']) {
$days = round((time() - $_COOKIE['lastsave']) / 86400);
echo "<br /> $days jour(s) depuis votre dernier envoi";
}
?>
</form>
</body>
</html>
<?php
}
else {
// le formulaire a été envoyé
// rempli le cookie avec les données du formulaire et du timestamp
// le cookie expire dans 30 jours
if (!empty($_POST['email'])) {
setcookie("email", $_POST['email'], mktime()+(86400*30), "/");
setcookie("lastsave", time(), mktime()+(86400*30), "/");
echo "Votre email à été enregistrée.";
}
else {
echo "ERREUR: Entrez votre adresse email!";
}
}
?>
</body>
</html>
Dans ce cas, les valeurs entrées dans la feuille sont stockés dans une cookie appelé email, et automatiquement retrouvée pour pré-remplir le formulaire avant la requete.
Cette technique est fréquement utlisée par les sites qui requièrent un login et un mot de passe, pré-remplissant ainsi le nom d'utilisateur dans la boite de login avec la valeur
stocké dans le cookie depuis la dernière identification réusssie.
Cet exemple démontre également comme stocker plus d'un cookie par domaine, en appelant setcookie() plusieurs fois. Dans l'exemple ci-dessus, le temps que prend
l'utilisateur pour remplir le formulaire est stocké dans un second cookie pour calculer le temps entre deux entrées.
Pour supprimer un cookie chez le client, appelez simplement setcookie() avec la meme syntaxe, mais définissez une date de validité dans le passé. Cela provoque la
suppression du cookie chez le client. Voici un exemple :
<?php
// supprime un cookie
setcookie("lastsave", NULL, mktime() - 3600, "/");
?>
Lisez-en plus a propos des cookies et de la fonction setcookie a : http://www.php.net/manual/en/features.cookies.php et
http://www.php.net/manual/en/function.setcookie.php.
Accès autorisé
Comme je l'ai dit au début de ce tutorial, les cookies et les sessions sont deux modes différents pour stocker des données "persistantes" chez le client. Une session retient les
données pour le temps d'une session, un cookie retient les données pour autant de temps que vous avez besoin. En sachant cela, jetons un oeil sur l'exemple suivant, qui
utilise conjointement les deux méthodes :
L'application est une simple identification. Avec certaines pages visibles seulement aux utilisateur identifées avec succès. Les utilisateurs non identifées avec un mot de passe
valide sont redirigés vers une page d'erreur spéciale. La liste des noms d'utilisateurs et des mots de passe sont stockés dans une base MySQL et php vérifie si il peut
accorder ou non l'accès
The application here is a simple user authentication system, where certain pages can only be viewed by users who successfully log in to the system. Users who have not
been authenticated with a valid password are denied access to these "special" pages. The list of valid usernames and passwords is stored in a MySQL database, and PHP is
used to verify a user's credentials and decide whether or not to grant access.
On présume qu'une table comme ceci existe :
+-------+-----------------------------------------------+
| name | pass |
+-------+-----------------------------------------------+
| sue | 9565d44fd0fe4db59f073eea1db70f3ea258e10b |
| harry | 6e74234b8b552685113b53c7bff0f386c8cef8cf |
| louis | 6817dda51b64b8190029490d2811a4d9cb9cd432 |
| sam | bd17f8243e771a57cfbb06aa9a82bbf09fd2d90b |
| james | 792ec9b44d432c947ac6775b2b52326e9d08512f |
+-------+-----------------------------------------------+
avec un champ unique de nom d'utilisateur et un mot de passe crée avec la fonction SHA1(). Ici php va faire le gros du travail :
<?php
if (isset($_POST['name']) || isset($_POST['pass'])) {
// formulaire recu
// verifie les valeurs
if (empty($_POST['name'])) {
die ("ERREUR: Entrez un nom d'utilisateur!");
}
if (empty($_POST['pass'])) {
die ("ERREUR: Veuillez entrer un mot de passe!");
}
// Defini les variable d'acces au serveur mySQL
$host = "localhost";
$user = "test";
$pass = "test";
$db = "db2";
// Ouvre la connexion
$connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
// Selectionne la base
mysql_select_db($db) or die ("Unable to select database!");
// Construit la requete
$query = "SELECT * FROM users WHERE name = '" . $_POST['name'] . "' AND pass = SHA1('" . $_POST['pass'] . "')";
// Execute la requete
$result = mysql_query($query) or die ("Error in query: $query. " . mysql_error());
// Regarde les valeurs retournees par la base
if (mysql_num_rows($result) == 1) {
// si une ligne est retournee,
// l'authentification est bonne
// crée le cookie avec le nom d'utilisateur et la session
session_start();
$_SESSION['auth'] = 1;
setcookie("username", $_POST['name'], time()+(84600*30));
echo "Acces autorisé !";
}
else {
// pas de résultat
// authentification impossible
echo "ERREUR: Login ou Mot de passe incorrect!";
}
// Libere le resultat
mysql_free_result($result);
// Ferme la connexion
mysql_close($connection);
}
else {
// pas d'envoi
// affiche le formulaire
?>
<html>
<head></head>
<body>
<center>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Username <input type="text" name="name" value="<?php echo $_COOKIE['username']; ?>">
<p />
Password <input type="password" name="pass">
<p />
<input type="submit" name="submit" value="Log In">
</center>
</body>
</html>
<?php
}
?>
Ici, les valeurs entrées dans la boite de login sont intégrée dans une requete mySQL, qui est executée sur la table user. Si le nom et le mot de passe correspondent, un seul
enregistrement est retourné, indiquant que l'authentification est accordée, si aucun enregistrement n'est retourné, c'est que l'authentification a échou
Quand l'authentification reussi, une session est créer : $_SESSION['auth'] est créer et assigné a la valeur booléene true, et le nom d'utilisateur est stocké dans un cookie
pour la prochaine fois, le cookie dure 30 jours, et est utilisé pour pré-remplir le champ Login pour la fois suivante.
Bien sur ce n'est pas assez, il faut maintenant bloquer les pages interdites si l'utilisateur n'est pas identifiée. Sans cela, n'importe qui peut passer la sécurité en tapant
simplement l'adresse de la page dans l'URL.
Maintenant, pour protéger une page, il suffit de vérifier que la variable $_SESSION['auth'] existe et d'autoriser l'acces ou non
<?php
// start session
session_start();
if (!$_SESSION['auth'] == 1) {
// verifie si l'autorisation est la
// sinon die avec erreur
die ("ERREUR: Acces interdit!");
}
else {
?>
<html>
<head></head>
<body>
Ceci est une page sécurisée. Vous pouvez seulement la voir si $_SESSION['auth'] = 1
</body>
</html>
<?php
}
?>
Chouette non ? Seul les utilisateurs loggés sont autorisés a voir cette page, parceque leur clients on la session $_SESSION['auth'] a la valeur 1 en mémoire. Tout les autres
auront la page d'erreur.
Voici la fin de ce tutorial.