Sécuriser un minimum une authentification par cookie en PHP, code source du site test
Bien que paraissant pas très "sécure", c'est quand même agréable de ne pas avoir à s'identifier systématiquement.
Je souhaite donc placer une information dans le cookie de l'utilisateur qui permettra de l'authentifier par la suite.
Ce n'est pas pour le site de la NASA, c'est pour ça qu'on va tolérer les cookies. Cependant on va tenter de répondre à quelques exigences de sécurité.
Considérations.
En cas de vol du cookies (sniffage des connexion, faille du navigateur, ou tout simplement avec une clé USB en direct sur le poste de l'utilisateur), le cookie ne doit pas permettre une authentification depuis un autre ordinateur que celui pour lequel il a été créé. Je m'appuie sur l'adrese IP. tant pis pour ceux qui n'ont pas une IP fixe, ils s'identifieront à chaque fois.
- le cookie ne doit pas contenir d'indication sur une méthode d'authentification alternative (le formulaire identifiant mot de passe du site par exemple).
- Changement du cookie à chaque connexion.
Mise en oeuvre.
Construisons un mini site pour le test.
Trois fichiers:
- index.php
- session.php
- connectetoi.htm
par là: http://blog-du-grouik.tinad.fr/authbycookies
un fichier index.php tout simple:
<?php //démarer une session sur le serveur session_start(); if (!isset($_SESSION['is_registered'])){ //La variable de session $_SESSION['is_registered'] n'existant pas, //on appelle le script qui va lui donner une valeur: include("./session.php"); } //$_SESSION['is_registered']=1 si l'utilisateur est authentifié //$_SESSION['is_registered']=0 si ce n'est pas le cas switch($_SESSION['is_registered']){ case "1": // Il est reconnu, saluons le visiteur echo "<center> Bonjour, ".$_SESSION['username']."</center><br />"; echo "<center><a href=\"./session.php?mod=deco\">se déconnecter</a></center>"; break; case"0": // Il n'est reconu, envoyons le sur la page de loggin et d'inscription include("./connectetoi.htm"); break; default: //Si le script session.php appelé ci dessus est bien fait, ce cas est impossible echo "<center>Comment que t'as fait ça toi?</center>"; } ?>
Nous faisons un super site qui dira bonjour à l'utilisateur authentifié et qui renverra vers une page de login les autres ^^
le fichier connectetoi.htm. Il contient un formulaire de login et un formulaire d'inscription:
<form id="auth" method="POST" action="./session.php?mod=connect"> <center> <TABLE BORDER="0"> <CAPTION>Identifiez vous:</CAPTION> <TR><TH>Identifiant:</TH><TD><input name="connect_id" type="text"></TD></TR> <TR><TH>Mot de passe:</TH><TD><input name="connect_pwd" type="password"></TD></TR> <TR><TH></TH><TD><input name="connect_submit" value="Se connecter" type="submit"></TD></TR> </table> </center> </form> <br/><br/> <form id="inscription" method="POST" action="./session.php?mod=inscription"> <center> <TABLE BORDER="0"> <CAPTION>Inscrivez vous:</CAPTION> <TR><TH>Votre identifiant</TH><TD><input name="inscription_id" type="text"></TD></TR> <TR><TH>Mot de passe:</TH><TD><input name="inscription_pwd" type="password"></TD></TR> <TR><TH>Confirmez votre mot de passe:</TH><TD><input name="inscription_pwd_confirm" type="password"></TD></TR> <TR><TH></TH><TD><input name="inscription_submit" value="S'inscrire" type="submit"></TD></TR> </table> </center> </form>
Le fichier session.php qui gèrera toutes les ations relatives à l'inscription, la connection, etc...:
<?php if (!mysql_connect('localhost', 'MySqlUsername', '**********************')) { echo 'Impossible de se connecter à MySQL'; exit; } mysql_query("USE blogdugrouik"); // Generate a random character string function rand_str($length = 32, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890') { //fonction par kyle dot florence [@t] gmail dot com 08-May-2009 10:23 //publiée là: http://php.net/manual/fr/function.rand.php // Length of character list $chars_length = (strlen($chars) - 1); // Start our string $string = $chars{rand(0, $chars_length)}; // Generate random string for ($i = 1; $i < $length; $i = strlen($string)) { // Grab a random character from our list $r = $chars{rand(0, $chars_length)}; // Make sure the same two characters don't appear next to each other if ($r != $string{$i - 1}) $string .= $r; } // Return the string return $string; } function placecookie($iduser){ //c'est la fonction qui place un cookie chez l'utilisateur $valuecookie = rand_str(); setcookie('authbycookies[value]',$valuecookie,time()+60*60*24*30,'/authbycookies/','blog-du-grouik.tinad.fr'); mysql_query("UPDATE test_cookies SET update_time=NOW(), unique_key='".md5($valuecookie)."', user_ip='".$_SERVER["REMOTE_ADDR"]."' WHERE user_id='".$iduser."'"); } session_start(); //par défaut: $_SESSION['is_registered']=(int)(0); switch($_GET['mod']){ case "inscription": //Vérifier que les trois champs sont remplis if (($_POST['inscription_pwd']=="")||($_POST['inscription_pwd_confirm']=="")||($_POST['inscription_id']=="")){ echo "<center><H1>Vous n'avez pas renseigné tous les champs pour l'inscription</H1></center>"; include("connectetoi.htm"); die; } //vérifier la correspondance des mots de passe if($_POST['inscription_pwd'] <> $_POST['inscription_pwd_confirm']){ echo "<center><H1>les mots de passe ne correspondent pas</H1></center>"; include("connectetoi.htm"); die; } //bon bah on inscrit l'user mysql_query("INSERT INTO test_cookies( username, password, update_time) VALUES( '".mysql_real_escape_string($_POST['inscription_id'])."', '".mysql_real_escape_string(md5($_POST['inscription_pwd']))."', NOW()"); //et arf, on le connecte $_SESSION['is_registered']=(int)(1); $_SESSION['username']=$_POST['inscription_id']; //trouve son userid $result=mysql_query("SELECT user_id FROM test_cookies WHERE username='".mysql_real_escape_string($_POST['inscription_id'])."' AND password='".mysql_real_escape_string(md5($_POST['inscription_pwd']))."'"); $row=mysql_fetch_row($result); //cookie placecookie($row[0]); header("Location: index.php"); die; break; case "connect": //on cherche un enregisrement contenant l'username et le md5 du mot de passe $result=mysql_query("SELECT user_id FROM test_cookies WHERE username='".mysql_real_escape_string($_POST['connect_id'])."' AND password='".mysql_real_escape_string(md5($_POST['connect_pwd']))."'"); $row=mysql_fetch_row($result); if (isset($row[0])){ //ok //on enregistre la connection mysql_query("UPDATE test_cookies SET update_time=NOW() WHERE user_id='".$row[0]."'"); $_SESSION['is_registered']=(int)(1); $_SESSION['username']=$_POST['connect_id']; //cookie placecookie($row[0]); //redirection header("Location: index.php"); die; }else{ //fail $_SESSION['is_registered']=(int)(0); header("Location: index.php"); die; } break; case "deco": //echo "plop"; //setcookie('authbycookies[value]',''); setcookie("authbycookies[value]", false, time() - 3600,'/authbycookies/','blog-du-grouik.tinad.fr'); $_SESSION['is_registered']=(int)(0); unset($_COOKIE['authbycookies']['value']); session_unset(); header("Location: index.php"); die; break; default: } //test de l'authentification par cookie if (!isset($_COOKIE['authbycookies']['value'])){ $_SESSION['is_registered']=(int)(0); }else{ $result = mysql_query("SELECT user_id, username FROM test_cookies WHERE unique_key='".md5($_COOKIE['authbycookies']['value'])."' AND user_ip='".$_SERVER["REMOTE_ADDR"]."'"); $row=mysql_fetch_row($result); if (isset($row[0])){ //ok //on enregistre la connection mysql_query("UPDATE test_cookies SET update_time=NOW() WHERE user_id='".$row[0]."'"); $_SESSION['is_registered']=(int)(1); $_SESSION['username']=$_POST['connect_id']; // renouvellement du cookie placecookie($row[0]); }else{ $_SESSION['is_registered']=(int)(0); } } ?>
la table dans la base de données est faite comme ça:
mysql> describe test_cookies; +-------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------+------+-----+---------+----------------+ | user_id | int(11) | NO | PRI | NULL | auto_increment | | username | text | NO | | NULL | | | password | text | NO | | NULL | | | update_time | datetime | NO | | NULL | | | unique_key | text | NO | | NULL | | | user_ip | text | NO | | NULL | | +-------------+----------+------+-----+---------+----------------+ 6 rows in set (0.01 sec)
Conclusion
C'est super brouillon, et j'ai la flegme d'écrire un tutoriel là dessus. Mais ça me resservira de l'avoir à porté de main.
Commentaires
JE trouve que ça fait beaucoup de lignes pour un cookie...
Explique moi un peu l'utilité de tout ça alors que tu pourais faire un truc du genre
if(isset$_SESSION['REMOTE_ADDR'] && $_SERVER['REMOTE_ADDR']==$_SESSION['REMOTE_ADDR'])
{
include('login.php');
//
}
avec les verrifications qui s'imposent et l'affectation de $_SESSION['REMOTE_ADDR'] dans login.php evidement...
Bonjour flémar
Mon titre est mal choisi car ce n'est pas que le cookie, mais aussi l'authentification normale là.
je l'utilise de cette manière au début des fichiers qui sont appelés par des includes. ça fait moins de code que dans votre commentaire:
if($_SESSION['is_registered']!="1")
{
echo "";
die;
}
Dans l'index.php cependant là si l'utilisateur n'est pas authentifié, je le renvoie sur la page de loggin, et ce n'est qu'une fois:
if (!isset($_SESSION['is_registered'])){
include("./session.php");
}
switch($_SESSION['is_registered']){
case "1":
//site
break;
default:
include("./connectetoi.htm"); break;
}
Ce qui revient exactement au même que votre proposition sauf que j'ai appelé votre fichier "login.php" session.php