Empêcher les attaques par injection de JavaScript

Louis-Guillaume MORAND (Traduction)

Empêcher les attaques par injection de JavaScript ou les attaques par Cross-Site Scripting (XSS) n’est pas toujours évident à réaliser. Dans ce tutoriel, Stephen Walther vous explique comment contrer ces deux types d’attaques en encodant (en HTML) votre contenu. ♪

- Titre : Empêcher les attaques par injection de JavaScript

- Auteur : Louis-Guillaume MORAND (Traduction)

- Parution : 1 mai 2009

- Licence : Copyright ® 2009 Equipe dotnet. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Traduction

- Introduction

- Qu’est-ce qu’une attaque par injection de JavaScript?

- Approche #1: Encoder en HTML dans la vue

- Approche #2: Encoder en HTML depuis le Contrôleur.

- Conclusion

Traduction

Cet article est la traduction la plus fidèle possible de l’article original : Preventing JavaScript Injection Attacks

Introduction

L’objectif de ce tutoriel est d’expliquer comment vous pouvez empêcher les attaques par injection JavaScript dans vos applications ASP.Net MVC. Ce tutoriel discute de deux approches pour se défendre contre ce type d’attaques. Vous allez apprendre comment bloquer ce type d’injection en encodant les données que vous affichez ou alors, en encodant les données que vous recevez.

Qu’est-ce qu’une attaque par injection de JavaScript?

À chaque fois que vous acceptez une saisie utilisateur et réaffichez cette saisie vous ouvre la porte 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 retours utilisateurs (Livre d’or). Les visiteurs peuvent visiter le site et entrer leur feedback personnel. Une fois les données saisies et le formulaire soumis, les données sont réaffichées dans la page.

Le livre d’or utilise le contrôleur présenté par le code suivant. Ce contrôleur contient deux actions nommées Index() et Create().

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 méthode Index() affiche la vue Index. Cette méthode passe tous les anciens avis utilisateurs à la vue Index, en récupérant cette liste d’avis depuis la base (en utilisant une requête LINQ to SQL).

La méthode Create() créé un nouvel élément Feedback et l’ajoute à la base de données. Ce message, que l’utilisateur saisi dans le formulaire, est passé à la méthode Create() via le paramètre message. Un élément Feedback est créé et le message est assigné à la propriété Message de l’élément Feedback. Puis, le Feedback est inséré en base avec l’appel à la méthode DataContext.SubmitChanges(). Pour finir, le visiteur est redirigé vers la vue Index où tous les autres feedbacks sont affichés.

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 vue Index a deux sections. La section du haut contient le formulaire de saisie de feedback tandis que la section du bas contient un For…Each qui boucle sur les feedbacks précédemment saisis et affiche les propriétés EntryDate et Message pour chacun d’entre eux.

Le site de Livre d’or est un site Web tout simple. Malheureusement, ce site est ouvert à des attaques par injection JavaScript.

Imaginez que vous entrez le code suivant dans le formulaire de feedback :

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

Ce texte représente un script JavaScript qui affiche un message d’alerte. Après que quelqu’un a soumis son formulaire de feedback, le message « Boo! » apparaîtra à chaque fois qu’une personne visite le Livre d’or.

Maintenant, la première réponse à une attaque par injection JavaScript pourrait être l’apathie. Vous pourriez vous dire que ce type d’attaque n’est simplement qu’une attaque dite de défacement. Vous pourriez penser que personne ne pourrait vraiment faire quelque chose de mal et/ou dangereux avec une simple injection JavaScript.

Malheureusement, un hacker pourrait réaliser des choses vraiment peu gentilles. Il pourrait utiliser le JavaScript pour faire une attaque Cross-Site Scripting (XSS). Dans une attaque XSS, vous volez les informations utilisateur (mot de passe, carte de crédit) et envoyez ces informations sur un autre site Web qui les enregistre pour les réutiliser plus tard.

Par exemple, un hacker peut utiliser une attaque par injection JavaScript pour voler les valeurs des cookies navigateur d’autres utilisateurs. Si des informations sensibles, comme des mots de passe, des numéros de crédit, etc., sont stockées dans les cookies alors le hacker peut utiliser un code JavaScript pour récupérer cette information. Ou, si un utilisateur entre des informations sensibles dans un champ de formulaire contenu dans une page qui a été compromise avec une attaque JavaScript alors le hacker peut utiliser son code JavaScript pour récupérer les données et les envoyer directement sur un autre site.

S’il vous plait, soyez effrayés par ce type d’attaques. Prenez les attaques par injection JavaScript au sérieux et protégez les informations confidentielles de vos utilisateurs. Dans les deux prochaines sections, nous allons discuter de deux techniques que vous pourrez utiliser pour défendre votre application ASP.Net MVC contre ce type d’attaques.

Approche #1: Encoder en HTML dans la vue

Une méthode simple de se protéger contre les injections JavaScript est d’encoder en HTML toute donnée qui sera réaffichée dans une vue. La vue Index listée ci-dessous, utilise justement cette approche :

Index.aspx (HTML Encoded)

<%@ 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 feedback.Message est encodé en HTML avant la valeur affichée, grâce au code suivant :

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

Qu’est-ce que signifie encoder en HTML une chaine de caractères? Lorsque vous encodez une chaine de caractères, les caractères dangereux tels que < et > sont remplacés par une référence HTML telle que &lt; and &gt;.Ainsi, la chaine <script>alert(« Boo! »)</script> est encodée et est convertie en &lt;script&gt;alert(&quot;Boo!&quot;)&lt;/script&gt;. La chaine encodée ne peut alors plus exécuter de JavaScript lorsqu’elle est interprétée par un navigateur. À la place, vous obtenez la page inoffensive suivante :

Notez que dans la vue Index listée plus haut, seule la valeur feedback.Message est encodée. La valeur de feedback.EntryDate ne l’est pas. Vous avez seulement besoin d’encoder les données saisies par l’utilisateur. Comme la valeur EntryDate est générée par le contrôleur, vous pouvez lui « faire confiance » et n’avez pas besoin de l’encoder.

Approche #2: Encoder en HTML depuis le Contrôleur.

Plutôt que d’encoder les données lorsque vous les affichez dans une vue, vous pouvez encoder ces mêmes données juste avant d’insérer ces données dans la base de données. Cette seconde approche est justement utilisée par le contrôleur suivant :

HomeController.cs (HTML Encoded)

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 du message est encodée en HTML avant que la valeur soit soumise dans la base de données. Lorsque le Message est réaffiché dans la vue, le Message est encodé en HTML et n’importe quel code JavaScript ne pourra pas être exécuté.

Typiquement, vous devriez favoriser la première approche discutée dans ce tutoriel. Le problème avec la seconde approche est vous finissez avec du contenu encodé en HTML dans la base de données. Entre autres termes, les données de votre base sont remplies de caractères étranges et peu lisibles.

Pourquoi est-ce si mauvais ? Si vous avez besoin d’afficher les données dans autre chose qu’une page Web, vous aurez des problèmes. Par exemple, vous aurez du mal à afficher correctement ces informations dans une application WinForms.

Conclusion

L’objectif de ce tutoriel était de vous effrayer à propos de la possibilité d’une attaque par injection JavaScript. Ce tutoriel a discuté de deux différentes approches pour défendre votre application MVC donc ce type d’attaques : vous pouvez encoder en HTML le contenu, soit à l’affichage, soit à la saisie des entrées utilisateurs via le contrôleur.