Protection contre les attaques CSRF

Mise en place d'un système de défense contre Cross-site request forgery

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

CSRF est souvent appelée "l'attaque sur l'inconscient" car la victime, n'effectuant aucune opération dangereuse, ne se rend compte de rien. Cet article montrera quel moyen de protection on utilisera dans notre exemple d'application Spring.

Se protéger contre CSRF sous Spring

Une autre faille de sécurité potentielle concerne CSRF. L'attaque est expliquée plus en détails dans l'article de mon blog consacré aux façons de protéger une application web contre les attaques CSRF.

L'idée est de créer un outil qui va construire un token unique par utilisateur dans toutes les actions qu'il peut effectuer (modification du compte, emprunt d'un livre...). Le token unique sera basé sur 4 éléments : 2 chaînes de caractères qui serviront à "hasher" le token, 1 intention (action que l'utilisateur veut effectuer) et 1 sel (unique par utilisateur, servira à encoder les 3 éléments précédents). Regardons ensemble dans le code :

    <beans:bean id="CSRFProtector" name="CSRFProtector" class="library.security.CSRFProtector">
        <beans:property name="expirationTime"><beans:value type="long">1800000</beans:value></beans:property>
        <beans:property name="hashStart" value="9F.fdsoN33!!!" />
        <eans:property name="hashEnd" value="Nofds$POE*dso.e" />
        <beans:property name="passwordEncoder" ref="passwordEncoder" />
    </beans:bean>

Et la classe Java :

afficher le code

Comment ça fonctionne dans une implémentation ? Pour le constater, on regardera le formulaire d'enregistrement :

@Controller
public class SubscriberController extends FrontendController {
    public String register(@ModelAttribute("subscriber") Subscriber subscriber, Model layout, RedirectAttributes redAtt, HttpServletRequest request) {
        // only code using CSRFProtector's instance is displayed
        try {
            csrfProtector.setIntention("register");
            subscriber.setToken(csrfProtector.constructToken(request.getSession()));
            subscriber.setAction(csrfProtector.getIntention());
            logger.info("Generated token " + subscriber.getToken());
        } catch(Exception e) {
            logger.error("An exception occured on creating CSRF token", e);
        }
    }
    // ...
}

On voit que la sécurisation est banale. Il s'agit juste d'un simple jeu des setters et des getters. La validation est beaucoup plus intéressante. Pour l'effectuer on crée une annotation pouvant être définie au niveau de la classe (@CSRFConstraint). Elle utilise le CSRFConstraintValidator pour valider le token transmis dans la requête avec celui qui est correct. Cette classe de validation implémente l'interface ConstraintValidator. On y injecte deux classes : HttpServletRequest et CSRFProtector. Cette première servira à récupérer la session de l'utilisateur. L'autre s'occupera de générer le token correct. Voici ses éléments regroupés :

afficher le code

Une autre solution, consistant à implémenter l'interface org.springframework.web.servlet.support.RequestDataValueProcessor, sera abordée dans une version améliorée du système.

Bartosz KONIECZNY Sécurité

Une question ? Une remarque ?

*

*

Un conseil Symfony1

Comment afficher le schéma du formulaire ?

Symfony 1.4 gérait déjà les formulaires avec une simplicité. Mais dans les situation de débuggage c'était très utile de voir leurs schémas. Pour ce faire, il suffisait d'utiliser :

print_r( $this->form->getWidgetSchema() );