ValidatorEmailList : valider une liste d'e-mails

0 gravatar Par Grégoire Marchal - 25/01/2011

Voici un petit validateur pas très compliqué mais pratique, qui valide une liste d'adresses e-mails présentes dans un champ type "textarea".

class ValidatorEmailList extends sfValidatorBase
{
  public function doClean($value)
  {
    $aValues = explode("\n", $value);
    $aValues = array_map('trim', $aValues);
    $aEmails = array();
    
    $oEmailValidator = new sfValidatorEmail();

    foreach ($aValues as $sEmail)
    {
      // ignore empty lines
      if ($sEmail != '')
      {
        // verify email syntax using sfValidatorEmail
        // sfValidatorError exception will be thrown if invalid
        $oEmailValidator->clean($sEmail);
        $aEmails[] = $sEmail;
      }
    }

    return $aEmails;
  }
}

Ça me fait penser qu'on pourrait améliorer ça en passant le validateur (ici "new sfValidatorEmail()") en option de ValidatorEmailList pour pouvoir valider des listes de ce qu'on veut en fait... Enfin je vous laisse le faire !

Projet sfAntiBruteForcePlugin

7 gravatar Par Grégoire Marchal - 11/12/2010

Récemment j'ai créé une petite application web pour jouer un peu avec les API de twitter, qui est sécurisée par un classique login / mot de passe : sécurité basique. Je me suis rapidement dit qu'il faudrait ajouter une sécurité pour parer aux attaques de type "brute force". Pour rappel, ce type d'attaque consiste à tenter un maximum de couples login / mot de passe sur un formulaire en espérant trouver des identifiants qui fonctionnent (plus d'info). J'ai rapidement cherché quelques informations à ce sujet et espéré trouvé un plugin symfony tout fait (on peut espérer non ?). Résultat : pas de plugin, mais quelques informations intéressantes qui m'ont donné envie de me lancer dans le développement du fameux plugin que je cherchais ! Je vais donc recenser ces informations ici, faire une sorte de cahier des charges collaboratif pour mon plugin, et j'espère bien que vous allez m'y aider !

La plupart des informations intéressantes que j'ai récoltées proviennent de ce tutoriel : un anti brute-force léger et rapide.

Le principe : empêcher un utilisateur et surtout un robot de tenter une infinité de couples login / mot de passe sur une page d'authentification donnée. Pour cela, on doit compter les tentatives erronées des utilisateurs, et les empêcher à partir d'un certain seuil de tentatives par unité de temps. Voici les questions qui en découlent.

Sur quoi se baser pour compter les tentatives ?

Sur l'IP du client ? Mauvaise idée, cette donnée n'est pas fiable, la plupart des hackers sauront la modifier et pourront donc faire une infinité de tentatives.

Sur les sessions ? Non plus, il suffit d'effacer le cookie et c'est reparti pour d'autres tentatives...

Sur le login utilisé pour l'authentification ? Ce n'est pas une solution parfaite, mais c'est la meilleure que j'ai trouvé à ce jour. L'inconvénient principal est que n'importe qui peut bloquer le compte de quelqu'un s'il connait son login...

Si vous avez d'autres idées, je suis preneur !

Comment stocker le nombre de tentatives effectuées ?

En base de données ? Ca semble être la solution la plus logique. Néanmoins, certains projets ne fonctionnent pas avec une base de données (et c'est mon cas ici !). Ce serait un peu intrusif de devoir créer une base pour ça, de charger un ORM etc.

Dans des fichiers ? C'est la solution qu'a choisi l'article ci-dessus. Niveau rapidité, charge etc., il faudrait faire un comparatif avec l'utilisation d'un ORM pour savoir qui est le meilleur. Cette solution est moins envahissante dans la mesure où elle fonctionnerait sur tous les projets symfony (à ma connaissance).

Si vous avez d'autres idées...

Où intervenir au niveau du code symfony ?

A la base, j'avais pensé qu'il faudrait faire un filtre pour contrôler les accès en amont dans l'application. Le problème est que, si je pars sur la solution d'utiliser le login pour compter les tentatives, j'ai besoin de ce login pour incrémenter son compteur de tentatives en cas d'échec. J'ai donc également besoin de savoir si la tentative d'autentification est un échec ou non. Pour cela, j'ai donc besoin d'internvenir au niveau du contrôleur, dans l'action qui gère l'authentification. J'imagine que le développeur devra ajouter un appel de ce genre lorsqu'une tentative d'authentification échouera :

sfAntiBruteForceManager::notifyFailedAuthentication($identifier);

Cette méthode aura pour but d'incrémenter le compteur d'échec pour cet utilisateur.

Il faudrait également, avant la tentative d'authentification, vérifier que l'utilisateur qui s'apprête à s'authentifier a le droit de le faire :

if (sfAntiBruteForceManager::canTryAuthentication($identifier))
{
  // ...
}

Faut-il s'intégrer au plugin sfGuard ?

Dans un second temps peut-être, on va pas mettre la charrue avant les bœufs ! Mais bon, à creuser.

Voilà pour les premières pistes pour la réalisation de ce petit plugin qui me semblerait très utile pour tout développeur sensible à la sécurité de son application (aka tout bon développeur !). J'attends vos remarques / suggestions / idées avec impatience !

PS : suivez les évolutions sur la page officielle de sfAntiBruteForcePlugin.

Gare au whereIn !

2 gravatar Par Grégoire Marchal - 17/10/2010

Doctrine c'est cool, ça simplifie la vie, on n'a plus à se palucher des énormes requêtes SQL à la main, ça fait plaisir. Mais parfois c'est vicieux Doctrine. Dernier exemple en date avec la méthode whereIn(). A priori, rien de compliqué avec cette méthode, elle transforme gentiment ça :

$query->whereIn('id', array(1, 2, 3))

En ça :

AND id IN (1, 2, 3)

Mais imaginons que le tableau que vous lui passez est construit dynamiquement, et que parfois il est vide... Que se passe-t-il dans ce cas ? Avec MySQL, l'instruction "IN ()" retourne une erreur. Que fait Doctrine ? Eh bien... Rien. Il ignore le critère et effectue la requête, donnant des résultats souvent trompeurs... Une exception est censée être levée, mais une coquille dans le code fait qu'elle ne l'est pas. Un ticket est ouvert chez Doctrine... Attention donc !