Comment empêcher XSS avec HTML / PHP?

256

Comment empêcher XSS (cross-site scripting) d'utiliser uniquement HTML et PHP?

J'ai vu de nombreux autres articles sur ce sujet, mais je n'ai pas trouvé d'article qui indique clairement et de manière concise comment réellement empêcher XSS.

Tim tim
la source
3
Juste une note que cela ne résoudra pas le cas où vous voudrez peut-être utiliser la saisie utilisateur comme attribut HTML. Par exemple, l'URL source d'une image. Pas un cas courant, mais facile à oublier.
Michael Mior
@MichaelMior voici une solution pour empêcher l' attribut XSS dans hrefou srcHTML: stackoverflow.com/questions/19047119/…
baptx
Il y a un bel article ici qui explique XSS et comment l'empêcher dans différentes langues (y compris PHP).
XCore

Réponses:

296

Fondamentalement, vous devez utiliser la fonction htmlspecialchars()chaque fois que vous souhaitez sortir quelque chose dans le navigateur provenant de l'entrée utilisateur.

La bonne façon d'utiliser cette fonction est quelque chose comme ceci:

echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');

Google Code University propose également ces vidéos très pédagogiques sur la sécurité Web:

Alix Axel
la source
7
@TimTim: Pour la plupart des cas, oui. Cependant, lorsque vous devez autoriser l'entrée HTML, les choses deviennent un peu plus compliquées et si tel est le cas, je vous recommande d'utiliser quelque chose comme htmlpurifier.org
Alix Axel
@Alix Axel, votre réponse est-elle donc d'utiliser htmlspecialchars ou d'utiliser htmlpurifier.org ?
TimTim
3
Si vous devez accepter une entrée HTML, utilisez HTML Purifier, sinon utilisez-le htmlspecialchars().
Alix Axel du
9
htmlspecialchars ou htmlentities? Vérifiez ici stackoverflow.com/questions/46483/…
kiranvj
4
La plupart du temps, c'est correct, mais ce n'est pas aussi simple que cela. Vous devriez envisager de mettre une chaîne non approuvée en HTML, Js, Css et envisager de mettre du HTML non approuvé en HTML. Regardez ceci: owasp.org/index.php/…
homme de bronze
41

L'une de mes références OWASP préférées est l' explication Cross-Site Scripting car bien qu'il existe un grand nombre de vecteurs d'attaque XSS, les quelques règles suivantes peuvent se défendre contre la majorité d'entre elles considérablement!

Ceci est une feuille de triche de sécurité PHP

Wahyu Kristianto
la source
7
Moi aussi .. Ceci est une feuille de triche XSS Filter Evasion owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
1
Pas exactement XSS, mais je pense que XSS et CSRF sont généralement mélangés et les deux sont vraiment dangereux: owasp.org/index.php/…
Simon
2
Cette page n'existe plus
Mazzy
1
@Mazzy dernier cache web.archive.org/web/20180817180409/owasp.org/index.php/…
Wahyu Kristianto
15

L'une des étapes les plus importantes consiste à nettoyer toute entrée utilisateur avant qu'elle ne soit traitée et / ou restituée au navigateur. PHP a quelques fonctions de " filtre " qui peuvent être utilisées.

La forme que les attaques XSS ont généralement consiste à insérer un lien vers du javascript hors site qui contient une intention malveillante pour l'utilisateur. En savoir plus ici .

Vous voudrez également tester votre site - je peux recommander le module complémentaire Firefox XSS Me .

James Kolpack
la source
De quoi ai-je besoin pour m'assurer de désinfecter exactement l'entrée. Y a-t-il un caractère / chaîne particulier à surveiller?
TimTim
27
@TimTim - non. Toutes les entrées des utilisateurs doivent toujours être considérées comme intrinsèquement hostiles.
zombat
En outre, les données internes (employés, administrateur système, etc.) pourraient être dangereuses. Vous devez identifier et surveiller (avec la date d'enregistrement et l'utilisateur) les données affichées avec interprétation.
Samuel Dauzon
9

