All posts in PHP

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.

Comparateur de fichier (diff tool) sur mac

Changes vous permet de comparer vos fichiers et vos dossiers pour trouver rapidement les différences. L’interface est très intuitive, et l’application peut être intégrée sous forme de plug-in dans la plupart des éditeurs de texte (Textmate, Coda, BBEdit, Espresso, …). Il permet de travailler sur des fichiers locaux, ainsi qu’avec les système de versionning (Subversion, CVS, Git, …).

J’ai découvert ce soft via Daring Fireball, qui propose un coupon de réduction de 10 $ pour le logiciel, jusqu’au 15 avril.

[ad#posts]

Bespin, éditeur de texte en ligne


Bespin est un éditeur de texte en ligne, issu du Mozilla Lab. Utilisant Canvas, il permet d’éditer vos fichiers html, js, … en ligne. Après un petit test rapide, l’outil semble rapide et efficace. Il dispose d’une ligne de commande permettant d’utiliser des fonctions (remplacer du texte avec des regex, trim, …).
Pour le tester, il faut utiliser un navigateur supportant Canvas, et les fonctions avancées de Canvas, donc Firefox 3 ou une nightly de Safari.


Introducing Bespin from Dion Almaer on Vimeo.

Plus de détail sur l’annonce de Bespin sur Ajaxian.

[ad#posts]

Personnaliser l’affichage des carte Google Map


Andreas Gohr présente comment personnaliser une carte Google Map, pour générer une carte de pirate. Il utilise la librairie GD, ainsi qu’une classe qu’il a développé pour récupérer une carte depuis Google Map et la personnaliser, en appliquant des effets sur la carte et en ajoutant par dessus des photos, des marqueurs, etc. Une idée intéressante pour customiser un peu les cartes affichées sur un site.

[ad#posts]

Stream d’un fichier pdf généré à la volée et page blanche avec IE

De plus en plus de site web ont recours à la génération de pdf à la volée, que ce soit pour permettre au visiteur de sauvegarder une page d’un site, ou pour générer divers documents (facture, fiche produit, formulaire personnalisé, …).
J’utilise ce système sur de nombreux sites, et je viens de découvrir un problème survenant avec IE sur pc. Sur certains sites en effet, plutôt que d’afficher le pdf généré et renvoyé au navigateur par le serveur, IE affiche une page blanche.
Après investigation, il apparaît que le problème survient si le serveur est configuré pour envoyer les pages au navigateur en les compressant en gzip. La compression semble perturber IE. Même si le serveur n’est configuré que pour compresser les pages html et php par exemple, puisque la génération du pdf se fait par l’appel d’une page php, le flux de retour est compressé en gzip, et modifier le header retourné par le serveur n’y change rien.
La solution à ce problème est soit de générer automatiquement le pdf, puis de mettre un lien pointant directement sur le fichier pdf (dans ce cas le flux de réponse ne sera pas compressé puisqu’on appelle un fichier pdf), soit, plus radical, supprimer la compression gzip pour les pages à destination d’IE. La premiére solution n’est pas toujours possible, la deuxième est un peu extrême à mon gout. Si vous connaissez une autre technique, je suis preneur.

WinBinder, transformez vos scripts PHP en applications natives pour Windows

WinBinder est une nouvelle extension open source qui permet de transformer vos scripts PHP en applications pour Windows. La transformation semble assez facile à voir les exemples sur le site. Jusqu’ici, la meilleure solution pour distribuer des applications PHP me semblait être ZazouMiniWebServer, mais WinBinder, permettant de distribuer de véritables applications, semble encore mieux.

Exemple WinBinder

Nouveau webmail PHP, XHTML et CSS

L'interface de RoundCubeRoundCube est un nouveau webmail imap écrit en PHP, entièrement skinnable en CSS. Le projet en est au tout début, mais il semble prometteur, et il propose déjà une version utilisable. Le but est de simuler le comportement d’un lecteur de courrier classique (drag & drop, règles de tri, fichier attachés, …), le tout implémenté avec les dernières technologies web (CSS2, javascript, DOM, XMLHttpRequest). A suivre…

Le site de RoundCube >>

Recherche testeurs pour module PHP 5 sous OS X

Marc Liyanage met à jour ses packages pour OS X (PHP, Postgre, MySql, …) et recherche des testeurs pour son premier package, PHP 5.0.5.

http://www.entropy.ch/phpbb2/viewtopic.php?p=7928