Ne faites jamais confiance aux données reçues !
Ne faites jamais confiance aux données reçues !
Il est impossible de terminer ce chapitre sans que je vous mette en garde contre les dangers qui guettent les apprentis webmasters que vous êtes. Nous allons en effet découvrir qu'il ne faut JAMAIS faire confiance aux variables qui transitent de page en page, comme $_GET que nous étudions ici.
Le but de cette section est, je l'avoue, de vous faire peur, car trop peu de développeurs PHP ont conscience des dangers et des failles de sécurité qu'ils peuvent rencontrer, ce qui risque pourtant de mettre en péril leur site web ! Oui je suis alarmiste, oui je veux que vous ayez — un peu — peur ! Mais rassurez-vous : je vais vous expliquer tout ce qu'il faut savoir pour que ça se passe bien et que vous ne risquiez rien. ;-)
Ce qui compte, c'est que vous ayez conscience très tôt (dès maintenant) des problèmes que vous pourrez rencontrer lorsque vous recevrez des paramètres de l'utilisateur.
Tous les visiteurs peuvent trafiquer les URL
Si vous faites les tests des codes précédents chez vous, vous devriez tomber sur une URL de la forme :
http://localhost/tests/bonjour.php?nom=Dupont&prenom=Jean
On vous dit bien « Bonjour Jean Dupont ! ». Mais si vous êtes un peu bricoleurs, vous pouvez vous amuser à changer les paramètres directement dans la barre d'adresse, comme dans la figure suivante.
On peut facilement modifier les paramètres dans le navigateur
Essayez par exemple de modifier l'adresse pour :
http://localhost/tests/bonjour.php?nom=Dupont&prenom=Marc
Comme vous le voyez, ça marche ! N'importe qui peut facilement modifier les URL et y mettre ce qu'il veut : il suffit simplement de changer l'URL dans la barre d'adresse de votre navigateur.
Et alors, quel est le problème ? C'est pas plus mal, si le visiteur veut adapter l'URL pour qu'on lui dise bonjour avec son vrai nom et son vrai prénom ?
Peut-être, mais cela montre une chose : on ne peut pas avoir confiance dans les données qu'on reçoit. N'importe quel visiteur peut les changer. Dans la page index.phpj'ai fait un lien qui dit bonjour à Jean Dupont, mais la page bonjour.php ne va pas forcément afficher « Bonjour Jean Dupont ! » puisque n'importe quel visiteur peut s'amuser à modifier l'URL.
Je vous propose de faire des modifications encore plus vicieuses et de voir ce qu'on peut faire pour éviter les problèmes qui en découlent.
Tester la présence d'un paramètre
Allons plus loin. Qu'est-ce qui empêche le visiteur de supprimer tous les paramètres de l'URL ? Par exemple, il peut très bien tenter d'accéder à :
http://localhost/tests/bonjour.php
Que va afficher la page bonjour.php ? Faites le test ! Elle va afficher quelque chose comme :
Code : Console
Bonjour Notice: Undefined index: prenom in C:\wamp\www\tests\bonjour.php on line 9 Notice: Undefined index: nom in C:\wamp\www\tests\bonjour.php on line 9 ! |
Que s'est-il passé ? On a essayé d'afficher la valeur de $_GET['prenom'] et celle de $_GET['nom']… Mais comme on vient de les supprimer de l'URL, ces variables n'ont pas été créées et donc elles n'existent pas ! PHP nous avertit qu'on essaie d'utiliser des variables qui n'existent pas, d'où les « Undefined index ».
Pour résoudre ce problème, on peut faire appel à une fonction un peu spéciale : isset(). Cette fonction teste si une variable existe. Nous allons nous en servir pour afficher un message spécifique si le nom ou le prénom sont absents.
Code : PHP
1 2 3 4 5 6 7 8 9 10 |
<?php if (isset($_GET['prenom']) AND isset($_GET['nom'])) // On a le nom et le prénom { echo 'Bonjour ' . $_GET['prenom'] . ' ' . $_GET['nom'] . ' !'; } else // Il manque des paramètres, on avertit le visiteur { echo 'Il faut renseigner un nom et un prénom !'; } ?> |
Que fait ce code ? Il teste si les variables $_GET['prenom'] et $_GET['nom'] existent. Si elles existent, on dit bonjour au visiteur. S'il nous manque une des variables (ou les deux), on affiche un message d'erreur : « Il faut renseigner un nom et un prénom ! ».
Essayez maintenant d'accéder à la page bonjour.php sans les paramètres, vous allez voir qu'on gère bien le cas où le visiteur aurait retiré les paramètres de l'URL.
Est-ce que c'est vraiment la peine de gérer ce cas ? C'est vrai quoi, moi je ne m'amuse jamais à modifier mes URL et mes visiteurs non plus, je pense !
Oui, oui, trois fois oui : il faut que vous pensiez à gérer le cas où des paramètres que vous attendiez viendraient à manquer.
Vous vous souvenez de ce que je vous ai dit ? Il ne faut jamais faire confiance à l'utilisateur. Tôt ou tard vous tomberez sur un utilisateur malintentionné qui essaiera de trafiquer l'URL pour mettre n'importe quoi dans les paramètres. Il faut que votre site soit prêt à gérer le cas.
Dans notre exemple, si on ne gérait pas le cas, ça ne faisait rien de bien grave (ça affichait juste des messages d'erreur). Mais lorsque votre site web deviendra plus complexe, cela pourrait avoir des conséquences plus ennuyeuses.
Un exemple ? Sur le Site du Zéro lui-même, nous avions une fois oublié de gérer le cas où un paramètre viendrait à manquer. Un utilisateur avait essayé de l'enlever « pour voir » : notre page PHP était faite de telle sorte que si le paramètre manquait, ça supprimait toutes les préférences de tous les membres inscrits du site !
Vous n'en êtes pas encore là, mais je tiens à vous sensibiliser aux problèmes potentiels que cela peut provoquer. Soyez donc vigilants dès maintenant et ne supposez jamais que vous allez recevoir tous les paramètres que vous attendiez.
Contrôler la valeur des paramètres
Allons plus loin dans notre tripatouillage vicieux de l'URL. On va modifier notre code source pour gérer un nouveau paramètre : le nombre de fois que le message doit être répété. Les paramètres vont donc être :
bonjour.php?nom=Dupont&prenom=Jean&repeter=8
Le paramètre repeter définit le nombre de fois que l'on dit « bonjour » sur la page. Sauriez-vous adapter notre code pour qu'il dise bonjour le nombre de fois indiqué ? Voici la solution que je vous propose :
Code : PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php if (isset($_GET['prenom']) AND isset($_GET['nom']) AND isset($_GET['repeter'])) { for ($i = 0 ; $i < $_GET['repeter'] ; $i++) { echo 'Bonjour ' . $_GET['prenom'] . ' ' . $_GET['nom'] . ' !<br />'; } } else { echo 'Il faut renseigner un nom, un prénom et un nombre de répétitions !'; } ?> |
On teste si le paramètre repeter existe lui aussi (avec isset($_GET['repeter'])). Si tous les paramètres sont bien là, on fait une boucle (j'ai choisi de faire un for, mais on aurait aussi pu faire un while). La boucle incrémente une petite variable $i pour répéter le message de bienvenue le nombre de fois indiqué.
Le résultat ? Si on va sur…
http://localhost/tests/bonjour.php?nom=Dupont&prenom=Jean&repeter=8
Si vous n'avez pas créé de répertoire test, il faut bien sûr adapter votre URL.
… alors le message de bienvenue s'affichera huit fois :
Code : Console
Bonjour Jean Dupont ! Bonjour Jean Dupont ! Bonjour Jean Dupont ! Bonjour Jean Dupont ! Bonjour Jean Dupont ! Bonjour Jean Dupont ! Bonjour Jean Dupont ! Bonjour Jean Dupont ! |
Nous avons amélioré notre page pour qu'elle puisse dire « bonjour » plusieurs fois, et ce nombre de fois est personnalisable dans l'URL. Très bien.
Maintenant, mettez-vous dans la peau du méchant. Soyez méchants. Soyez vicieux. Que pourrions-nous faire et que nous n'aurions pas prévu, juste en modifiant l'URL ?
J'ai une idée ! On peut mettre un nombre de répétitions très grand, par exemple 1984986545665156. La page PHP va boucler un très grand nombre de fois et votre PC ne pourra probablement pas supporter une telle charge de travail.
bonjour.php?nom=Dupont&prenom=Jean&repeter=1984986545665156
Si vous avez peur d'essayer, je vous rassure : votre PC ne va pas prendre feu en faisant ça. Par contre, il va se mettre à consommer énormément de mémoire pour stocker tous les messages « bonjour » et n'arrivera sûrement pas jusqu'au bout (PHP s'arrête au bout d'un certain temps d'exécution). Ou alors s'il y arrive, votre page va être extrêmement surchargée de messages « bonjour ».
Mon dieu, c'est horrible ! Comment peut-on empêcher ça ?
En étant plus malins et surtout plus prévoyants. Voici ce qu'il faut anticiper.
- Le cas où le nombre qu'on vous envoie n'est pas une valeur raisonnable.
- Par exemple on peut dire que si on dépasse 100 fois, c'est trop, et il faut refuser d'exécuter la page.
- De même, que se passe-t-il si je demande de répéter « $-4$ fois » ? Ça ne devrait pas être autorisé. Il faut que le nombre soit au moins égal à 1.
- Le cas où la valeur n'est pas logique, où elle n'est pas du tout ce qu'on attendait. Rien n'empêche le visiteur de remplacer la valeur du paramètre repeter par du texte !
- Que se passe-t-il si on essaie d'accéder à
bonjour.php?nom=Dupont&prenom=Jean&repeter=grenouille ?
Cela ne devrait pas être autorisé. Le mot « grenouille » n'a pas de sens, on veut un nombre entier positif. - Que se passe-t-il si on essaie d'accéder à
bonjour.php?nom=Dupont&prenom=Jean&repeter=true ?
Cela ne devrait pas être autorisé non plus, on attendait un nombre entier positif, pas un booléen.
Pour résoudre ces problèmes, on doit :
- vérifier que repeter contient bien un nombre ;
- vérifier ensuite que ce nombre est compris dans un intervalle raisonnable (entre 1 et 100, par exemple).
Pour vérifier que repeter contient bien un nombre, une bonne solution pourrait être de forcer la conversion de la variable en type entier (int). On peut utiliser pour cela ce qu'on appelle le transtypage, une notion qu'on n'a pas encore vue jusqu'ici mais qui est très simple à comprendre. Regardez ce code :
Code : PHP
1 2 3 |
<?php $_GET['repeter'] = (int) $_GET['repeter']; ?> |
Il faut lire cette instruction de droite à gauche. Le (int) entre parenthèses est comme un tuyau de conversion. Tout ce qui transite à travers ressort forcément en une valeur de type int (entier). Voyez la figure suivante pour vous en convaincre.
Transtypage
Si la valeur est bien un entier (par exemple 14) alors elle n'est pas modifiée. En revanche, si la variable contient autre chose qu'un entier (par exemple « grenouille »), elle est transformée en entier. Ici, comme PHP ne peut pas associer de valeur à grenouille, il lui donne 0.
Après avoir exécuté cette instruction, la variable $_GET['repeter'] contient maintenant forcément un nombre entier. Il ne reste plus qu'à vérifier que ce nombre est bien compris entre 1 et 100 à l'aide d'une condition (ce que vous savez faire, normalement !).
Voici donc le code final sécurisé qui prévoit tous les cas pour éviter d'être pris au dépourvu par un utilisateur malintentionné :
Code : PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php if (isset($_GET['prenom']) AND isset($_GET['nom']) AND isset($_GET['repeter'])) { // 1 : On force la conversion en nombre entier $_GET['repeter'] = (int) $_GET['repeter'];
// 2 : Le nombre doit être compris entre 1 et 100 if ($_GET['repeter'] >= 1 AND $_GET['repeter'] <= 100) { for ($i = 0 ; $i < $_GET['repeter'] ; $i++) { echo 'Bonjour ' . $_GET['prenom'] . ' ' . $_GET['nom'] . ' !<br />'; } } } else { echo 'Il faut renseigner un nom, un prénom et un nombre de répétitions !'; } ?> |
Cela fait beaucoup de conditions pour quelque chose qui était à la base assez simple, mais c'était nécessaire. Vous devrez toujours faire très attention et prévoir tous les cas les plus tordus, comme nous venons de le faire :
- vérifier que tous les paramètres que vous attendiez sont bien là ;
- vérifier qu'ils contiennent bien des valeurs correctes comprises dans des intervalles raisonnables.
Si vous gardez cela en tête, vous n'aurez pas de problèmes. :-)
Nous n'avons pas encore vu tous les risques liés aux données envoyées par l'utilisateur. Cette première approche devrait déjà vous avoir sensibilisés au problème, mais nous irons plus loin dans le chapitre suivant pour finir de couvrir ce sujet. Tout ceci est un peu complexe, je suis d'accord, mais c'est réellement important !
Créé avec HelpNDoc Personal Edition: Transformez votre document Word en un eBook professionnel avec HelpNDoc