Share via


Sécurité ASP.NET

Sécurisez vos applications ASP.NET

Adam Tuliper

Dans le numéro précédent, j'ai parlé de l'importance de la sécurisation de vos applications Web et examiné certains types d'attaques, dont l'injection de code SQL et la falsification de paramètres, ainsi que les méthodes de prévention contre ces attaques (msdn.microsoft.com/magazine/hh580736). Cet article sera quant à lui consacré à deux autres attaques courantes qui vous permettront de compléter votre arsenal de protection des applications, à savoir le script de site à site (XSS) et la falsification de requête intersites (CSRF).

Vous vous demandez peut-être pourquoi vous ne pouvez pas utiliser un simple scanneur de sécurité de production. Les scanneurs sont des outils formidables lorsque vous devez rechercher des éléments faciles d'accès, et plus particulièrement lorsqu'il s'agit de détecter des problèmes de configuration du système et des applications. Toutefois, ils ne peuvent pas connaître vos applications aussi bien que vous. Il est donc impératif que vous appreniez à connaître les problèmes de sécurité potentiels et que vous preniez le temps de vérifier vos applications et d'intégrer la sécurité dans le cycle de vie du développement de vos logiciels.

Script de site à site

Qu'est-ce que c'est ? Le script de site à site est une attaque au cours de laquelle un script est injecté de façon malveillante dans la session de navigation d'un utilisateur, généralement à l'insu de ce dernier. Un grand nombre de personnes connaissent maintenant ces types d'attaques en raison d'incidents au cours desquels un site de réseau social important a été touché et des messages ont été publiés sans l'autorisation des utilisateurs. Si une personne malveillante publie un script malveillant qu'elle peut faire exécuter par le navigateur, ce script est exécuté dans le contexte de la session de la victime, ce qui permet surtout à l'attaquant de faire tout ce qu'il veut au DOM, notamment afficher des boîtes de dialogue de connexion fictives ou voler des cookies. Ce type d'attaque pourrait même installer un enregistreur de frappe HTML sur la page active de façon à envoyer en continu des informations provenant de cette fenêtre vers un site distant.

Comment ce type d'attaque est-il exploité ? Le script de site à site est exploité via plusieurs méthodes qui reposent toutes sur la présence d'une sortie non échappée ou mal échappée. Prenons le cas d'une application qui doit afficher un simple message d'état destiné à l'utilisateur final. En règle générale, ce message est passé sur la chaîne de requête, comme illustré à la figure 1.

Query String MessageFigure 1 Message de la chaîne de requête

Cette technique est couramment utilisée après une redirection permettant d'afficher un état destiné à l'utilisateur, par exemple le message indiquant que le profil a été enregistré, comme illustré à la figure 1. Ce message est lu depuis la chaîne de requête et inscrit directement sur la page. Si la sortie n'est pas codée en HTML, n'importe qui peut aisément injecter du code JavaScript au lieu du message d'état. Ce type d'attaque est considéré comme une attaque de script de site à site réfléchie car ce qui se trouve sur la chaîne de requête est rendu immédiatement sur la page. Dans le cadre d'une attaque persistante, le script malveillant est stocké, généralement dans une base de données ou un cookie.

À la figure 1, vous pouvez voir que l'URI prend un paramètre msg. La page Web de cet URI contiendrait un code similaire au suivant pour écrire simplement la variable sur la page sans aucun codage :

    <div class="messages"> <%=Request.QueryString["msg"]%></div>

Si vous remplacez « Profile Saved » par le script illustré à la figure 2, la fonction d'alerte apparaît dans le navigateur depuis le script inclus dans la chaîne de requête. La réussite de cette attaque repose sur le fait que les résultats ne sont pas codés en HTML et que la balise <script> est en fait analysée comme du JavaScript valide par le navigateur et exécutée. Selon toute évidence, ce n'était pas l'intention du développeur dans le cas présent.

Injecting Script into the URI
Figure 2 Injection de script dans l'URI

Mais en quoi l'affichage d'une alerte pose-t-il un problème ? Poursuivons avec cet exemple, comme illustré à la figure 3. Vous remarquerez que j'ai raccourci l'attaque dans le cadre de ma démonstration. La syntaxe n'est pas tout à fait correcte, mais une légère modification la transformerait en véritable attaque.

