Se protéger contre les XSS

 

Comment mitiger les vulnérabilités XSS ? Eh bien il existe deux manières. La première consiste à modifier le code source de l'application dans le but de filtrer les inputs des utilisateurs. C'est ce que l'on appelle une protection logicielle. La seconde en revanche demande un peu plus de connaissances au niveau administration système puisqu'elle va consister à déployer un WAF et configurer nos serveurs web.

I/ Protection logicielle

A) Pour le langage PHP

Voici le formulaire que nous allons protéger:

<!DOCTYPE html>
<html>
  <body>
    <form action="#" method="get">
      <label>Que recherchez vous ?</label>
      <input type="text" name="recherche"/>
      <input type="submit" value="Rechercher"/>
    </form>
  <?php
    echo $_GET["recherche"];
  ?>
  </body>
</html>

Avant toute chose il est important de noter que les variables $_GET, $_POST et $_REQUEST sont automatiquement urldécodées (via l'utilisation de la fonction urldecode()) et ce de manière transparente. En revanche lorsque l'on traite des paramètres GET il est important de noter que si vous envoyez des données déjà encodées, alors cette dernière ne sera pas ré-encodé. Ainsi que vous envoyez ce payload:

ou celui-ci:

Revient strictement au même. Cependant si vous envoyez votre payload dans via un formulaire (post ou get peu importe) ce dernier sera directement urlencodé. Ainsi le payload:<script>alert()</script>

Sera encodé de cette manière:

Il est important de prendre en compte l'encodage naturel de notre navigateur car cela pourra modifier la manière donc nous exploiterons une XSS ou toute autre vulnérabilité web.

En ce qui concerne les fonctions de filtrage des inputs nous en avons 2 majoritaires:

  • htmlentities :

La fonction htmlentities va remplacer certains caractères ayant une signification spéciales en HTML par leurs équivalents en entités HTML. Voici les remplacement effectués par cette fonction:

Caractère Remplacement
& &amp;
" &quot;
' &apos; seulement lorsque le flag ENT_QUOTES est défini
< &lt;
> &gt;

Et le retour obtenu lorsque l'on applique cette fonction au payload classique:

Le payload est bien transformé ce qui empêche l'exécution du JavaScript.

  • filter_var:

Cette fonction est utilisé afin de valider le format d'une variable et en supprimer certains caractères. Dans le cas présent ce qui nous intéresse c'est le flag FILTER_SANITIZE_STRING qui va supprimer les balises et supprimer ou encoder certains caractères spéciaux.

Au final notre formulaire protégé ressemblera à ça:

<!DOCTYPE html>
<html>
  <body>
    <form action="#" method="get">
      <label>Que recherchez vous ?</label>
      <input type="text" name="recherche"/>
      <input type="submit" name="Rechercher"/>
    </form>
    <?php
	  echo filter_var(htmlentities($_GET["recherche"], ENT_QUOTES), FILTER_SANITIZE_STRING);
    ?>
  </body>
</html>

On pourrait aller plus loin dans le filtrage est appliqué un filtre alpha numérique (via une regex) qui supprimerait tous les caractères qui ne sont ni une lettre ni un chiffre.

Attention, sur plusieurs forums il est écrit qu'avant d'afficher un input utilisateur il faut le urldécoder. C'est absolument faux en plus d'être très dangereux. Prenons le formulaire suivant:

<!DOCTYPE html>
<html>
  <body>
    <form action="#" method="get">
      <label>Que recherchez vous ?</label>
      <input type="text" name="recherche"/>
      <input type="submit" name="Rechercher"/>
    </form>
    <?php
      $input = htmlentities($_GET["query"]);
	  echo urldecode($input);
    ?>
  </body>
</html>

Ainsi que les trois payload suivant:

<script>alert(1)</script>
%3Cscript%3Ealert(1)%3C%2Fscript%3E
%253Cscript%253Ealert(1)%253C%252Fscript%253E

Les deux premiers payloads seront désamorcés. En effet la fonction htmlentities va détecter la présence des caractères < > et / et les remplacer par leurs équivalents entités. De même pour le second payload puisque ce dernier sera automatiquement décodé. En revanche le troisième passera à travers la protection. En effet, le payload sera une première fois décodé ce qui le transformera en payload 2. La fonction htmlentities ne détectera pas de caractères spéciaux donc ne traitera pas ce payload qui sera ensuite redécodé par la fonction urldecode ce qui nous donnera le payload 1 qui lui sera bien exécuté.

II/ Protection via un WAF

De ce côté là plusieurs solutions existent. La plus simple consiste à mettre en place le mod_security disponible sous Apache2. En effet le Mod_Security est un module apache2/nginx édité par le projet OWASP qui

De même il est possible de configurer les headers de sécurité CSP et X-XSS-Protection.