Archives pour "Javascript"

Posted by Frato on 6th février 2010

Sécuriser un formulaire sans captcha

Eternel problème du développeur web, sécuriser les formulaires qui se trouvent sur un site pour empêcher les robots de soumettrent des formulaires de spam. Jusqu’à aujourd’hui, deux solutions s’offrent à nous pour bloquer ces robots :

  • Utiliser un captcha
  • Utiliser les services d’un filtre antispam, genre Akismet

Je suis personnellement totalement contre les captcha, qui compliquent beaucoup trop la saisie d’un formulaire, qui se doit d’être simple si on veut que le visiteur le remplisse et l’envoi. Les captcha affichent des images de plus en plus compliquées, au point que même un être humain a du mal à identifier les lettres qu’il est censé recopier.
Pour les filtres anti-spam, je n’ai pas pris le temps de tester, peut être est-ce efficace, mais cela fait reposer le bon fonctionnement de votre formulaire sur un service tiers, et ajoute donc des potentialités de pannes.

La solution que j’utilise était jusqu’à présent composée d’une double protection, qui s’est avérée insuffisante, je viens donc d’en ajouter une troisième.

1. Contrôler les champs renvoyés par le formulaire :

Il arrive que les robots ajoutent des champs au formulaire qu’ils renvoient. La solution consiste donc à ne traiter que les formulaires qui ne contiennent que les champs que nous avons défini.
Pour l’exemple, utilisons un formulaire de contact ou l’utilisateur entre son nom, son mail et un message. Le formulaire retournera donc 4 résultats : nom, mail, message, ainsi que la valeur du bouton submit (si le formulaire est envoyé avec un champ input submit et pas un lien).

En php, on définit une liste de champs acceptés :

$whitelist = array('nom', 'mail', 'message', 'envoyer');

Lors de la soumission du formulaire, on contrôle ce qui est retourné :

if(checkWhitelist($whitelist))
{
     // Traitment du formulaire
}

La fonction checkWhitelist retourne false s’il trouve un champ qui ne figure pas dans la whitelist

function checkWhitelist ($list)
{
	foreach ($_POST as $key => $item)
	{
		if (!in_array($key, $list))
		{
			return false;
		}
	}

	return true;
}

2. Placer un token en session

De nombreux robots ne sont pas capable de gérer des cookies. La solution consiste donc à placer un nombre aléatoire dans un champ caché du formulaire, et à stocker ce nombre en session. Une fois que le formulaire est retourné, on vérifie que le nombre retourné par le formulaire est identique au nombre stocké en session. Les robots ne gérant pas les sessions ne retourneront rien, et du coup le formulaire ne sera pas traité.

Au chargement de la page, on génère un token (le paramètre de la fonction est le nom du formulaire) :

$token = generateFormToken('contact_form');

La fonction generateFormToken génère un nombre aléatoire, le stock en session, et le retourne.

function generateFormToken($form)
{
    $token = md5(uniqid(microtime(), true));
    $_SESSION[$form.'_token'] = $token;
    return $token;
}

On place ensuite ce token dans un champ caché du formulaire :

<input type="hidden" name="token" value="<?php echo $token; ?>" />

Lorsque le formulaire est retourné, on vérifie le token :

if(verifyFormToken('contact_form'))
{
    // Traitement du formulaire
}

Fonction verifyFormToken :

function verifyFormToken($form)
{
    if (!isset($_SESSION[$form.'_token']))
    {
        return false;
    }

    if (!isset($_POST['token']))
    {
        return false;
    }

    if ($_SESSION[$form.'_token'] !== $_POST['token'])
    {
        return false;
    }

    return true;
}

3. Changer le contenu d’un champ caché en javascript

Les robots ne remplissent pas les formulaires, ils se contentent d’envoyer directement le contenu du formulaire à l’adresse définie. La troisième solution consiste donc à placer un champ caché dans le formulaire, avec une valeur de 0 (ou autre), et de changer la valeur de ce champ lorsque l’utilisateur saisie son nom (ou autre).

A l’aide de Jquery, on attache une action qui sera déclenchée dès que l’utilisateur placera son curseur dans le champ nom. Cette action va modifier la valeur de notre champ caché (appellé ici « control ») :

$(function(){
	$('#nom').bind('focus', function(){
		$("#control").val('666');
	})
});

Lorsque le formulaire est renvoyé, on vérifie que le champ control a bien la valeur 666 :

if($_POST['control'] == 666)
{
    // Traitement du formulaire
}

Pour résumer, voici le script en entier :

<?php
	session_start();

	function generateFormToken($form)
	{

	    $token = md5(uniqid(microtime(), true));
		$_SESSION[$form.'_token'] = $token;
	   	return $token;
	}

	function verifyFormToken($form)
	{

	    if (!isset($_SESSION[$form.'_token']))
		{
			return false;
	    }

		if (!isset($_POST['token']))
		{
			return false;
	    }

		if ($_SESSION[$form.'_token'] !== $_POST['token'])
		{
			return false;
	    }

		return true;
	}

	function checkWhitelist ($list)
	{
		foreach ($_POST as $key => $item)
		{
			if (!in_array($key, $list))
			{
				return false;
			}
		}

		return true;
	}

	$whitelist = array('nom', 'mail', 'message', 'envoyer', 'control', 'token');

	if(isset($_POST['nom']) && verifyFormToken('contact_form') && checkWhitelist($whitelist) && $_POST['control'] == 666)
	{
		// Traitement du formulaire
	}

	$token = generateFormToken('contact_form');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title></title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
	<script type="text/javascript">
		$(function(){
			$('#mail').bind('focus', function(){
				$("#control").val('666');
			});
		});
	</script>
</head>
<body>
	<form action="" method="post" name="contact_form">
		<label for="nom">Nom</label>
		<input type="text" name="nom" id="nom" />
		<label for="mail">Mail</label>
		<input type="text" name="mail" id="mail" />
		<label for="message">Message</label>
		<textarea name="message"></textarea>
		<input type="hidden" name="control" id="control" value="0" />
		<input type="hidden" name="token" value="<?php echo $token; ?>" />
		<input type="submit" name="envoyer" value="Envoyer"
	</form>
</body>
</html>

J’utilise cette solution depuis quelques temps, elle semble efficace. Concernant les deux premières techniques, je les ai trouvé sur un site anglais, mais pas moyen de retrouver l’adresse de ce site ; je l’ajouterai si je la retrouve. Pour la troisième technique, elle pose un problème si le visiteur du site a désactivé le javascript sur son navigateur. A vous de voir si cela est acceptable ou pas.

Si vous utilisez une autre technique, qui soit simple et efficace, n’hésitez pas à en faire part dans les commentaires.

Posted by Frato on 23rd novembre 2009

Espionner les visiteurs d’un site avec javascript

Posted by Frato on 1st avril 2009

Comparateur de fichier (diff tool) sur mac

Posted by Frato on 27th février 2009

Consulter la documentation Jquery avec Spotlight

Posted by Frato on 25th février 2009

Atlas, éditeur en ligne pour créer des applications avec le framework Cappuccino

Posted by Frato on 13th février 2009

Bespin, éditeur de texte en ligne

Posted by Frato on 8th février 2009

Un slideshow animé sans flash grâce à Canvas

Posted by Frato on 27th octobre 2008

Petite sélection de plug-in jquery

Posted by Frato on 5th février 2007

Plotr, librairie javascript pour générer des graphique (utilise Canvas)

Posted by Frato on 28th avril 2006

script.aculo.us Reflector, reflet d’image

Real Time Web Analytics