Creating a Malicious Attack
Figure 3 Création d'une attaque malveillante

Une attaque de ce type entraînerait l'affichage d'une boîte de dialogue de connexion fictive, dans laquelle les utilisateurs entreraient gaiement leurs informations d'identification. Dans le cas présent, un script distant est téléchargé, ce qui est généralement autorisé par les paramètres de sécurité par défaut du navigateur. Théoriquement, ce type d'attaque peut se produire partout où une chaîne qui n'a pas été codée ni expurgée peut être renvoyée à un utilisateur.

La figure 4 montre une attaque qui utilise un script similaire sur un site de développement permettant aux utilisateurs de laisser des commentaires au sujet des produits proposés. Toutefois, au lieu de laisser un vrai commentaire, quelqu'un a entré du JavaScript malveillant. Ce script affiche désormais une boîte de dialogue de connexion pour chaque utilisateur qui atteint la page Web, puis il collecte et envoie les informations d'identification à un site distant. Il s'agit d'une attaque de script de site à site persistante. Le script est stocké dans la base de données et répété pour toute personne qui visite la page.

A Persistent XSS Attack Showing a Fake Dialog
Figure 4 Une attaque XSS persistante qui affiche une boîte de dialogue fictive

Le script de site à site peut également être exploité en utilisant des éléments HTML, comme lorsque du texte dynamique est autorisé dans des balises HTML, de la façon suivante :

    <img onmouseover=alert([user supplied text])>

Si une personne malveillante injecte un texte similaire à : « onmouseout=alert(document.cookie) », la balise suivante est créée dans le navigateur qui accède au cookie :

    <img onmouseover=alert(1) onmouseout=alert(document.cookie) >

Il n'y a aucune balise « <script> » susceptible d'être utilisée pour filtrer les entrées et il n'y a rien à échapper, mais il s'agit d'un morceau de JavaScript tout à fait valide qui permet de lire un cookie, et pourquoi pas un cookie d'authentification. Certaines méthodes propres à chaque cas de figure vous permettent de rendre cela plus sûr mais étant donné le risque, il est préférable d'empêcher les entrées utilisateur d'atteindre ce code en ligne ici.

Comment éviter un script de site à site ? Respectez rigoureusement les règles ci-après pour éviter la plupart, voire toutes les attaques XSS dans votre application :

  1. Veillez à ce que toutes vos sorties soient codées en HTML.

  2. N'autorisez aucun texte fourni par l'utilisateur à se terminer par une chaîne d'attribut d'élément HTML.

  3. Empêchez votre application d'utiliser Internet Explorer 6 en vérifiant Request.Browser comme indiqué dans msdn.microsoft.com/library/3yekbd5b.

  4. Connaissez le comportement du contrôle et sachez s'il code la sortie en HTML. Si tel n'est pas le cas, codez les données qui vont vers le contrôle.

  5. Utilisez la bibliothèque anti-script de site à site et définissez-la comme votre encodeur HTML par défaut.

  6. Utilisez l'objet AntiXSS Sanitizer (nous aborderons plus loin dans cet article cette bibliothèque qui fait l'objet d'un téléchargement distinct) pour appeler GetSafeHtml ou GetSafeHtmlFragment avant d'enregistrer les données HTML dans la base de données. Ne codez pas les données avant de les enregistrer.

  7. Pour Web Forms, ne définissez pas EnableRequestValidation=false dans vos pages Web. Malheureusement, la plupart des publications sur le Web des groupes d'utilisateurs conseillent la désactivation de ce paramètre en cas d'erreur. Ce paramètre est là pour une bonne raison et il interrompra la requête si, par exemple, la combinaison de caractères « <X, » est republiée sur le serveur. Si vos contrôles republient le HTML sur le serveur et reçoivent l'erreur illustrée à la figure 5, il est idéalement recommandé de coder les données avant de les publier sur le serveur. Il s'agit d'un scénario courant avec les contrôles WYSIWYG et la plupart des versions modernes codent correctement leurs données HTML avant de les republier sur le serveur.

    Server Error from Unencoded HTML
    Figure 5 Erreur de serveur due à un HTML codé

  8. Pour les applications ASP.NET MVC 3, lorsque vous devez republier du HTML sur votre modèle, n'utilisez pas ValidateInput(false) pour désactiver la validation de requête. Contentez-vous d'ajouter [AllowHtml] à la propriété du modèle, de la façon suivante :