Par ordre de préférence:

  1. Si vous utilisez un moteur de modèles (par exemple Twig, Smarty, Blade), vérifiez qu'il offre un échappement contextuel. Je sais par expérience que Twig le fait.{{ var|e('html_attr') }}
  2. Si vous souhaitez autoriser HTML, utilisez HTML Purifier . Même si vous pensez que vous n'acceptez que Markdown ou ReStructuredText, vous souhaitez toujours purifier le HTML de sortie de ces langages de balisage.
  3. Sinon, utilisez htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)et assurez-vous que le reste de votre document utilise le même jeu de caractères que $charset. Dans la plupart des cas, 'UTF-8'c'est le jeu de caractères souhaité.

Assurez-vous également de vous échapper sur la sortie, pas sur l'entrée .

Scott Arciszewski
la source
7

Publication croisée en tant que référence consolidée à partir de la version bêta de la documentation SO qui est mise hors ligne.

Problème

Les scripts intersites sont l'exécution involontaire de code à distance par un client Web. Toute application Web peut s'exposer à XSS si elle prend l'entrée d'un utilisateur et la renvoie directement sur une page Web. Si la saisie inclut HTML ou JavaScript, le code à distance peut être exécuté lorsque ce contenu est rendu par le client Web.

Par exemple, si un côté tiers contient un fichier JavaScript:

// http://example.com/runme.js
document.write("I'm running");

Et une application PHP génère directement une chaîne qui lui est passée:

<?php
echo '<div>' . $_GET['input'] . '</div>';

Si un paramètre GET non contrôlé contient <script src="http://example.com/runme.js"></script>alors la sortie du script PHP sera:

<div><script src="http://example.com/runme.js"></script></div>

Le JavaScript tiers s'exécutera et l'utilisateur verra "Je cours" sur la page Web.

Solution

En règle générale, ne faites jamais confiance aux entrées provenant d'un client. Chaque valeur GET, POST et cookie peut être n'importe quoi et doit donc être validée. Lors de la sortie de l'une de ces valeurs, échappez-les afin qu'elles ne soient pas évaluées de manière inattendue.

Gardez à l'esprit que même dans les applications les plus simples, les données peuvent être déplacées et il sera difficile de garder une trace de toutes les sources. Par conséquent, il est recommandé de toujours échapper la sortie.

PHP fournit plusieurs façons d'échapper à la sortie en fonction du contexte.

Fonctions de filtrage

Phps Fonctions Filter permettent les données d'entrée du script php pour être désinfecté ou validé à bien des égards . Ils sont utiles lors de l'enregistrement ou de la sortie des entrées client.

Encodage HTML

htmlspecialcharsconvertira tous les "caractères spéciaux HTML" en leurs encodages HTML, ce qui signifie qu'ils ne seront alors pas traités comme du HTML standard. Pour corriger notre exemple précédent en utilisant cette méthode:

<?php
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// or
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';

Produirait:

<div>&lt;script src=&quot;http://example.com/runme.js&quot;&gt;&lt;/script&gt;</div>

Tout ce qui se trouve à l'intérieur de la <div>balise ne sera pas interprété comme une balise JavaScript par le navigateur, mais plutôt comme un simple nœud de texte. L'utilisateur verra en toute sécurité:

<script src="http://example.com/runme.js"></script>

Encodage d'URL

Lors de la sortie d'une URL générée dynamiquement, PHP fournit la urlencodefonction pour sortir en toute sécurité des URL valides. Ainsi, par exemple, si un utilisateur est capable de saisir des données qui font partie d'un autre paramètre GET:

<?php
$input = urlencode($_GET['input']);
// or
$input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
echo '<a href="http://example.com/page?input="' . $input . '">Link</a>';

