Nous avons déjà parlé dans le chapitre sur les chaînes de caractères de la protection des chaînes ("Protéger les chaînes") quand elles doivent être affichées par le navigateur.
Quand nous affichons des chaînes littérales que nous avons nous-même inclus dans notre code (message d'erreur, label de champ de formulaire, texte explicatif, etc), nous somme sûrs qu'elles ne contiennent pas de code HTML ou JavaScript que nous ne voudrions pas voir exécuter par le navigateur quand il va faire l'affichage de ces chaînes.
Il n'en va pas de même quand nous affichons des chaînes dont nous ne maîtrisons pas totalement la provenance ou la composition. C'est particulièrement vrai pour ce qui est stocké dans une base de données, d'autant plus que la philosophioe du "Web 2.0" est une participation accrue des utilisateurs qui sont sollicités pour laisser des messages, des avis, des commentaires, etc.
Rien n'empêche ces utilisateurs si ils ont des connaissances en HTML ou JavaScript d'inclure des tags ou des instructions dans les textes qu'ils laissent sur telle ou telle site. Si rien n'est fait pour surveiller et inhiber ces éléments de langage, ils seront tout simplement "exécutés" par le navigateur, ce qui peut conduire à des failles de sécurité dans notre site, ou à ce que d'autres utilisateurs soient piégés.
L'exemple suivant illustre du code malicieux saisi dans du texte et stocké dans la base de données.
Le responsable de cette "redirection" vers un site pirate imitant la
page de login de Moodle est simplement le morceau de code suivant
glissé dans le résumé du livre :
<script>location
= "../exemple/login.html"</script>
Il ne faut pas croire que seul JavaScript est capable de faire des dégâts. Un simple lien HTML (<a>) peut se révéler aussi dangereux car le click peut nous emmener autre part que ce qui est indiqué. Un tag <img> peut aussi pointer non sur une image, mais sur un script PHP qui pourra par exemple obtenir des informations sur les cookies de session avant de renvoyer (ou pas) une image.
Pour éviter que du code soit interprété par le navigateur sans que nous le voulions, il faut protéger toutes les chaînes de caractères qui viennent de la base de données avant d'en faire l'affichage.
Nous pouvons utiliser 3 fonctions :
On voit que maintenant le code HTML et JavaScript inclus dans le texte du résumé n'est plus exécuté, mais affiché tel qu'il a été saisi puisque htmlentities() a transformé les < et > syntaxiques du langage HTML en ... entités HTML, c'est à dire en simples < et >. Ainsi, le navigateur ne reçoit plus la chaîne '<script>' (qui signifie pour lui balise ouvrante d'un élément SCRIPT) mais la chaîne '<script>' et il affiche simplement cette chaîne en remplaçant les entités HTML par les caractères correspondant (l'entité < est par exemple remplacé par un simple caractère "inférieur".
Je vous invite à regarder le code source de la page obtenue lors de l'exécution du script précédent, vous y retouverez notamment la chaîne '<script>'.
strip_tags() a retiré les tags contenus dans le résumé du livre, ne laissant plus apparaître que l'instruction JavaScript qui est devenue inopérante puisqu'elle n'est plus entourées de tags indiquant au navigateur de l'exécuter.
On privilégiera htmlentities() pour protéger les chaînes avant affichage car cette fonction fait aussi le remplacement des caractères accentués.
Le recours à une fonction pour faire le travail à notre place est intéressant car utiliser htmlentities() pour toutes les sorties peut s'avérer pénible du fait de la répétition du code et des oublis qui peuvent arriver.
Une telle fonction est facile à écrire et à placer dans notre bibliothéque bib_fonctions.php :
/**
* Protection HTML des chaînes contenues dans un tableau
*
* @param array $tab Tableau des chaînes à protéger
*/
function htmlProteger(&$tab) {
foreach ($tab as $cle => &$val) {
$val = htmlentities($val, ENT_COMPAT, 'ISO-8859-1');
}
}
Remarques :
function htmlProteger(&$tab) {
foreach ($tab as $cle => $val) {
$tab[$cle] = htmlentities($val, ENT_COMPAT, 'ISO-8859-1');
}
}
Mais, avec cette deuxième solution, la boucle foreach réalise une copie du tableau
(relisez le chapitre consacré aux 'Boucles avec foreach' si nécessaire). Donc la première solution permet
d'économiser de la mémoire.