public class BlogEntry
{
  public int UserId {get;set;}
  [AllowHtml]
  public string BlogText {get;set;}
}

Certains produits tentent de détecter <script> et d'autres combinaisons de mots ou modèles d'expressions régulières dans une chaîne pour tenter de détecter le script de site à site. Bien que ces produits puissent fournir des vérifications supplémentaires, ils ne sont pas totalement fiables en raison des nombreuses variantes qui ont été créées par des personnes malveillantes. Prenez le temps de jeter un œil à la fiche de révision sur le script de site à site, à l'adresse ha.ckers.org/xss.html afin de vous rendre compte de la difficulté de la détection.

Pour comprendre les solutions possibles, supposons qu'une personne malveillante ait injecté un script qui se trouve maintenant dans une variable de notre application. L'injection a eu lieu depuis la chaîne de requête ou un champ de formulaire, comme illustré ici :

string message = Request.QueryString["msg"];

ou :

string message = txtMessage.Text;

Bien qu'un contrôle HTML TextBox code sa sortie, il ne code pas la propriété Text lorsque vous le lisez depuis le code. Avec l'une ou l'autre de ces lignes de code, vous obtenez la chaîne suivante dans la variable du message :

message = "<script>alert('bip')</script>"

Dans une page Web contenant du code similaire à ce qui suit, le JavaScript est exécuté dans le navigateur de l'utilisateur simplement parce que ce texte a été écrit sur la page :

    <%=message %>

Le codage HTML de la sortie interrompt cette attaque en pleine action. La figure 6 illustre les options principales de codage des données dangereuses.

Ces options empêchent le type d'attaque indiqué dans l'exemple et devraient être utilisées dans vos applications.

Figure 6 Options de codage HTML

ASP.NET (MVC ou Web Forms) <%=Server.HtmlEncode(message) %>
Web Forms (syntaxe ASP.NET 4) <%: message %>
Razor ASP.NET MVC 3 @message
Liaison de données

Malheureusement, la syntaxe de liaison de données ne contient pas encore de syntaxe de codage intégrée, cela sera disponible dans la prochaine version d'ASP.NET sous la forme <%#: %>. D'ici là, utilisez :

<%# Server.HtmlEncode(Eval("PropertyName")) %>

Meilleur codage

Depuis la bibliothèque AntiXSS de l'espace de noms Microsoft.Security.Application :

Encoder.HtmlEncode(message)

Il est important de connaître vos contrôles. Quels sont ceux qui codent vos données en HTML et quels sont ceux qui ne le font pas ? Par exemple, un contrôle TextBox code en HTML la sortie rendue, contrairement à un LiteralControl. Cette distinction est importante. Un textbox auquel est attribué :

yourTextBoxControl.Text = "Test <script>alert('bip')</script>";