Toute entrée malveillante sera convertie en un paramètre d'URL codé.

Utilisation de bibliothèques externes spécialisées ou de listes OWASP AntiSamy

Parfois, vous souhaiterez envoyer du HTML ou d'autres types d'entrées de code. Vous devrez maintenir une liste de mots autorisés (liste blanche) et non autorisés (liste noire).

Vous pouvez télécharger les listes standard disponibles sur le site Web OWASP AntiSamy . Chaque liste est adaptée à un type spécifique d'interaction (ebay api, tinyMCE, etc ...). Et c'est open source.

Il existe des bibliothèques pour filtrer le HTML et empêcher les attaques XSS pour le cas général et effectuer au moins ainsi que les listes AntiSamy avec une utilisation très facile. Par exemple, vous avez HTML Purifier

Matt S
la source
5

De nombreux frameworks aident à gérer XSS de différentes manières. Lorsque vous lancez le vôtre ou en cas de problème XSS, nous pouvons tirer parti de filter_input_array (disponible en PHP 5> = 5.2.0, PHP 7.) J'ajoute généralement cet extrait à mon SessionController, car tous les appels passent par là avant tout autre contrôleur interagit avec les données. De cette manière, toutes les entrées utilisateur sont nettoyées dans 1 emplacement central. Si cela est fait au début d'un projet ou avant que votre base de données ne soit empoisonnée, vous ne devriez pas avoir de problèmes au moment de la sortie ... arrête les ordures, les ordures.

/* Prevent XSS input */
$_GET   = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST  = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
/* I prefer not to use $_REQUEST...but for those who do: */
$_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;

Ce qui précède supprimera TOUTES les balises HTML et de script. Si vous avez besoin d'une solution qui autorise les balises sûres, basée sur une liste blanche, consultez HTML Purifier .


Si votre base de données est déjà empoisonnée ou si vous souhaitez gérer XSS au moment de la sortie, OWASP recommande de créer une fonction d'encapsuleur personnalisée pour echo, et de l'utiliser PARTOUT où vous produisez des valeurs fournies par l'utilisateur:

//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
   return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
}
function xecho($data)
{
   echo xssafe($data);
}
webaholik
la source
2

Vous pouvez également définir certains en-têtes de réponse HTTP liés à XSS via header(...)

X-XSS-Protection "1; mode = block"

pour être sûr, le mode de protection XSS du navigateur est activé.

Content-Security-Policy "default-src 'self'; ..."

pour activer la sécurité du contenu côté navigateur. Voir celui-ci pour plus de détails sur la politique de sécurité du contenu (CSP): http://content-security-policy.com/ La configuration de CSP pour bloquer les scripts en ligne et les sources de script externes est particulièrement utile contre XSS.

pour un ensemble général d'en-têtes de réponse HTTP utiles concernant la sécurité de votre webapp, consultez OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers

chris
la source
1
<?php
function xss_clean($data)
{
// Fix &entity\n;
$data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');

// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);

// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);

// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);

// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);

do
{
    // Remove really unwanted tags
    $old_data = $data;
    $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);

// we are done...
return $data;
}
Abdo-Host
la source
5
Vous ne devez pas utiliser preg_replacecomme il utilise evalsur votre entrée. owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Injection
CrabLab
0

Utiliser htmlspecialcharssur PHP. Sur HTML, essayez d'éviter d'utiliser:

element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);

varest contrôlé par l'utilisateur .

Évitez également évidemment d'éviter eval(var), si vous devez utiliser l'un d'entre eux, essayez de les échapper JS , HTML les échapper et vous devrez peut-être en faire plus, mais pour les bases, cela devrait suffire.

Pablo
la source
0

La meilleure façon de protéger votre entrée est sa htmlentitiesfonction d' utilisation . Exemple:

htmlentities($target, ENT_QUOTES, 'UTF-8');

Vous pouvez obtenir plus d'informations ici .

Marco Concas
la source