Sécuriser ses formulaires avec une clé unique
Il y a quelques jours je suis tombé sur un article très intéressant de Wouter Bulten concernant la sécurité lors de la soumission de formulaire web. Sa solution contre le cross-site scripting ? Créer une classe PHP qui nous permettra de créer une clé unique pour chaque formulaire afin de n’accepter que les requêtes provenant de notre site web.
Comment cela va se passer ?
- D’abord il nous faudra un champ caché qui contiendra la clé unique pour notre formulaire.
- Enregistrer cette clé dans une session PHP
- Vérifier la clé lors de la soumission du formulaire
Notre formulaire
Pour l’exemple on utilisera un bête formulaire de login, qui est tout de même l’un des formulaires les plus importants d’un site, puisque c’est lui qui donne accès à certaines informations plus ou moins « sensibles ».
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head><meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title>Securing forms with form keys</title> </head> <body> <form action="" method="post"> <dl> <dt><label for="username">Username:</label></dt> <dd><input type="text" name="username" id="username" /></dd> <dt><label for="username">Password:</label></dt> <dd><input type="password" name="password" id="password" /></dd> <dt></dt> <dd><input type="submit" value="Login" /></dd> </dl> </form> </body> </html>
Maintenant qu’on a notre formulaire, il est temps de commencer le processus de sécurisation
Créer une classe PHP
Nous allons donc créer un classe PHP qui servira à la génération d’une clé et la validation de celle-ci, d’abord parce que l’utilisation de la programmation orientée objet c’est sympa, et surtout parce que cela nous permettra d’utiliser cette classe partout où on le désirera. On appellera se fichier securekey.class.php
<?php
Class SecureKey
{
private $secureKey ; //variable qui permettra de stocker la clé générée
private $old_secureKey ; //varible qui stocke l’anciene valeur de la clé, vous comprendrez plus tard
//fonction pour générer la clé
private function generateKey()
{
}
}
?>
Ici on a donc la structure de base de notre classe. Elle contient donc deux variables ($secureKey et $old_secureKey) et une fonction. Nos variables et notre fonction on l’attribut private car elles ne seront pas utilisées autre part que dans notre classe. D’autres fonctions s’occuperont de faire le lien entre la partie privée de notre classe et le formulaire.
Passons maintenant à la génération de la clé. Du fait que notre clé doit être unique, on utlisera l’adresse IP de l’utilisateur comme base pour notre clé et on y ajoutera un nombre aléatoire avec les fonction mt_rand() et uniqid(). On cryptera le tout en MD5 avec la fonction md5(), on pourrai aussi utiliser un autre cryptage comme le SHA256. Ainsi le hash qui résultera de cette manipulation ne refletera pas la valeur de notre clé.
private function generateKey()
{
$ip = $_SERVER[‘REMOTE_ADDR’] ; // récupération de l’adresse IP
//on utlilise mt_rand() pour avoir une valeur plus aléatoire qu’avec la fonction rand(),
//et on passe true en paramètre à uniqid() pour lui dire qu’on veut une longue chaine de caractère
$uniqid = uniqid(mt_rand(),true) ;
return md5($ip . $uniqid); // on retourn le hash
}
[
Insertion d’une clé dans notre formulaire
Ici on va créer une nouvelle fonction dans notre classe, qui permettra d’inserer la valeur de notre clé dans un champ caché du formulaire. Notre fonction marchera ainsi :
- On génère la clé avec generateKey()
- On enregistre la clé dans la variable $secureKey et dans une session
- On revoie la valeur au champ de formulaire
On appellera notre fonction outputKey() et on la définira comme publique, puisqu’elle sera utilisée hors de notre classe. Cette fonction utilisera la fonction privée générateKey() et on enregistrera la clé dans une session.
public function outputKey()
{
//on génère la clé et on l’enregistre dans la classe
$this->secureKey = $this->generateKey() ;
//enregistrement de la clé dans la session
$_SESSION[‘secure_key’] = $this->secureKey ;
//envoi de la clé dans le formulaire
echo "<input type= 'hidden' name='secure_key' id='secure_id' value='" .$this->secureKey."' />";
}
Maintenant on va ajouter notre clé à notre formulaire de login pour sécuriser tout ça. Pour se faire on doit inclure notre classe dans notre fichier login.php. On doit aussi à la fonction session_start() puisque notre classe utilise les sessions pour stocké la valeur de la clé.
session_start() ; require(‘securekey.class.php’) ; $secureKey = new secureKey() ; //instanciation de la classe
Désormais on a plus qu’à modifier notre fomulaire login.php pour qu’il contienne la clé en champ caché.
<form action="" method="post"> <dl> <?php $secureKey->outputKey(); ?> <dt><label for=”username”>Username:</label></dt> <dd><input type=”text” name=”username” id=”username” /></dd> <dt><label for=”password”>Password:</label></dt> <dd><input type=”password” name=”password” id=”password” /></dd> </dl> </form>
Validation du formulaire
Ici je ne traiterai pas la validation du formulaire complet, juste ce qui concerne notre clé unique. Faites un recherche sur google pour le des tutoriaux sur la validation de formulaire.
Allez c’est parti ! C’est ici que vous allez comprendre à quoi nous sert notre variable $old_secureKey que l’on a déclaré plus tôt dans notre classe. Du fait que notre fonction generateKey() réécrit la valeur de notre variable de sessions, on doit ajouter un constructeur à notre classe PHP qui permettra de stocker l’ancienne valeur de la session dans la variable $old_secureKey si elle existe.
function __construct()
{
if(isset($_SESSION[‘secure_key’]))
{
$this->old_secureKey = $_SESSION[‘secure_key’];
}
}
Maintenant on va pouvoir valider notre formulaire. Ici on utilisera une bête fonction publique dans notre classe PHP qui comparera la valeur de notre clé passé en POST par le formulaire et la valeur stockée dans la variable de classe $secureKey.
public function validate()
{
if($_POST[‘secure_key’] == $this->old_secureKey)
{
return true; //notre clé est valide, on retourne vrai
}
else
{
return false; //notre clé n’est pas valide
}
}
Dans notre fichier login.php maintenant, on valide la clé en utilisant la fonction publique que l’on vient juste de créer. Bien entendu on ne valide que si on a une requête POST. Ajoutez le code suivant juste après la ligne $secureKey = new secureKey() ;
$erreur = ‘Aucune erreur’ ;
//est-ce qu’une requête POST est passée ?
if($_SERVER[‘REQUEST_METHOD’] == ‘post’)
{
//on valide la clé
if(!isset($_POST[‘secure_key’]) || !$secureKey->validate())
{
//la clé est invalide
$erreur = ‘clé invalide!’;
}
else
{
//suite de la validation
$erreur = ‘clé valide’;
}
}
Ici on a créé une variable $erreur qui stock notre message d’erreur dans le cas où l’on souhaite afficher un message d’erreur. Si une requête POST a été envoyée on valide notre clé avec $secureKey->validate(), si la valeur de retour est false, la clé est invalide et on affiche le message d’erreur.
Tweets en cours de chargement ...












24 juin, 2009 à 6 h 50 min
Pretty cool post. I just came by your site and wanted to say
that I’ve really enjoyed browsing your blog posts. Any way
I’ll be subscribing to your feed and I hope you write again soon!
9 juillet, 2009 à 17 h 27 min
Thanks for your comment
17 novembre, 2009 à 17 h 24 min
salut ! Je fais un formulaire pour un site d’annonces en plus du captcha déja présent je vais rajouter ta technique, trés cool … merci pour tout et bon travail sur le web
22 novembre, 2009 à 2 h 41 min
De rien, je suis content qu’un de mes articles t’ai été utile
Bonne continuation
26 novembre, 2009 à 15 h 25 min
My article translated in French, that was the last thing I expected to happen
26 novembre, 2009 à 15 h 37 min
Yeah, I though your article on Net.tutsplus.com was good so I decided to make a french translation for non-english people ^^. I hope you’re okay with that.
28 novembre, 2009 à 13 h 58 min
[...] has even been translated into french: http://www.angechierchia.com/php/securiser-ses-formulaires-avec-une-cle-unique/) If you are using Zend Framework you can easily add this kind of protection to your forms. You only [...]
28 novembre, 2009 à 14 h 03 min
Yes of course! The more people learn it the better
It would also be great if you link to the article on my blog but that’s up to you