rend correctement le texte sur la page sous la forme suivante :

    Test &lt;script&gt;alert(&#39;bip&#39;)&lt;/script&gt;

En revanche :

yourLiteralControl.Text = "Test <script>alert('bip')</script>";

entraîne l'affichage d'une alerte JavaScript sur la page. Cette alerte confirme la vulnérabilité aux scripts de site à site. Pour résoudre ce problème, il suffit d'utiliser ce qui suit :

yourLiteralControl.Text = Server.HtmlEncode(
    "Test <script>alert('bip')</script>");

Les choses sont légèrement plus complexes lors de l'utilisation de la liaison de données dans Web Forms. Observons l'exemple suivant :

    <asp:Repeater ID="Repeater1" runat="server">
        <ItemTemplate>
          <asp:TextBox ID="txtYourField" Text='<%# Bind("YourField") %>'
            runat="server"></asp:TextBox>
        </ItemTemplate>
      </asp:Repeater>

Est-ce vulnérable ? Non, pas du tout. Bien que le code en ligne donne l'impression de pouvoir écrire le script ou de sortir des guillemets du contrôle, il est bel et bien codé. 

Qu'en est-il de ce qui suit :

    <asp:Repeater ID="Repeater2" runat="server">
      <ItemTemplate>
        <%# Eval("YourField") %>
      </ItemTemplate>
    </asp:Repeater>

Est-ce vulnérable ? Oui, tout à fait. La syntaxe de la liaison de données <%# %> ne code pas en HTML. Voici la solution :

    <asp:Repeater ID="Repeater2" runat="server">
      <ItemTemplate>
        <%#Server.HtmlEncode((string)Eval("YourText"))%>
      </ItemTemplate>
    </asp:Repeater>

Notez que si vous utilisez Bind dans ce scénario, vous ne pourrez pas inclure Server.HtmlEncode dans un wrapper en raison de la façon dont Bind compile en arrière-plan sous forme de deux appels distincts. Ce qui suit échouera :

    <asp:Repeater ID="Repeater2" runat="server">
      <ItemTemplate>
        <%#Server.HtmlEncode((string)Bind("YourText"))%>
      </ItemTemplate>
    </asp:Repeater>

Si vous utilisez Bind et que vous n'attribuez pas le texte à un contrôle susceptible d'être codé en HTML (par exemple, le contrôle TextBox), pensez à utiliser Eval à la place afin de pouvoir inclure dans un wrapper l'appel de Server.HtmlEncode, comme dans l'exemple précédent.

Le même concept de liaison de données n'existe pas dans ASP.NET MVC, vous devez donc savoir si les assistances HTML coderont. Les assistances des étiquettes et des zones de texte codent en HTML. Par exemple, ce code :

@Html.TextBox("customerName", "<script>alert('bip')</script>")
@Html.Label("<script>alert('bip')</script>")

est rendu de la façon suivante :

    <input id="customerName" name="customerName" type="text"
      value="&lt;script>alert(&#39;bip&#39;)&lt;/script>" />
    <label for="">&lt;script&gt;alert(&#39;bip&#39;)&lt;/script&gt;</label>

J'ai déjà mentionné plus tôt la bibliothèque anti-script de site à site. Actuellement disponible en version 4.1 bêta 1, cette bibliothèque a été réécrite et améliorée. Du point de vue de la sécurité, elle propose un encodeur HTML meilleur que celui fourni avec ASP.NET. Il n'y a rien qui cloche avec Server.HtmlEncode, mais il se concentre sur la compatibilité et non la sécurité. La bibliothèque anti-script de site à site utilise une approche différente pour coder. Vous trouverez des informations complémentaires sur msdn.microsoft.com/security/aa973814.

La version bêta est disponible sur bit.ly/gMcB5K. Vérifiez si la bibliothèque anti-script de site à site n'est plus en version bêta. Si tel n'est pas le cas, vous devrez télécharger le code et le compiler. Jon Galloway a publié un excellent article à ce sujet sur bit.ly/lGpKWX.

Pour utiliser l'encodeur anti-script de site à site, il suffit de faire l'appel suivant :

    <%@ Import Namespace="Microsoft.Security.Application" %>
    ...
    ...
    <%= Encoder.HtmlEncode(plainText)%>

ASP.NET MVC 4 a ajouté une nouvelle fonctionnalité exceptionnelle qui vous permet de remplacer l'encodeur HTML ASP par défaut et d'utiliser l'encodeur anti-script de site à site à la place. Au moment où cet article est rédigé, vous avez besoin de la version 4.0. Étant donné qu'elle se trouve actuellement en version bêta, vous devez télécharger le code, le compiler et ajouter la bibliothèque comme référence à votre application. Cette opération prend cinq minutes en tout. Ensuite, dans web.config, ajoutez la ligne suivante dans la section <system.web> :

<httpRuntime encoderType=
  "Microsoft.Security.Application.AntiXssEncoder, AntiXssLibrary"/>

Maintenant, tout appel codant en HTML effectué via l'une des syntaxes répertoriées à la figure 6, dont la syntaxe Razor ASP.NET MVC 3, sera codé par la bibliothèque anti-script de site à site. Pas mal pour une fonctionnalité enfichable.

Cette bibliothèque comprend également un objet Sanitizer susceptible d'être utilisé pour nettoyer le HTML avant de le stocker dans une base de données, ce qui est très utile si vous fournissez un éditeur WYSIWYG à l'utilisateur pour la modification du HTML. Cet appel tente de supprimer le script de la chaîne :

using Microsoft.Security.Application;
...
...
string wysiwygData = "before <script>alert('bip ')</script> after ";
string cleanData = Sanitizer.GetSafeHtmlFragment(wysiwygData);
This results in the following cleaned string that can then be saved to the database:
cleanData = "before  after ";

Falsification de requête intersites (CSRF, Cross-Site Request Forgery)

Qu'est-ce que c'est ? La falsification de requête intersites est une attaque qui se produit lorsque quelqu'un profite de la confiance entre votre navigateur et un site Web pour exécuter une commande à l'aide de la session d'un utilisateur innocent. Cette attaque est légèrement plus difficile à imaginer sans voir les détails. Nous allons donc les examiner immédiatement.

Comment ce type d'attaque est-il exploité ? Supposons que Jean ait été authentifié comme administrateur sur le site PureShoppingHeaven. PureShoppingHeaven a une URL limitée à l'accès admin et permet aux informations d'être passées sur l'URL afin d'exécuter une action, par exemple la création d'un utilisateur, comme illustré à la figure 7.

Passing Information on the URL
Figure 7 Passage d'informations sur l'URL

Si une personne malveillante parvient à faire en sorte que Jean demande cette URL via diverses méthodes, son navigateur la demandera depuis le serveur, puis enverra les informations d'authentification déjà en cache ou utilisées dans le navigateur de Jean, par exemple des cookies ou autres jetons d'authentification, dont l'authentification Windows.

Il s'agit d'un exemple simple, mais les attaques par falsification de requête intersites peuvent être bien plus sophistiquées et intégrer des POST de formulaires outre les requêtes GET. Elles peuvent également profiter simultanément d'autres attaques telles que les scripts de site à site.

Supposons que Jean visite un site de réseau social vulnérable qui a été exploité. Il est possible qu'une personne malveillante ait placé un peu de JavaScript sur la page via une vulnérabilité de script de site à site qui demande maintenant l'URL AddUser.aspx en profitant de la session de Jean. Cette image mémoire de Fiddler (fiddler2.com) après la consultation de la page Web par Jean montre que le navigateur envoie également un cookie d'authentification de site personnalisé :

GET http://pureshoppingheaven/AddUser.aspx?userName=hacked&pwd=secret HTTP/1.1
Host: pureshoppingheaven
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Cookie: CUSTOMERAUTHCOOKIE=a465bc0b-e1e2-4052-8292-484d884229ab

Tout cela se produit à l'insu de Jean. Ce qu'il est important de comprendre, c'est qu'en raison de sa conception, le navigateur envoie tous les cookies ou informations d'authentification valides. Avez-vous déjà remarqué que votre client de messagerie électronique ne charge généralement pas les images par défaut ? Il s'agit notamment pour ce client d'éviter la falsification de requête intersites. Si vous avez reçu un message électronique HTML avec une balise d'image intégrée, comme indiqué ci-après, l'URL est demandée et le serveur exécute cette action si vous êtes authentifié auprès de ce site Web :

    <img src='yoursite/createuser.aspx?id=hacked&pwd=hacked' />

S'il s'avère que vous êtes un administrateur de « yoursite » qui est déjà authentifié, le navigateur sera ravi d'envoyer la requête GET avec les informations d'identification. Le serveur considère qu'il s'agit d'une requête valide faite par un utilisateur authentifié et il l'exécute à votre insu car aucune réponse d'image valide ne doit être rendue dans votre client de messagerie électronique.

Comment éviter la falsification de requête intersites ? Pour éviter la falsification de requête intersites, commencez par suivre certaines règles :

  1. Assurez-vous qu'une requête ne peut pas être émise à nouveau en cliquant simplement sur un lien pour une requête GET. La spécification HTTP des requêtes GET implique que ces dernières doivent uniquement être utilisées pour l'extraction et non pour la modification de l'état.
  2. Veillez à ce qu'une requête ne puisse pas être émise à nouveau si une personne malveillante a utilisé du JavaScript pour simuler une requête POST de formulaire.
  3. Empêchez toute action via GET. Par exemple, ne permettez pas la création ni la suppression des enregistrements via une URL. Idéalement, ces opérations doivent impliquer une interaction utilisateur. Bien que cela n'évite pas les attaques plus intelligentes reposant sur les formulaires, une multitude d'attaques plus simples sont empêchées, comme celle décrite dans l'exemple d'image de courrier électronique, ainsi que les liens de base intégrés à des sites victimes d'un script de site à site.

La protection contre les attaques via Web Forms est gérée légèrement différemment depuis ASP.NET MVC. Avec Web Forms, l'attribut MAC ViewState peut être signé, ce qui permet de protéger contre la falsification tant que vous n'avez pas défini EnableViewStateMac=false. Veillez également à signer le ViewState avec la session utilisateur en cours et à éviter que le ViewState soit passé sur la chaîne de requête afin de bloquer ce que certains nomment une attaque en un clic (voir la figure 8).

Figure 8 Protection contre une attaque en un clic

void Page_Init(object sender, EventArgs e)
{
  if (Session.IsNewSession)
  {
    // Force session to be created;
    // otherwise the session ID changes on every request.
    Session["ForceSession"] = DateTime.Now;
  }
  // 'Sign' the viewstate with the current session.
  this.ViewStateUserKey = Session.SessionID;
  if (Page.EnableViewState)
  {
    // Make sure ViewState wasn't passed on the querystring.
    // This helps prevent one-click attacks.
    if (!string.IsNullOrEmpty(Request.Params["__VIEWSTATE"]) &&
      string.IsNullOrEmpty(Request.Form["__VIEWSTATE"]))
    {
      throw new Exception("Viewstate existed, but not on the form.");
    }
  }
}

J'attribue une valeur de session aléatoire ici afin de m'assurer que la session est établie. Vous pourriez utiliser n'importe quel identificateur de session,mais l'ID de session ASP.NET est modifié à chaque requête jusqu'à ce que vous ayez créé une session. Il est impossible d'avoir un ID de session qui change avec chaque requête ici, c'est pourquoi vous devez le bloquer en créant la nouvelle session.

ASP.NET MVC contient son propre jeu d'assistances intégrées qui protègent contre la falsification de requête intersites à l'aide de jetons uniques passés avec la requête. Les assistances utilisent non seulement un champ de formulaire masqué obligatoire, mais également une valeur de cookie, ce qui rend légèrement plus compliquée la falsification d'une requête. Ces protections sont faciles à implémenter et doivent absolument être intégrées à vos applications. Pour ajouter @Html.AntiForgery­Token() à l'intérieur de <form> dans votre vue, procédez comme suit :

@using (Html.BeginForm())
{
  @Html.AntiForgeryToken();
  @Html.EditorForModel();
  <input type="submit" value="Submit" />
}
Decorate any controllers that accept post data with the [Validate­AntiForgeryToken], like so:
[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult Index(User user)
{
  ...
}

Comprendre la vulnérabilité

Cet article était consacré au script de site à site et à la falsification de requête intersites, deux méthodes courantes d'intrusion dans les applications Web. Avec les deux attaques abordées le mois dernier, à savoir l'injection de code SQL et la falsification de paramètres, vous avez maintenant une bonne compréhension de la vulnérabilité de vos applications.

Vous avez également vu à quel point il est facile de sécuriser vos applications afin de les protéger contre certaines des attaques les plus courantes. Si vous avez déjà intégré la sécurité au cycle de développement de vos logiciels, c'est parfait ! Si tel n'est pas le cas, aujourd'hui est le moment idéal pour commencer. Vous pouvez vérifier vos applications existantes page par page/module par module et, dans la plupart des cas, les refactoriser très facilement. Protégez également vos applications avec SSL afin d'éviter la détection de vos informations d'identification. N'oubliez pas de penser à la sécurité avant, pendant et après le développement.

Adam Tuliper occupe le poste d'architecte logiciel chez Cegedim et il développe des logiciels depuis plus de 20 ans. Il est conférencier national pour la communauté INETA et s'exprime régulièrement lors de conférences et dans le cadre de groupes d'utilisateurs .NET. Suivez-le sur Twitter à l'adresse twitter.com/AdamTuliper et consultez son blog sur completedevelopment.blogspot.com ou sur le nouveau site WEb secure-coding.com. Si vous souhaitez consulter des informations plus approfondies sur la protection de vos applications ASP.NET contre les intrusions, visionnez sa série vidéo Pluralsight lorsqu'elle sera disponible.

Merci à l'expert technique suivant d'avoir relu cet article : Barry Dorrans