Partager via


Prévention des attaques par injection de code JavaScript (C#)

par Stephen Walther

Empêchez les attaques par injection de code JavaScript et les attaques par script intersites de vous arriver. Dans ce tutoriel, Stephen Walther explique comment vous pouvez facilement vaincre ces types d’attaques en encodant votre contenu au format HTML.

L’objectif de ce tutoriel est d’expliquer comment vous pouvez empêcher les attaques par injection de Code JavaScript dans vos applications ASP.NET MVC. Ce tutoriel décrit deux approches pour défendre votre site web contre une attaque par injection de Code JavaScript. Vous découvrez comment empêcher les attaques par injection javaScript en encodant les données que vous affichez. Vous apprenez également à empêcher les attaques par injection javascript en encodant les données que vous acceptez.

Qu’est-ce qu’une attaque par injection javascript ?

Chaque fois que vous acceptez l’entrée utilisateur et réaffichez l’entrée utilisateur, vous ouvrez votre site web aux attaques par injection JavaScript. Examinons une application concrète ouverte aux attaques par injection JavaScript.

Imaginez que vous avez créé un site web de commentaires des clients (voir la figure 1). Les clients peuvent visiter le site web et entrer des commentaires sur leur expérience à l’aide de vos produits. Lorsqu’un client envoie ses commentaires, les commentaires sont réaffichés sur la page de commentaires.

Site web des commentaires des clients

Figure 01 : Site web de commentaires des clients (cliquer pour afficher l’image en taille réelle)

Le site web de commentaires des clients utilise le dans listing controller 1. Il controller contient deux actions nommées Index() et Create().

Listing 1 – HomeController.cs

using System;
using System.Web.Mvc;
using CustomerFeedback.Models;

namespace CustomerFeedback.Controllers
{
     [HandleError]
     public class HomeController : Controller
     {
          private FeedbackDataContext db = new FeedbackDataContext();

          public ActionResult Index()
          {
               return View(db.Feedbacks);
          }

          public ActionResult Create(string message)

          {
               // Add feedback
               var newFeedback = new Feedback();
               newFeedback.Message = message;
               newFeedback.EntryDate = DateTime.Now;
               db.Feedbacks.InsertOnSubmit(newFeedback);
               db.SubmitChanges();

               // Redirect
               return RedirectToAction("Index");
          }
     }
}

La Index() méthode affiche la Index vue. Cette méthode transmet tous les commentaires clients précédents à la Index vue en récupérant les commentaires de la base de données (à l’aide d’une requête LINQ to SQL).

La Create() méthode crée un élément Feedback et l’ajoute à la base de données. Le message que le client entre dans le formulaire est passé à la Create() méthode dans le paramètre message. Un élément Feedback est créé et le message est affecté à la propriété de l’élément Message Feedback. L’élément Feedback est envoyé à la base de données avec l’appel de méthode DataContext.SubmitChanges() . Enfin, le visiteur est redirigé vers la Index vue où tous les commentaires sont affichés.

La Index vue est contenue dans la liste 2.

Listing 2 – Index.aspx

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CustomerFeedback.Views.Home.Index"%>

<%@ Import Namespace="CustomerFeedback.Models" %>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
     <h1>Customer Feedback</h1>
     <p>
          Please use the following form to enter feedback about our product.
     </p>

     <form method="post" action="/Home/Create">

          <label for="message">Message:</label>
          <br />
          <textarea name="message" cols="50" rows="2"></textarea>
          <br /><br />
          <input type="submit" value="Submit Feedback" />
     </form>

     <% foreach (Feedback feedback in ViewData.Model)
     {%>
          <p>
          <%=feedback.EntryDate.ToShortTimeString()%>
          --
          <%=feedback.Message%>
          </p>
     <% }%>

</asp:Content>

La Index vue comporte deux sections. La section supérieure contient le formulaire de commentaires des clients réels. La section inférieure contient un for.. Chaque boucle qui effectue une boucle dans tous les éléments précédents de commentaires des clients et affiche les propriétés EntryDate et Message pour chaque élément de commentaires.

Le site web de commentaires des clients est un site web simple. Malheureusement, le site web est ouvert aux attaques par injection javascript.

Imaginez que vous entrez le texte suivant dans le formulaire de commentaires des clients :

<script>alert("Boo!")</script>

Ce texte représente un script JavaScript qui affiche une zone de message d’alerte. Une fois que quelqu’un a envoyé ce script dans le formulaire de commentaires, le message Boo!s’affiche chaque fois que quelqu’un visite le site web de commentaires des clients à l’avenir (voir la figure 2).

JavaScript Injection

Figure 02 : Injection de Code JavaScript (cliquer pour afficher l’image en taille réelle)

À présent, votre réponse initiale aux attaques par injection javascript peut être l’apathie. Vous pensez peut-être que les attaques par injection JavaScript sont simplement un type d’attaque par déformation . Vous pouvez croire que personne ne peut faire quoi que ce soit de vraiment mal en commettant une attaque par injection JavaScript.

Malheureusement, un hacker peut faire des choses vraiment, vraiment maléfiques en injectant JavaScript dans un site web. Vous pouvez utiliser une attaque par injection JavaScript pour effectuer une attaque XSS (Cross-Site Scripting). Dans le cadre d’une attaque par script intersites, vous volez des informations utilisateur confidentielles et envoyez les informations à un autre site web.

Par exemple, un pirate peut utiliser une attaque par injection JavaScript pour voler les valeurs des cookies de navigateur à d’autres utilisateurs. Si des informations sensibles, telles que des mots de passe, des numéros de crédit carte ou des numéros de sécurité sociale, sont stockées dans les cookies du navigateur, un pirate peut utiliser une attaque par injection javascript pour voler ces informations. Ou, si un utilisateur entre des informations sensibles dans un champ de formulaire contenu dans une page qui a été compromise par une attaque JavaScript, le pirate peut utiliser le Code JavaScript injecté pour récupérer les données du formulaire et les envoyer à un autre site web.

S’il vous plaît avoir peur. Prenez au sérieux les attaques par injection de JavaScript et protégez les informations confidentielles de votre utilisateur. Dans les deux sections suivantes, nous aborderons deux techniques que vous pouvez utiliser pour défendre vos applications ASP.NET MVC contre les attaques par injection javascript.

Approche n°1 : Encodage HTML dans l’affichage

Une méthode simple pour empêcher les attaques par injection JavaScript consiste à encoder HTML toutes les données entrées par les utilisateurs du site web lorsque vous réafficher les données dans une vue. La vue mise à jour Index dans la liste 3 suit cette approche.

Listing 3 – Index.aspx (encodé en HTML)

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CustomerFeedback.Views.Home.Index"%>

<%@ Import Namespace="CustomerFeedback.Models" %>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
     <h1>Customer Feedback</h1>
     <p>
          Please use the following form to enter feedback about our product.
     </p>

     <form method="post" action="/Home/Create">
          <label for="message">Message:</label>
          <br />
          <textarea name="message" cols="50" rows="2"></textarea>
          <br /><br />
          <input type="submit" value="Submit Feedback" />

     </form>

     <% foreach (Feedback feedback in ViewData.Model)
     {%>
          <p>
          <%=feedback.EntryDate.ToShortTimeString()%>
          --
          <%=Html.Encode(feedback.Message)%>
          </p>
     <% }%>

</asp:Content>

Notez que la valeur de feedback.Message est encodée au format HTML avant que la valeur ne s’affiche avec le code suivant :

<%=Html.Encode(feedback.Message)%>

Que signifie l’encodage HTML d’une chaîne ? Lorsque vous encodez une chaîne au format HTML, les caractères dangereux tels que < et > sont remplacés par des références d’entité HTML telles que &lt; et &gt;. Par conséquent, lorsque la chaîne <script>alert("Boo!")</script> est encodée au format HTML, elle est convertie &lt;script&gt;alert(&quot;Boo!&quot;)&lt;/script&gt;en . La chaîne encodée ne s’exécute plus en tant que script JavaScript lorsqu’elle est interprétée par un navigateur. Au lieu de cela, vous obtenez la page inoffensive de la figure 3.

Attaque JavaScript vaincue

Figure 03 : Attaque JavaScript vaincue (cliquer pour afficher l’image en taille réelle)

Notez que dans la Index vue de la liste 3, seule la valeur de feedback.Message est encodée. La valeur de feedback.EntryDate n’est pas encodée. Vous devez uniquement encoder les données entrées par un utilisateur. Étant donné que la valeur d’EntryDate a été générée dans le contrôleur, vous n’avez pas besoin d’encoder cette valeur au format HTML.

Approche n°2 : Encodage HTML dans le contrôleur

Au lieu de données d’encodage HTML lorsque vous affichez les données dans une vue, vous pouvez encoder les données au format HTML juste avant d’envoyer les données à la base de données. Cette deuxième approche est adoptée dans le cas de la controller liste 4.

Listing 4 – HomeController.cs (encodé en HTML)

using System;
using System.Web.Mvc;
using CustomerFeedback.Models;
namespace CustomerFeedback.Controllers
{
     [HandleError]
     public class HomeController : Controller
     {
          private FeedbackDataContext db = new FeedbackDataContext();

          public ActionResult Index()
          {
               return View(db.Feedbacks);
          }

          public ActionResult Create(string message)
          {
               // Add feedback
               var newFeedback = new Feedback();
               newFeedback.Message = Server.HtmlEncode(message);
               newFeedback.EntryDate = DateTime.Now;
               db.Feedbacks.InsertOnSubmit(newFeedback);

               db.SubmitChanges();

               // Redirect
               return RedirectToAction("Index");
          }
     }
}

Notez que la valeur de Message est encodée au format HTML avant que la valeur ne soit envoyée à la base de données au sein de l’action Create() . Lorsque le message est réaffiché dans la vue, le message est encodé au format HTML et tout code JavaScript injecté dans le message n’est pas exécuté.

En règle générale, vous devez privilégier la première approche décrite dans ce tutoriel par rapport à cette deuxième approche. Le problème avec cette deuxième approche est que vous vous retrouvez avec des données encodées au format HTML dans votre base de données. En d’autres termes, vos données de base de données sont sales avec des caractères drôles.

Pourquoi est-ce mauvais ? Si vous avez besoin d’afficher les données de base de données dans autre chose qu’une page web, vous rencontrez des problèmes. Par exemple, vous ne pouvez plus facilement afficher les données dans une application Windows Forms.

Résumé

L’objectif de ce tutoriel était de vous faire peur à propos de la perspective d’une attaque par injection javascript. Ce tutoriel a abordé deux approches pour défendre vos applications ASP.NET MVC contre les attaques par injection JavaScript : vous pouvez encoder html les données envoyées par l’utilisateur dans la vue ou coder html les données envoyées par l’utilisateur dans le contrôleur.