Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Par Rick Anderson
Cross-Site Scripting (XSS) est une vulnérabilité de sécurité qui permet à un cyberattaquant de placer des scripts côté client (généralement JavaScript) dans des pages Web. Lorsque d’autres utilisateurs chargent des pages affectées, les scripts de cyber-attaque s’exécutent. La cyber-attaque peut ensuite voler des cookies et des jetons de session, modifier le contenu de la page web via la manipulation DOM ou rediriger le navigateur vers une autre page. Les vulnérabilités XSS se produisent généralement lorsqu’une application prend une entrée utilisateur et l’affiche sur une page sans la valider, l’encoder ni l’échapper.
Cet article s’applique principalement à ASP.NET Core MVC avec des vues, Razor Pages et d’autres applications qui retournent du code HTML qui peut être vulnérable à XSS. Les API web qui retournent des données sous la forme de code HTML, XML ou JSON peuvent déclencher des attaques XSS dans leurs applications clientes s’ils ne nettoient pas correctement l’entrée utilisateur. Ce comportement dépend de la confiance que l’application cliente place dans l’API. Si une API accepte le contenu généré par l’utilisateur et le retourne dans une réponse HTML, les données sont ouvertes aux attaques. Une cyber-attaque peut injecter des scripts malveillants dans le contenu qui s’exécute lorsque la réponse est affichée dans le navigateur de l’utilisateur.
Pour empêcher les attaques XSS, les API Web doivent implémenter la validation des entrées et le codage des sorties. La validation des entrées garantit que l'entrée de l'utilisateur répond aux critères attendus et n'inclut pas de code malveillant. L’encodage de sortie garantit que toutes les données retournées par l’API sont correctement nettoyées afin qu’elles ne puissent pas être exécutées en tant que code par le navigateur de l’utilisateur. Pour plus d’informations, consultez GitHub dotnet/aspnetcore.docs issue #28789.
Protéger votre application contre XSS
À la base, XSS fonctionne en incitant votre application à insérer une balise <script> dans votre page rendue ou en insérant un événement On* dans un élément.
Pour éviter d’introduire XSS dans l’application, les développeurs doivent implémenter les techniques de prévention suivantes :
Ne placez jamais de données non approuvées dans votre entrée HTML, sauf si vous suivez les autres techniques répertoriées dans cette section.
Les données non fiables sont toutes les données qu’un cyberattaquant peut contrôler. Les exemples incluent des entrées de formulaire HTML, des chaînes de requête, des en-têtes HTTP ou même des données sources à partir d’une base de données. Une cyber-attaque peut être en mesure de violer votre base de données même si elle ne peut pas violer votre application.
Avant de placer des données non approuvées dans un élément HTML, vérifiez que les données sont encodées au format HTML.
L’encodage HTML prend des caractères tels que le crochet à angle gauche ou inférieur à (
<) et les modifie en une forme sécurisée comme (<).Avant de placer des données non approuvées dans un attribut HTML, vérifiez que les données sont encodées par l’attribut HTML.
Cette forme spécialisée d’encodage HTML gère les guillemets doubles (
"), les guillemets simples ('), les ampersand (&) et les caractères inférieurs à (<). Lorsque vous traitez d’une entrée non approuvée, utilisez l’encodage HTML pour le contenu HTML général et l’encodage d’attribut HTML pour les attributs HTML.Avant de placer des données non approuvées dans JavaScript, placez les données dans un élément HTML dont vous récupérez le contenu lors de l’exécution.
Si vous ne pouvez pas suivre cette technique, vérifiez que les données sont encodées en JavaScript. L’encodage JavaScript convertit les caractères dangereux pour JavaScript en valeur équivalente hexadécimale. Par exemple, l’encodage JavaScript transforme le caractère « inférieur à » (
<) en la valeur hexadécimale\u003C.Avant de placer des données non approuvées dans une chaîne de requête d’URL, vérifiez que les données sont encodées par l’URL.
Explorer l’encodage HTML avec Razor
Le Razor moteur utilisé dans MVC encode automatiquement toutes les sorties provenant de variables, sauf si vous travaillez pour empêcher ce comportement. Il utilise les règles d’encodage des attributs HTML chaque fois que vous utilisez la directive « @ ». Étant donné que l’encodage d’attribut HTML est un super-ensemble d’encodage HTML, vous n’avez pas à déterminer s’il faut utiliser l’encodage HTML ou l’encodage d’attribut HTML. Vous devez vous assurer que vous utilisez uniquement le symbole @ dans un contexte HTML, et non lors de la tentative d’insertion d’entrée non approuvée directement dans JavaScript.
Razor Les Tag Helpers encodent également les entrées que vous utilisez dans les paramètres de balise.
Tenez compte de la vue suivante Razor :
@{
var untrustedInput = "<\"123\">";
}
@untrustedInput
Cette vue génère le contenu de la untrustedInput variable. La variable inclut certains caractères utilisés dans les attaques XSS : inférieur à (<), guillemet double (") et crochet à angle droit ou supérieur à (>). L'examen de la source montre la sortie rendue encodée comme :
<"123">
Avertissement
ASP.NET Core MVC fournit une classe HtmlString qui n'est pas automatiquement encodée lors de la sortie. Cette classe ne doit jamais être utilisée en combinaison avec une entrée non approuvée, car elle expose une vulnérabilité XSS.
Explorer l’encodage JavaScript avec Razor
Dans certains cas, vous souhaiterez peut-être insérer une valeur dans du code JavaScript afin de la traiter dans votre vue. Il existe deux façons d’accomplir cette tâche. Le moyen le plus sûr d'insérer des valeurs consiste à placer la valeur dans un attribut de données d'une balise et à la récupérer dans votre JavaScript. Par exemple :
@{
var untrustedInput = "<script>alert(1)</script>";
}
<div id="injectedData"
data-untrustedinput="@untrustedInput" />
<div id="scriptedWrite" />
<div id="scriptedWrite-html5" />
<script>
var injectedData = document.getElementById("injectedData");
// All clients
var clientSideUntrustedInputOldStyle =
injectedData.getAttribute("data-untrustedinput");
// HTML 5 clients only
var clientSideUntrustedInputHtml5 =
injectedData.dataset.untrustedinput;
// Put the injected, untrusted data into the scriptedWrite div tag.
// Do NOT use document.write() on dynamically generated data as it can lead to XSS.
document.getElementById("scriptedWrite").innerText += clientSideUntrustedInputOldStyle;
// Or, you can use createElement() to dynamically create document elements.
// This instance uses textContent to ensure the data is properly encoded.
var x = document.createElement("div");
x.textContent = clientSideUntrustedInputHtml5;
document.body.appendChild(x);
// You can also use createTextNode on an element to ensure data is properly encoded.
var y = document.createElement("div");
y.appendChild(document.createTextNode(clientSideUntrustedInputHtml5));
document.body.appendChild(y);
</script>
Le balisage précédent génère le code HTML suivant :
<div id="injectedData"
data-untrustedinput="<script>alert(1)</script>" />
<div id="scriptedWrite" />
<div id="scriptedWrite-html5" />
<script>
var injectedData = document.getElementById("injectedData");
// All clients
var clientSideUntrustedInputOldStyle =
injectedData.getAttribute("data-untrustedinput");
// HTML 5 clients only
var clientSideUntrustedInputHtml5 =
injectedData.dataset.untrustedinput;
// Put the injected, untrusted data into the scriptedWrite div tag.
// Do NOT use document.write() on dynamically generated data as it can lead to XSS.
document.getElementById("scriptedWrite").innerText += clientSideUntrustedInputOldStyle;
// Or, you can use createElement() to dynamically create document elements.
// This instance uses textContent to ensure the data is properly encoded.
var x = document.createElement("div");
x.textContent = clientSideUntrustedInputHtml5;
document.body.appendChild(x);
// You can also use createTextNode on an element to ensure data is properly encoded.
var y = document.createElement("div");
y.appendChild(document.createTextNode(clientSideUntrustedInputHtml5));
document.body.appendChild(y);
</script>
Le code précédent génère la sortie suivante :
<script>alert(1)</script>
<script>alert(1)</script>
<script>alert(1)</script>
Avertissement
Ne concaténer pas d’entrée non approuvée dans JavaScript pour créer des éléments DOM ou utiliser document.write() sur du contenu généré dynamiquement.
Utilisez plutôt l’une des approches suivantes pour empêcher l’exposition du code à des XSS basés sur DOM :
- Appelez et affectez
createElement()des valeurs de propriété avec des méthodes ou des propriétés appropriées, telles quenode.textContent=ounode.InnerText=. - Appelez la méthode
document.CreateTextNode()et ajoutez-la à l’emplacement approprié dans le DOM. - Appelez la méthode
element.SetAttribute(). - Utilisez l’assignation
element[attribute]=.
Accéder aux encodeurs dans le code
Vous pouvez utiliser des encodeurs HTML, JavaScript et URL dans votre code de deux façons :
- Injectez-les via l'injection de dépendances.
- Utilisez les encodeurs par défaut contenus dans l'espace de noms
System.Text.Encodings.Web.
Lorsque vous utilisez les encodeurs par défaut, toutes les personnalisations appliquées aux plages de caractères (de sorte qu’elles sont traitées comme sécurisées) ne prennent pas effet. Les encodeurs par défaut utilisent les règles d'encodage les plus sûres possibles.
Pour utiliser les encodeurs configurables par injection de dépendances, vos constructeurs doivent accepter un paramètre HtmlEncoder, JavaScriptEncoder et UrlEncoder, selon le cas.
Par exemple :
public class HomeController : Controller
{
HtmlEncoder _htmlEncoder;
JavaScriptEncoder _javaScriptEncoder;
UrlEncoder _urlEncoder;
public HomeController(HtmlEncoder htmlEncoder,
JavaScriptEncoder javascriptEncoder,
UrlEncoder urlEncoder)
{
_htmlEncoder = htmlEncoder;
_javaScriptEncoder = javascriptEncoder;
_urlEncoder = urlEncoder;
}
}
Encoder les paramètres d’URL
Si vous souhaitez générer une chaîne de requête d’URL avec une entrée non approuvée comme valeur, utilisez le UrlEncoder paramètre pour encoder la valeur :
var example = "\"Quoted Value with spaces and &\"";
var encodedValue = _urlEncoder.Encode(example);
Après l’encodage, la encodedValue variable contient la chaîne %22Quoted%20Value%20with%20spaces%20and%20%26%22. Les espaces, guillemets, ponctuation et autres caractères non sécurisés sont encodés en pourcentage à leur valeur hexadécimale. Par exemple, un caractère d’espace est converti en %20.
Avertissement
N'utilisez pas d'entrée non fiable dans le cadre d'un chemin d'URL. Transmettez toujours une entrée non fiable en tant que valeur de chaîne de requête.
Personnaliser les encodeurs
Par défaut, les encodeurs utilisent une liste sécurisée limitée à la plage Unicode latine de base. Tous les caractères en dehors de la plage indiquée sont encodés en tant qu’équivalents de code de caractères. Ce comportement affecte également le rendu par Razor Tag Helpers et les Helpers HTML, car ils utilisent les encodeurs pour générer vos chaînes.
L’objectif de ce comportement est de se protéger contre les bogues inconnus ou futurs du navigateur. D’anciens bogues du navigateur ont perturbé l’analyse syntaxique liée au traitement de caractères non anglais. Si votre site web utilise beaucoup de caractères non latins, tels que chinois, cyrillique ou autres, ce comportement n’est probablement pas adapté à votre configuration.
Vous pouvez personnaliser les listes sécurisées de l’encodeur pour inclure des plages Unicode appropriées à l’application au démarrage. Effectuez les personnalisations dans le fichier Program.cs .
Par exemple, vous pouvez utiliser la configuration par défaut avec un Razor helper HTML similaire au code HTML suivant :
<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>
Le balisage précédent est rendu avec du texte chinois encodé :
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
Pour élargir la plage de caractères traitée comme sécurisée par l’encodeur, insérez la ligne suivante dans le fichier Program.cs :
builder.Services.AddSingleton<HtmlEncoder>(
HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
UnicodeRanges.CjkUnifiedIdeographs }));
Vous pouvez personnaliser les listes sûres de l'encodeur pour inclure des plages Unicode appropriées à votre application lors du démarrage, dans ConfigureServices().
Par exemple, en utilisant la configuration par défaut, vous pouvez utiliser un HtmlHelper Razor comme ceci ;
<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>
Lorsque vous affichez la source de la page Web, vous verrez qu'elle a été rendue comme suit, avec le texte chinois encodé ;
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
Pour élargir les caractères traités comme sûrs par l'encodeur, vous insérez la ligne suivante dans la méthode ConfigureServices() dans startup.cs ;
services.AddSingleton<HtmlEncoder>(
HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
UnicodeRanges.CjkUnifiedIdeographs }));
Cet exemple permet d’élargir la liste sécurisée pour inclure les ideographes unifiés de plage Unicode CJK. La sortie suivante montre l’affichage rendu pour la plage plus large de caractères sécurisés :
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
Les plages de listes sûres sont spécifiées sous forme de tableaux de code Unicode, et non de langues. La norme Unicode comporte une liste de graphiques de code que vous pouvez utiliser pour rechercher le graphique qui contient vos caractères. Chaque encodeur (HTML, JavaScript, URL) doit être configuré séparément.
Note
La personnalisation de la liste sécurisée affecte uniquement les encodeurs sources via l’injection de dépendances.
Si vous accédez directement à un encodeur via System.Text.Encodings.Web.*Encoder.Default, seule la liste sécurisée par défaut est utilisée, latin de base.
Déterminer quand et où encoder
En général, la pratique acceptée est que l’encodage a lieu au point de sortie et les valeurs encodées ne doivent jamais être stockés dans une base de données.
L’encodage au point de sortie vous permet de modifier l’utilisation des données. Par exemple, passez du code HTML à une valeur de chaîne de requête. Cette approche vous permet de rechercher facilement vos données sans avoir à encoder des valeurs avant la recherche. Il vous permet également de tirer parti des modifications ou correctifs de bogues apportés aux encodeurs.
Utiliser la validation comme technique de prévention XSS
La validation peut être un outil utile pour limiter les attaques XSS. Par exemple, une chaîne numérique contenant uniquement les caractères 0-9 ne déclenche pas d’attaque XSS.
La validation est plus compliquée lorsque le code HTML est accepté dans l’entrée utilisateur. L’analyse de l’entrée HTML peut être difficile et parfois impossible. Markdown, associé à un analyseur qui supprime le HTML intégré, est une option plus sûre pour accepter une entrée riche.
Ne comptez jamais uniquement sur la validation. Encoder toujours une entrée non approuvée avant la sortie, quelle que soit la validation ou l’assainissement effectué.