Protection contre les attaques XSS

Filtrage des données dans Spring

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

Le chapitre précédent abordait la problématique importante de l'utilisation du Spring Security. Néanmoins, il ne s'agit pas d'une seule thématique liée à la sécurité des applications web écrites avec Spring. Ces applications, comme toutes les autres d'ailleurs, sont sensibles à des attaques répandues dans le milieu d'internet : XSS, CSRF, problèmes des mots de passes.

Afin de minimaliser la casse que peut être provoquée par ces attaques, on a créé quelques outils supplémentaires de protection. Ils seront compris dans le fichier qu'on a utilisé pour définir les règles d'authentification et d’autorisation du Spring Security. Ils feront l'objet de cet article.

Se protéger contre XSS sous Spring

Spring supporte la protection contre les attaques XSS nativement. Cela peut être fait du côté des fichiers de vue, avec la configuration suivante du fichier web.xml :

<context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
</context-param>

Cette solution ne nous satisfait pas. Elle fait en sorte que toutes les données saisies par l'utilisateur soient vidées des balises HTML. Nous, ce qu'on veut dans notre application, c'est de permettre à l'utilisateur d'utiliser certaines balises HTML, utilisées pour le formattage du texte (b, i, em, strong). C'est pourquoi on va créer un outil permettant de garder uniquement les balises placées dans notre whitelist.

Voici la configuration de cet outil :

<beans:bean id="XSSCleaner" name="XSSCleaner" class="library.security.XSSCleaner">
        <beans:property name="acceptedTags">
            <beans:list>
                <beans:value>b</beans:value>
                <beans:value>i</beans:value>
                <beans:value>em</beans:value>
                <beans:value>strong</beans:value>
            </beans:list>
        </beans:property>
    </beans:bean>

Ce code ne nécessite pas des explications très longues. La seule propriété de notre classe XSSCleaner contient une liste des balises acceptées. Le coude de la classe elle-même est plus complexe :

public class XSSCleaner {
    final Logger logger = LoggerFactory.getLogger(XSSCleaner.class);
    /**
     * Array with tags accepted by webapp.
     * @access private
     * @return List
     */
    private List<String> acceptedTags;
    private String regexAcceptedTags = "";
    
    public void setAcceptedTags(List acceptedTags) {
        logger.info("=> Setting accepted tags " + acceptedTags);
        this.acceptedTags = acceptedTags;
        StringBuffer tagsBuffer = new StringBuffer();
        int tagsSize = this.acceptedTags.size();
        for (int i = 0; i < tagsSize; i++) {
            tagsBuffer.append(this.acceptedTags.get(i));
            if ((i + 1) < tagsSize) tagsBuffer.append("|");
        }
        regexAcceptedTags = tagsBuffer.toString();
    }

    public List getAcceptedTags() {
        return acceptedTags;
    }
    
    public String filterString(String toFilter, List<String> acceptedTags) {
        String filteredString = "";
        // Pattern strictPattern = Pattern.compile("\\<.*?\\>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        Pattern strictPattern = Pattern.compile("((?!\\<(|/)("+regexAcceptedTags+")\\>)(\\<.*?\\>))", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        return strictPattern.matcher(toFilter).replaceAll("").trim();
    }

    public String filterString(String toFilter) {
        return filterString(toFilter, acceptedTags);
    }
}

On remarque la présence d'une méthode clé, filterString(), qui s'occupe de supprimer les balises d'une chaîne de caractères avec l'utilisation d'une expression régulière. Toutes les balises correspondant au pattern, sont remplacées par une chaîne de caractères vide ("").

Bartosz KONIECZNY Sécurité

Une question ? Une remarque ?

*

*

Un conseil Symfony2

Comment exécuter son code dans le listener ?

Pour ce faire il suffit de surcharger la méthode handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) qui se trouve dans AppKernel.php .

Une nouvelle méthode handle() peut se présenter ainsi :

function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
{
  echo 'handle request';

  return parent::handle($request, $type, $catch);
}