lundi 23 juin 2008

Une classe de protection contre le flood

Il est très courant d'être victime de flood pour certaines fonctionnalités proposées par votre site.

Exemples :

Que penseriez vous d'une classe qui permettrait de bloquer (ou plutôt ralentir) les tentatives de flood sur les fonctionnalités sensibles de votre site ? (ex: pas plus d'1 post sur le forum toutes les 30 secondes).

Pour cela, nous allons utiliser memcache. Si vous ne connaissez pas memcache, c'est un système de cache distribué extrèmement rapide. Comme tout système de cache, il offre un mécanisme de stockage, de récupération et d'expiration des données.

Pourquoi memcache ?
C'est un outil pratique aussi bien dans un environnement multiserveur que sur un seul serveur, donc il est accessible à un grand nombre de personnes (désolé ca n'inclu pas les comptes de type "mutualisés" grand public).
Il propose également un mécanisme d'expiration qui correspond parfaitement à nos besoins pour ce système de flood-control.

Principe :
l'extension memcache pour PHP propose une fonction Memcache::add() qui permet d'ajouter une donnee au cache si et seulement si celle-ci n'existe pas déjà.
A chaque action d'un utilisateur que nous voulons limiter, il suffit donc d'ajouter une information dans le cache avec une date d'expiration correspondant au temps d'attente necessaire avant la prochaine action. Si la fonction add() renvoie une erreur, c'est qu'une action a déjà été effectuée durant cette intervalle de temps.

Scénario type : (flood control sur un forum, 1 post toutes les 30 sec)
11:00:00 : jean-craoude much poste un message sur le forum. Le forum ajoute une entrée au cache qui a pour ID "ForumPost:JeanCraoudeMuch" et pour date d'expiration 30 secondes.
11:00:15 : jean-craoude Much s'endort malencontreusement sur la touche F5 de son clavier. Toutes les nanosecondes, une requête est donc envoyée au serveur pour poster à nouveau le même message. Le forum va donc pour chaque requête tenter de remettre en cache une donnée ayant pour ID "ForumPost:JeanCraoudeMuch", mais celle-ci existant déjà, la fonction Memcache::add() va renvoyer une erreur. Le post sera donc refusé.
11:00:29 : jean-craoude Much se réveille et se regarde dans le mirroir. Voyant un F5 rouge se dessiner sur son front, il se précipite sur le forum pour voir l'etendue de ses dégats. Fort heureusement, tout va bien, il est rassuré. Il se rendort donc consciencieusement, mais cette fois-ci à côté du clavier

Bref, si vous êtes séduit par ce système, voici une petite classe qui vous permettra de le mettre en place très facilement : BigOrNot_FloodControl

Exemple d'utilisation :

/* Initialisation d'un objet memcache */
$cache = new Memcache;
$cache->addServer('127.0.0.1');

/* Initialisation de la classe flood control */
$floodControl = new BigOrNot_FloodControl($cache);

/* ... */

/* Utilisation de la classe pour limiter les posts dans un forum */
$userId = Forum::getUserId();
if ($floodControl->isAllowed('posterUnMessageDansLeForum', $userId, 30))
{
    posterMessage();
}
else
{
    messageErreur();
}

Voila, c'est simple et efficace. Inutile de s'armer d'une base de données pour ce genre de choses.

Notes :

  • Avec cette solution, il n'y a pas la possibilité de faire des restrictions avec une intervalle inférieure à 1 seconde. Pour les amateurs de nanosecondes, cela nécessitera quelques modifications.
  • Je n'ai pas utilisé Zend_Cache car je n'ai pas trouvé de mécanisme équivalent au add() de Memcache. Il y a probablement moyen de mettre en place un système de lock qui y ressemblerait, mais ca ne m'enchantait pas trop... Alors si l'envie vous prend, n'hesitez pas ;) Méfiez vous des appels simultanés à votre système

Aucun commentaire: