Skip to main content

Protection des utilisateurs contre la falsification des demandes inter-sites

novembre

03, 2020

by Iron_Legion


Technologie

Imaginez ce scénario : après vous être connecté à votre compte bancaire et avoir vérifié votre solde, vous naviguez vers un autre site web. Soudain, vous remarquez que votre compte a été piraté ! Des fonds sont transférés et l’agresseur tente de réinitialiser votre mot de passe. Vous êtes soucieux de votre sécurité et ne tombez jamais dans le piège du phishing ou du partage de votre mot de passe, alors comment cela a-t-il pu se produire ?

Ceci pourrait être un exemple de Cross-Site Request Forgery, souvent abrégé en CSRF ou XSRF, si vous pensez eXtreme (vulnérabilité des services d’authentification web). XSRF est une exploitation courante de la sécurité du web à laquelle même les géants de la technologie comme Google, Netflix et Microsoft se sont montrés sensibles dans le passé. Heureusement, Roblox dispose de protections contre le XSRF, mais examinons comment cela fonctionne et quels sont les outils que nous avons mis à profit pour protéger notre site web.

Vous visitez le site web d’un agresseur, et ce site web fait en sorte que votre navigateur envoie des requêtes en votre nom. Ils peuvent manipuler vos données ou demander à votre navigateur d’effectuer des actions indésirables pour vous, comme réinitialiser votre mot de passe, modifier votre adresse électronique ou acheter un article virtuel. Comme votre navigateur inclut vos cookies et les informations de votre session avec la demande, le serveur web fera confiance à la demande et exécutera ce qui est demandé.

C’est une faille qui existe depuis les débuts de l’Internet. Un certain nombre de solutions ont été développées autour de la validation du référent ou de l’en-tête d’origine, mais elles ne fonctionnent pas dans tous les cas. Les cookies SameSite aident dans de nombreux cas, mais ils ne sont pas encore pris en charge par tous les navigateurs et nous avons besoin d’une couche de sécurité supplémentaire.

La solution standard du secteur consiste à joindre un « token XSRF » à toutes les demandes ou formulaires AJAX envoyés depuis un site web. Le token XSRF est une valeur secrète, unique à chaque utilisateur, qui est généralement intégrée dans la source de la page, ce qui le rend accessible au Javascript fonctionnant sur votre domaine, mais non accessible aux sites web tiers.

Lorsqu’une requête HTTP atteint le backend, le serveur web vérifie que le token XSRF correspond à la valeur attendue. Si ce n’est pas le cas, la demande est rejetée et le terminal renvoie une réponse d’erreur appropriée. Notez que la valeur est unique pour chaque utilisateur et qu’elle change au fil du temps afin de réduire le risque d’attaques par rediffusion.

XSRF sur Roblox

Chez Roblox, nous avons toujours appliqué la protection XSRF sur la base d’un opt-in, ce qui signifie que nous devions marquer manuellement chaque point terminal de commutation de données avec un attribut comme C#. [XsrfProtection]. C’était une solution viable pour Roblox pendant de nombreuses années, mais elle est fondamentalement peu sûre par défaut car elle repose sur le fait que les ingénieurs se souviennent d’ajouter un morceau de code spécifique à chaque point final, ce qui n’était pas efficace. Nous avons également ajouté des outils d’analyse de code qui signalent l’absence.

Lorsque nous avons commencé à travailler sur un nouveau cadre d’API web RESTful en 2015, nous avons ajouté la protection XSRF par défaut, avec la possibilité de se désengager pour certains points terminaux. Cela a fonctionné parfaitement, mais nous devions encore nous attaquer au problème de l’ajout de cette protection aux points terminaux existants.

Notre objectif :

  • Trouver un moyen d’introduire la protection XSRF de manière généralisée pour tous les terminaux existants.
  • La solution ne devrait pas nécessiter de personnalisation par point de terminaison.
  • La solution doit avoir un impact minimal ou nul pour les utilisateurs de la production.

L’un des défis est que nous devons soutenir tous nos cadres frontaux :

  • React (cadre primaire, utilisé avec Axios pour les appels HTTP)
  • AngularJS
  • jQuery
  • ASP.Net MVC
  • ASP.Net WebForms
  • Code C++ natif (utilisé pour les appels HTTP du client de jeu Roblox)

C’est là que ça devient un peu délicat. Certaines de nos anciennes pages sur le site web reposent sur l’utilisation de postes de navigation de forme normale, où le navigateur est chargé de faire la demande. Un message du navigateur ne permet pas d’attacher des en-têtes, et il ne permet pas non plus de réessayer des requêtes échouées qui passent dans un vieux token.

Pour les pages utilisant les ASP.Net WebForms, la totalité de la charge utile des demandes POST est validée à l’aide de ViewState, il nous suffit donc de personnaliser la ViewStateUserKey pour qu’elle soit unique pour chaque utilisateur afin d’éviter le XSRF.

Pour les terminaux ASP.Net MVC, nous devons nous préoccuper de deux cas d’utilisation différents :

  • Demandes AJAX
  • Formulaire d’envoi par navigateur

Tout terminal MVC est accessible dans les deux sens, ce qui signifie que nous devons adapter notre validation du backend pour rechercher le token XSRF à la fois dans les en-têtes et dans le corps du formulaire.

L’approche intégrée d’ASP.Net pour les messages de formulaires de navigateur consiste à utiliser un [ValidateAntiForgeryToken] attribut, mais celui-ci doit être ajouté manuellement à chaque point de terminaison. En outre, il ne prend pas en charge les demandes AJAX, sauf si ces demandes sont faites sur une page qui contient le token anti-falsification.

Première partie : Attacher automatiquement XSRF comme en-tête

Dans nos bibliothèques Javascript de base, nous enregistrons un gestionnaire pour modifier chaque requête avant qu’elle ne soit envoyée, et nous attachons le token XSRF comme en-tête si la requête est un POST/PATCH/PUT/DELETE. Le code est configuré pour réessayer automatiquement si la demande revient avec un statut 403 échec de validation du token ». Cette abstraction permet au code client javascript d’ignorer totalement l’existence de XSRF.

Nous devons enregistrer ces gestionnaires de trois manières différentes sur la base du cadre Javascript, mais le résultat final est presque identique.

  • Réagir – axios.interceptors.request.use
  • AngularJS – httpProvider.interceptors.push
  • jQuery – $.ajaxPrefilter

Partie 2 : Injection automatique d’un TOKEN XSRF dans les formulaires

Comme nous voulions une solution générale qui fonctionne dans tous les cas et qui ne nécessite pas de personnalisation particulière pour les pages individuelles, nous avons décidé d’écrire un Javascript qui intercepterait toutes les soumissions de formulaires du navigateur.

Cela peut être fait en surmontant le rôle de HTMLFormElement.prototype.submit avec une fonction qui ajoute un <input name=”CsrfToken” value=”secret”> au formulaire avant de le soumettre.

Qu’en est-il des cas où l’utilisateur a laissé une page inactive pendant quelques minutes et tente de soumettre un formulaire alors que le token XSRF a déjà expiré ? Pour les demandes AJAX, ce n’est pas grave, nous pouvons simplement réitérer la demande. Cependant, dans le cas des formulaires, le navigateur contrôle le flux des pages et nous ne pouvons pas gérer ce scénario avec élégance. Nous avons résolu ce problème en exposant un terminal qui renvoyait le token XSRF dans les en-têtes de réponse lorsque le client le demandait. Si l’utilisateur tente de soumettre un formulaire et que nous constatons que le token est périmé, en fonction du temps de chargement de la page d’origine, nous annulons la soumission du formulaire et demandons le dernier token XSRF pour nous assurer d’en obtenir un valable. Ce n’est que lorsque nous recevons ce dernier token que nous procédons à une nouvelle soumission du formulaire.

Pour la mise en œuvre de notre backend, nous avons créé un attribut de filtre d’action XsrfValidationFilterAttribute que nous avons enregistré à un niveau de base pour tous nos sites web. Cette classe se déroule avant l’exécution de chaque terminal et vérifie que le token XSRF est présent pour tous les terminaux de commutation de données (selon que la méthode HTTP est POST, PUT, PATCH ou DELETE).

Leçons à retenir

Lorsque vous ajoutez de nouvelles caractéristiques de sécurité, mesurez l’impact avant de commencer à les appliquer.

L’ajout de mesures par point de terminaison pour nous dire quelles URL ne gèrent pas correctement XSRF s’est avéré inestimable lors du débugage des problèmes. Nous avons utilisé la même approche avant de mettre en place la politique de sécurité des contenus.

Attention à l’indexation des propriétés en Javascript !

Lorsqu’un élément HTML contient une entrée avec un attribut nom, celui-ci a la priorité sur toutes les propriétés portant le même nom.

<form action=”endpoint-goes-here”>

<input name=”action” value=””>

</form>

L’une des pages de notre site Web comportait une entrée de formulaire appelée « action », de sorte que notre code XSRF, appelé form.action, lisait par inadvertance la valeur d’entrée au lieu de la propriété du formulaire !

Heureusement, nos testeurs d’assurance qualité ont repéré le problème très tôt et le passage à form.getAttribute(« action ») l’a résolu.


Ni Roblox Corporation ni ce blog ne cautionnent ni ne soutiennent aucune entreprise ou service. En outre, aucune garantie ou promesse n’est faite quant à l’exactitude, la fiabilité ou l’exhaustivité des informations contenues dans ce blog.

Cet article de blog a été publié à l’origine sur Roblox Tech Blog.