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 Fiyaz Hasan et Rick Anderson
La falsification de requêtes intersites est une attaque contre les applications hébergées sur le Web par laquelle une application web malveillante peut influencer l’interaction entre un navigateur client et une application web qui fait confiance à ce navigateur. Ces attaques sont possibles car les navigateurs Web envoient automatiquement certains types de jetons d'authentification à chaque requête adressée à un site Web. Cette forme d'exploit est également connue sous le nom d'attaque en un clic ou de chevauchement de session, car l'attaque tire parti de la session précédemment authentifiée de l'utilisateur. La falsification de requête intersites est également appelée XSRF ou CSRF.
Un exemple d'attaque CSRF :
Un utilisateur se connecte
www.good-banking-site.example.comà l'aide de l'authentification par formulaire. Le serveur authentifie l'utilisateur et émet une réponse qui inclut une authentification cookie. Le site est vulnérable aux attaques car il fait confiance à toute requête qu'il reçoit avec une authentification valide cookie.L'utilisateur visite un site malveillant,
www.bad-crook-site.example.com.Le site malveillant,
www.bad-crook-site.example.com, contient un formulaire HTML similaire à l'exemple suivant :<h1>Congratulations! You're a Winner!</h1> <form action="https://www.good-banking-site.example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click to collect your prize!" /> </form>Notez que le formulaire
actionenvoie les données au site vulnérable, et non au site malveillant. Il s’agit de la partie « intersites » de CSRF.L'utilisateur sélectionne le bouton Soumettre. Le navigateur fait la demande et inclut automatiquement l'authentification cookie pour le domaine demandé,
www.good-banking-site.example.com.La requête s'exécute sur le serveur
www.good-banking-site.example.comavec le contexte d'authentification de l'utilisateur et peut effectuer toute action qu'un utilisateur authentifié est autorisé à effectuer.
En plus du scénario où l'utilisateur sélectionne le bouton pour soumettre le formulaire, le site malveillant pourrait :
- Exécutez un script qui soumet automatiquement le formulaire.
- Envoyez la soumission du formulaire en tant que requête AJAX.
- Masquez le formulaire à l'aide de CSS.
Ces scénarios alternatifs ne nécessitent aucune action ou entrée de la part de l'utilisateur autre que la visite initiale du site malveillant.
L'utilisation de HTTPS n'empêche pas une attaque CSRF. Le site malveillant peut envoyer https://www.good-banking-site.example.com/ une demande aussi facilement qu’elle peut envoyer une demande non sécurisée.
Certaines attaques ciblent les endpoints qui répondent aux requêtes GET, auquel cas une balise d'image peut être utilisée pour effectuer l'action. Cette forme d'attaque est courante sur les sites de forum qui autorisent les images mais bloquent JavaScript. Les applications qui changent d'état sur les requêtes GET, où des variables ou des ressources sont modifiées, sont vulnérables aux attaques malveillantes. Les requêtes GET qui changent d'état ne sont pas sécurisées. Une bonne pratique consiste à ne jamais modifier l'état d'une requête GET.
Les attaques CSRF sont possibles contre les applications Web qui utilisent des cookies pour l’authentification car :
- Les navigateurs stockent les cookies émis par une application Web.
- Les cookies stockés incluent les cookies de session pour les utilisateurs authentifiés.
- Les navigateurs envoient tous les cookies associés à un domaine à l’application web à chaque requête, quelle que soit la manière dont la demande à l’application a été générée dans le navigateur.
Cependant, les attaques CSRF ne se limitent pas à exploiter les cookies. Par exemple, les authentifications Basic et Digest sont également vulnérables. Une fois qu'un utilisateur s'est connecté avec l'authentification De base ou Digest, le navigateur envoie automatiquement les informations d'identification jusqu'à la fin de la session.
Dans ce contexte, la session fait référence à la session côté client au cours de laquelle l'utilisateur est authentifié. Il n'est pas lié aux sessions côté serveur ou ASP.NET Core intergiciel de session.
Les utilisateurs peuvent se protéger contre les vulnérabilités CSRF en prenant des précautions :
- Déconnectez-vous des applications Web lorsque vous avez fini de les utiliser.
- Effacez périodiquement les cookies des navigateurs.
Cependant, les vulnérabilités CSRF sont fondamentalement un problème avec l'application Web, pas avec l'utilisateur final.
Concepts de base de l’authentification
L'authentification basée sur Cookie est une forme d'authentification populaire. Les systèmes d'authentification basés sur des jetons gagnent en popularité, en particulier pour les applications à page unique (SPA).
authentification basée sur Cookie
Lorsqu'un utilisateur s'authentifie à l'aide de son nom d'utilisateur et de son mot de passe, il reçoit un jeton contenant un ticket d'authentification. Le jeton peut être utilisé pour l’authentification et l’autorisation. Le jeton est stocké sous la forme d'un cookie qui est envoyé avec chaque requête faite par le client. La génération et la validation de ce cookie s’effectuent à l’aide du middleware d’authentification cookie. Le middleware sérialise un principal d’utilisateur dans un cookie chiffré. Lors des requêtes suivantes, le middleware valide le cookie, recrée le principal et attribue le principal à la propriété HttpContext.User.
Authentification basée sur un jeton
Lorsqu'un utilisateur est authentifié, il reçoit un jeton (et non un jeton anti-contrefaçon). Le jeton contient des informations sur l'utilisateur sous la forme de déclarations ou d'un jeton de référence qui dirige l'application vers les données d'état de l'utilisateur conservées par l'application. Lorsqu’un utilisateur tente d’accéder à une ressource qui nécessite une authentification, le jeton est envoyé à l’application avec un en-tête d’autorisation supplémentaire sous la forme d’un Bearer jeton. Cette approche rend l'application sans état. Dans chaque demande suivante, le jeton est transmis dans la requête de validation côté serveur. Ce token n'est pas chiffré; c'est encodé. Sur le serveur, le jeton est décodé pour accéder à ses informations. Pour envoyer le jeton lors de requêtes ultérieures, stockez le jeton dans le stockage local du navigateur. Placer un jeton dans le stockage local du navigateur, le récupérer et l'utiliser comme jeton porteur offre une protection contre les attaques CSRF. Cependant, si l’application est vulnérable à l’injection de script via XSS ou un fichier JavaScript externe compromis, un cyberattaquant peut récupérer n’importe quelle valeur du stockage local et se l’envoyer. ASP.NET Core code par défaut toutes les sorties côté serveur à partir de variables, ce qui réduit le risque de XSS. Si vous remplacez ce comportement en utilisant Html.Raw ou un code personnalisé avec une entrée non fiable, vous pouvez augmenter le risque de XSS.
Ne vous inquiétez pas de la vulnérabilité CSRF si le jeton est stocké dans le stockage local du navigateur. CSRF est un problème lorsque le jeton est stocké dans un fichier cookie. Pour plus d’informations, consultez le problème GitHub L’exemple de code SPA ajoute deux cookies.
Plusieurs applications hébergées sur un domaine
Les environnements d’hébergement partagé sont vulnérables au piratage de session, à la connexion CSRF et à d’autres attaques.
Bien que example1.contoso.net et example2.contoso.net soient des hôtes différents, il existe une relation d'approbation implicite entre les hôtes du domaine *.contoso.net. Cette relation de confiance implicite permet à des hôtes potentiellement non approuvés d’interagir avec les cookies entre eux (les stratégies de même origine qui régissent les requêtes AJAX ne s’appliquent pas nécessairement aux cookies HTTP).
Les attaques qui exploitent les cookies de confiance entre les applications hébergées sur le même domaine peuvent être évitées en ne partageant pas les domaines. Lorsque chaque application est hébergée sur son propre domaine, il n'y a pas de relation de confiance implicite cookie à exploiter.
Récupérer les en-têtes de métadonnées
Les navigateurs modernes attachent des en-têtes de requête Fetch Metadata , plus important encore Sec-Fetch-Site, à chaque requête.
Sec-Fetch-Site décrit la relation entre l’origine qui a lancé la demande et l’origine demandée : same-origin identifie une demande adressée au site lui-même, tandis que les same-sitecross-site demandes initiées par une autre origine sont identifiés. L’en-tête Origin contient l’origine initiatrice et sert de solution de repli pour les navigateurs antérieurs à Fetch Metadata.
Sec-Fetch-Site et Origin sont des en-têtes de requête interdits : le navigateur les définit, et JavaScript s’exécutant dans une page ne peut pas les remplacer ou les falsifier. Cela en fait un signal fiable pour distinguer les requêtes propres à un site des requêtes intersites, sans recourir à un jeton émis par le serveur. La protection CSRF automatique intégrée à ASP.NET Core utilise ce signal pour rejeter les soumissions de formulaires entre sites qui ne sont pas explicitement approuvées.
Protection CSRF automatique dans ASP.NET Core
ASP.NET Core fournit un middleware de protection CSRF automatique activé par défaut dans les applications créées avec WebApplication.CreateBuilder. Contrairement au système antiforgery basé sur les jetons, ce middleware n’émet pas ou ne valide pas les jetons. Au lieu de cela, il examine les Sec-Fetch-Site et Originen-têtes Fetch Metadata et consigne un verdict de validation pour la requête. Les composants qui traitent les données de formulaire soumises appliquent ce verdict, rejetant les publications de formulaire d’origine croisée qui ne sont pas explicitement approuvées.
Pour la plupart des applications, aucune modification du code n’est requise : les requêtes de navigateur identiques, les méthodes HTTP sécurisées et les clients non-navigateurs (curl, serveur à serveur, applications mobiles) passent toutes les données non affectées. L’intergiciel affecte principalement les applications qui acceptent des soumissions de formulaires inter-origines provenant d’un navigateur, comme un site qui envoie un formulaire à une API sur une autre origine. Ces scénarios doivent soit configurer CORS pour déclarer l’origine approuvée, soit refuser le point de terminaison.
Ce middleware est additif au système antiforgery basé sur les jetons. Les deux protections coexistent et peuvent être actives sur le même point de terminaison. Pour comparer les cas où chacune s’applique, consultez Interaction avec la protection anti-contrefaçon basée sur des jetons.
Fonctionnement
Pour chaque requête, l’intergiciel évalue une courte chaîne de règles pour atteindre un verdict , autorisé ou refusé. Les vérifications sont exécutées dans l’ordre, et la première correspondance l’emporte :
-
Les méthodes HTTP sécurisées sont toujours autorisées.
GET,HEAD,OPTIONSetTRACEles requêtes passent. Ceci est conforme à RFC 9110 §9.2.1 et cohérent avec la règle établie de longue date selon laquelle les points de terminaison ne doivent pas changer d’état lors deGET. -
Sec-Fetch-Site: same-originouSec-Fetch-Site: noneest autorisé. Les navigateurs modernes envoientSec-Fetch-Siteà chaque requête.same-origincouvre la navigation normale dans l’application et l’extraction, etnonecouvre les demandes lancées directement par l’utilisateur (en tapant une URL, à l’aide d’un signet). Il s’agit du chemin de code le plus courant : le trafic du navigateur le plus légitime se termine ici. - Une origine approuvée de CORS est autorisée. Si la requête comporte l’en-tête
Originet que la stratégie CORS résolue du point de terminaison autorise cette origine, la requête est autorisée. L’intergiciel détermine la stratégie de la même manière que l’intergiciel CORS : d’abord la stratégie par point de terminaison issue de[EnableCors("name")], puis la stratégie par défaut enregistrée avecAddDefaultPolicy. Consultez Autoriser les clients d’origines différentes pour en savoir plus sur les limites importantes de cette règle. - Toute autre
Sec-Fetch-Sitevaleur est refusée. QuandSec-Fetch-Siteestcross-siteousame-siteet que l’origine n’est pas considérée comme fiable via CORS, la requête est refusée. -
Aucun
Sec-Fetch-Site, maisOriginest présent : l’intergiciel compare leOriginàscheme://host[:port]obtenu à partir de la requête. S’ils correspondent, la demande est autorisée ; sinon, elle est refusée. Il s’agit du chemin de repli pour les navigateurs plus anciens que la spécification Fetch Metadata (publiée vers 2020). - Ni
Sec-Fetch-SiteniOrigin: la requête est autorisée. Les navigateurs envoient toujours au moins l’un d’entre eux sur une demande d’écriture. Une requête manquante est donc presque certainement un client non-navigateur tel quecurl, Postman, une application mobile ou un appelant serveur à serveur. Le CSRF est un vecteur d’attaque propre au navigateur ; ces requêtes passent donc au travers.
L’intergiciel enregistre ce verdict sur la demande plutôt que de mettre fin à la demande elle-même. Pour savoir comment et quand un verdict refusé se transforme en réponse HTTP 400 Bad Request , consultez validation différée.
Validation différée
L’intergiciel ne rejette pas une demande par elle-même. Au lieu de cela, il enregistre son verdict sur la requête IAntiforgeryValidationFeature( la même fonctionnalité que le système antiréforateur basé sur les jetons utilise) où un verdict refusé est enregistré comme non valide. La requête poursuit son chemin dans le pipeline. Un verdict non valide devient http 400 Bad Request uniquement lorsqu’un composant qui traite les données de formulaire l’observe. Ce report correspond à la façon dont le système basé sur les jetons se comporte déjà : le verdict est produit tôt mais appliqué au moment où un formulaire est consommé.
Les composants suivants lisent IAntiforgeryValidationFeature et rejettent une requête avec 400 - Bad Request lorsque le verdict enregistré n’est pas valide :
- Actions MVC protégées par antiforgery.
- Points de terminaison d’API minimaux qui lient un paramètre de formulaire.
- Blazor Points de terminaison SSR.
- Tout code qui lit directement le formulaire de requête, qui agit comme un backstop.
Chaque consommateur vérifie d’abord qu’un intergiciel de protection anti-CSRF a bien été exécuté avant de se fier au verdict, de sorte qu’un pipeline dépourvu de l’un ou l’autre de ces intergiciels ne produise pas de faux rejets.
Une conséquence de ce modèle est qu’un point de terminaison qui ne lit jamais les données de formulaire s’exécute même lorsque le verdict n’est pas valide. Par exemple, un point de terminaison d’API JSON qui associe le corps de la requête à des données JSON, ou un gestionnaire qui ignore le corps de la requête, n’est pas automatiquement rejeté lors d’une requête inter-origine. Le verdict est toujours enregistré dans IAntiforgeryValidationFeature pour le code qui souhaite le consulter, mais rien ne le fait respecter. La CSRF est un vecteur d’attaque lié aux formulairescookie, de sorte que les points de terminaison qui ne traitent pas de formulaires soumis par un navigateur n’ont généralement pas besoin de ce rejet. Les points de terminaison qui traitent les formulaires — Razor Pages, vues MVC, Blazor SSR et liaison de formulaires de l’API minimale — bénéficient automatiquement de cette protection.
Comportement par défaut
L’intergiciel est enregistré automatiquement WebApplication.CreateBuilder et s’exécute après l’authentification et l’autorisation. Il valide chaque requête à l’aide de l’implémentation inscrite ICsrfProtection , qui applique par défaut les règles décrites dans Le fonctionnement. L’implémentation par défaut peut être remplacée ; voir Personnalisation : implémenter ICsrfProtection. Pour désactiver entièrement le middleware, consultez Désactivation globale.
Le résultat est qu’une application minimale avec un point de terminaison de gestion de formulaire comme suit est déjà protégée :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/widgets", ([FromForm] Widget w) =>
Results.Created($"/widgets/{w.Id}", w));
app.Run();
Un navigateur effectuant une requête de même origine POST /widgets atteint normalement le point de terminaison. Un navigateur sur https://attacker.example.com qui soumet le même formulaire est rejeté avec 400 - Bad Request lorsque le point de terminaison associe les données du formulaire, avant que le corps du gestionnaire ne s’exécute. Une curl demande sans Sec-Fetch-Site ou Origin est autorisée.
Étant donné que le rejet est reporté aux consommateurs de formulaires, un point de terminaison qui ne lit pas les données de formulaire, comme une API JSON qui lie son corps à partir de JSON, n’est pas rejeté automatiquement, même sur une demande inter-origine. Le verdict est toujours enregistré sur la demande de code qui souhaite l’inspecter.
L’intergiciel s’intègre au modèle antiforgery existant :
-
API minimales : L’utilisation de
.DisableAntiforgery()pour un point de terminaison exclut ce point de terminaison des deux middlewares : celui basé sur les jetons et celui de protection CSRF. Les mêmes métadonnées (IAntiforgeryMetadata { RequiresValidation = false }) sont vérifiées par les deux. -
Contrôleurs et actions MVC :
[IgnoreAntiforgeryToken]exclut également le point de terminaison de ces deux protections.
Autoriser les clients d’origines différentes
Le scénario le plus courant nécessitant une action est celui d’un client web qui soumet un formulaire inter-origine, par exemple un site situé à https://app.contoso.com qui envoie un formulaire vers une API sur https://api.contoso.com. Ces envois de formulaire sont refusés par défaut parce que Sec-Fetch-Site est same-site ou cross-site plutôt que same-origin, et le composant consommateur du formulaire applique ce verdict avec un 400 - Bad Request.
L’intergiciel CSRF n’introduit pas sa propre liste de confiance. Il réutilise la même stratégie CORS que le middleware CORS détermine pour le point de terminaison : si cette stratégie autorise la valeur Origin de la requête, le middleware CSRF enregistre une décision d’autorisation pour la requête.
La stratégie est sélectionnée pour chaque point de terminaison dans cet ordre :
-
[EnableCors("api")](MVC) ou.RequireCors("api")(API minimale) → la stratégie nommée"api". - Aucune métadonnée CORS sur le point de terminaison → utilisation de la stratégie par défaut enregistrée avec
AddDefaultPolicy. - Aucune stratégie correspondante (stratégie nommée non enregistrée, aucune stratégie par défaut ou
services.AddCors()jamais appelée) → aucune relation de confiance dérivée de CORS. Le middleware applique ensuite les règlesSec-Fetch-Siteet les règles Origin-vs-Host.
Exemple minimal utilisant une stratégie par défaut et un point de terminaison d’API minimal :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
policy.WithOrigins("https://app.contoso.com")
.AllowAnyHeader()
.AllowAnyMethod());
});
var app = builder.Build();
app.UseCors();
app.MapPost("/widgets", ([FromForm] Widget w) =>
Results.Created($"/widgets/{w.Id}", w));
app.Run();
Pour une stratégie nommée sur un seul point de terminaison :
app.MapPost("/widgets", ([FromForm] Widget w) => Results.Created($"/widgets/{w.Id}", w))
.RequireCors("api");
Avertissement
AllowAnyOrigin n’est intentionnellement pas honoré comme signal de confiance CSRF.
AllowAnyOrigin signifie que « n’importe quel navigateur peut lire cette ressource », ce qui est une question distincte de « toute origine peut modifier l’état pour le compte de l’utilisateur ». Considérer AllowAnyOrigin comme fiable transformerait ce middleware en mécanisme sans effet pour les écritures inter-origines. Les applications qui ont besoin d’une politique CORS d’accès public en lecture, combinée à des opérations d’écriture protégées contre les attaques CSRF, doivent répertorier explicitement les origines d’écriture de confiance avec WithOrigins, ou exclure les points de terminaison d’écriture si elles ne s’appuient pas sur une authentification basée sur cookie.
[DisableCors] sur un point de terminaison n’est pas une exclusion de la protection CSRF. Cela saute l’étape de confiance dérivée de CORS, et la requête doit toujours respecter les règles Sec-Fetch-Site et de correspondance entre Origin et Host. Pour désactiver la protection CSRF, consultez Désactiver un point de terminaison.
Pour plus d’informations sur la configuration de CORS lui-même ( , , et le reste de l’API du générateur de stratégies), consultez AddCors. AddDefaultPolicyAddPolicyWithOrigins
Exclusion d’un point de terminaison
Si un point de terminaison n’est pas accessible par un navigateur ou est sécurisé par un mécanisme autrecookie que celui-ci, tel qu’un jeton du porteur ou une clé API, désactivez-le individuellement plutôt que de désactiver le middleware globalement.
API minimales — appelez DisableAntiforgery sur le point de terminaison ou le groupe :
app.MapPost("/api/webhook", (WebhookPayload p) => Results.Accepted())
.DisableAntiforgery();
Contrôleurs MVC — appliquez [IgnoreAntiforgeryToken] à l’action ou au contrôleur :
[ApiController]
[Route("api/[controller]")]
[IgnoreAntiforgeryToken]
public class WebhookController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] WebhookPayload payload) => Accepted();
}
L’une ou l’autre approche ajoute IAntiforgeryMetadata { RequiresValidation = false } au point de terminaison, que l’intergiciel CSRF respecte en ignorant la validation.
Avertissement
La protection CSRF sur un point de terminaison ne doit être désactivée que lorsque ce point de terminaison n’est pas vulnérable aux attaques CSRF, par exemple s’il ne peut pas être appelé depuis un navigateur ou s’il est sécurisé par une authentification non-cookie, telle que des jetons porteurs ou des clés API. Ne désactivez pas la protection CSRF sur les points de terminaison accessibles par navigateur qui s’appuient sur des cookies pour l’authentification.
Désactivation globale
L’intergiciel peut être désactivé sur l’ensemble de l’application à l’aide de la clé de DisableCsrfProtection configuration. Il s’agit d’une trappe d’échappement : préférez les opt-outs par point de terminaison.
Dans appsettings.json :
{
"DisableCsrfProtection": true
}
Ou en tant que variable d’environnement :
ASPNETCORE_DisableCsrfProtection=true
Lorsque cette clé est définie true, WebApplication ignore l’inscription du middleware dans le pipeline. Le service ICsrfProtection reste enregistré, de sorte que tout ce qui le résout directement continue de fonctionner.
Avertissement
L’intergiciel CSRF automatique répond également à l’exigence de protection anti-CSRF pour les points de terminaison qui nécessitent une validation, même lorsqu’une application n’appelle pas app.UseAntiforgery(). Si une application s’appuie sur l’antifalsification mais n’appelle pas app.UseAntiforgery(), la désactivation globale de l’intergiciel CSRF, ou l’exécution sur un hôte qui n’est pas construit avec WebApplication, dans lequel l’intergiciel n’est pas injecté, laisse ces points de terminaison sans intergiciel antifalsification. Une requête adressée à un tel point de terminaison génère alors une exception. Appelez app.UseAntiforgery() dans cette configuration.
Support des navigateurs
Sec-Fetch-Site est pris en charge par toutes les versions actuelles des navigateurs chromium, Firefox et Safari. Pour obtenir une table de compatibilité faisant autorité, consultez la référence MDN pour Sec-Fetch-Site.
Les navigateurs plus anciens, antérieurs à Fetch Metadata, n’envoient pas Sec-Fetch-Site. Pour ces clients, le middleware se rabat sur la comparaison de l’en-tête Origin avec le schéma et l’hôte de la requête. Les navigateurs envoient Origin sur les requêtes d’écriture inter-origines depuis de nombreuses années ; ce mécanisme de repli couvre donc la quasi-totalité du trafic des anciens navigateurs.
Clients non navigateur — curl, Postman, applications mobiles, appelants de serveur à serveur — n’envoient généralement ni Sec-Fetch-Site ni Origin. Ces demandes sont autorisées, car CSRF est un vecteur d’attaque par navigateur uniquement qui dépend du navigateur qui attache automatiquement des informations d’identification ambiantes telles que des cookies. Un client non-navigateur qui souhaite attaquer l’API n’a pas besoin de CSRF ; il peut simplement appeler l’API directement avec les informations d’identification qu’il possède.
Personnalisation : implémentation de ICsrfProtection
La logique de décision se trouve derrière une interface à méthode unique :
namespace Microsoft.AspNetCore.Antiforgery;
public interface ICsrfProtection
{
ValueTask<CsrfProtectionResult> ValidateAsync(HttpContext context);
}
Pour remplacer l’implémentation par défaut, enregistrez un singleton dans DI. Étant donné que l’infrastructure utilise TryAddSingleton, un appel explicite AddSingleton remplace la valeur par défaut :
builder.Services.AddSingleton<ICsrfProtection, AllowlistCsrfProtection>();
Une implémentation personnalisée est utile lorsque le modèle d’approbation ne correspond pas à CORS, par exemple lorsqu’une liste d’autorisation fixe d’origines de partenaires est recommandée ou si des règles plus strictes sont nécessaires :
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http;
public sealed class AllowlistCsrfProtection : ICsrfProtection
{
private static readonly HashSet<string> SafeMethods =
new(StringComparer.OrdinalIgnoreCase) { "GET", "HEAD", "OPTIONS", "TRACE" };
private static readonly HashSet<string> TrustedOrigins =
new(StringComparer.OrdinalIgnoreCase)
{
"https://app.contoso.com",
"https://admin.contoso.com",
};
public ValueTask<CsrfProtectionResult> ValidateAsync(HttpContext context)
{
if (SafeMethods.Contains(context.Request.Method))
{
return ValueTask.FromResult(CsrfProtectionResult.Allowed());
}
var origin = context.Request.Headers.Origin.ToString();
var allowed = !string.IsNullOrEmpty(origin) && TrustedOrigins.Contains(origin);
return ValueTask.FromResult(
allowed ? CsrfProtectionResult.Allowed() : CsrfProtectionResult.Denied());
}
}
L’intergiciel continue de respecter .DisableAntiforgery() / [IgnoreAntiforgeryToken] quelle que soit l’implémentation enregistrée ; la désactivation est gérée par l’intergiciel lui-même avant que ValidateAsync ne soit appelé.
Interaction avec l’antiforgery basée sur des jetons
Les deux défenses CSRF ciblent des couches différentes et sont conçues pour coexister. Ils partagent également la même fonctionnalité de requête : les deux enregistrent leur résultat sur IAntiforgeryValidationFeature, et les consommateurs de formulaires appliquent le verdict présent.
| Aspect | Basé sur un jeton AntiforgeryMiddleware |
Intergiciel de protection CSRF automatique |
|---|---|---|
| Présenté | ASP.NET Core 2.0+ | .NET 11 |
| Activation | Activation via app.UseAntiforgery() (ou implicitement via AddMvc / MapRazorPages / AddRazorComponents) |
Injecté automatiquement par WebApplication.CreateBuilder |
| Valide | Jeton synchronisé (champ de formulaire + cookie paire) |
Sec-Fetch-Site
/
Origin En-têtes |
| Requires | Vue d’ensemble de la protection des données ASP.NET Core pour le chiffrement de jetons | Aucun jeton, aucun état |
| Étendue du navigateur | Tous les navigateurs qui envoient des cookies | Tous les navigateurs modernes ; mécanisme de secours Origin pour les anciens navigateurs |
| Désactivation pour chaque point de terminaison | .DisableAntiforgery() / [IgnoreAntiforgeryToken] |
Identique : les deux respectent les mêmes métadonnées |
Le middleware basé sur les jetons protège spécifiquement contre le modèle d’attaque CSRF classique dans lequel un site malveillant déclenche un formulaire POST sur un site vulnérable à l’aide des cookies ambiants de l’utilisateur. L’intergiciel CSRF automatique résout la même menace au niveau de la couche HTTP à l’aide des métadonnées fournies par le navigateur. Les deux peuvent être actifs sur le même point de terminaison, et de nombreuses applications bénéficieront de la défense en profondeur :
- Razor Les pages, MVC et Blazor les applications SSR qui utilisent déjà le système de jetons obtiennent une vérification basée sur l’en-tête qui s’exécute avant la validation du jeton, sans modifier le flux de jetons.
-
Les applications API minimales qui lient des formulaires obtiennent une valeur par défaut utile sans avoir besoin d’appeler
app.UseAntiforgery()ou de threadrIAntiforgeryvia des points de terminaison. - Les API appelées depuis des SPA inter-origines peuvent s’appuyer sur ce middleware combiné à une liste d’autorisation CORS et se passer entièrement du système de jetons si l’API ne sert jamais de formulaires HTML.
L’intergiciel CSRF automatique remplace le système basé sur les jetons dans de nombreux scénarios, car les deux protègent les mêmes points de terminaison de gestion des formulaires. Conservez le système basé sur les jetons quand :
- L’application doit prendre en charge les navigateurs qui n’envoient
Sec-Fetch-Sitepas . Consultez la compatibilité du navigateur. - L’application utilise IAntiforgeryAdditionalDataProvider pour faire transiter des données supplémentaires dans le jeton.
- Une vérification de sécurité ou une exigence de conformité spécifie la défense des jetons en tant que couche indépendante.
Pour plus d’informations sur le système basé sur les jetons, notamment l’intégration de formulaires, les flux AJAX, la configuration via AntiforgeryOptionset IAntiforgery les API, consultez Antiforgery dans ASP.NET Core.
La validation des jetons est prioritaire
Lorsqu’une application appelle app.UseAntiforgery(), l’intergiciel basé sur les jetons s’exécute après l’intergiciel CSRF automatique. L’intergiciel de jeton efface tout verdict enregistré par l’intergiciel CSRF et le remplace par le résultat de la validation du jeton. Le résultat du token fait foi :
- Une demande indiquant que l’intergiciel CSRF marqué comme non valide devient valide s’il contient un jeton valide.
- Une requête autorisée par le middleware CSRF est considérée comme non valide si son jeton est manquant ou invalide.
Cet ordre signifie que les applications qui utilisent le système de jetons conservent le même comportement de bout en bout qu’avant l’existence du middleware automatique, tandis que les applications qui n’utilisent pas de jetons s’en remettent au verdict du middleware CSRF.
Blazor rendu statique côté serveur
Blazor les endpoints de rendu statique côté serveur (SSR) s’inscrivent dans le même modèle de traitement différé. Le point de terminaison Razor Components se fie au verdict enregistré sur IAntiforgeryValidationFeature par l’intergiciel en amont et ne renvoie 400 - Bad Request pour un envoi de formulaire que lorsque ce verdict est invalide. Le point de terminaison ne valide plus la requête elle-même.
Le comportement dépend du middleware exécuté :
- Les applications qui appellent
app.UseAntiforgery()sont inchangées. L’intergiciel à base de jetons valide chaque requête, et des jetons antifalsification sont générés pour les formulaires générés comme auparavant. - Les applications qui n’appellent pas
app.UseAntiforgery()sont plutôt protégées par le middleware CSRF automatique. Dans cette configuration, le point de terminaison ne génère pas de jeton antifalsification, car aucun middleware de validation de jeton n’est présent pour valider un jeton lors d’une requête ultérieure.
Il s’agit d’un changement de comportement pour le SSR statique : auparavant, app.UseAntiforgery() était supprimé ; désormais, ces éléments sont protégés par le middleware CSRF au lieu d’être laissés sans protection, et ils n’émettent plus de jetons anti-CSRF. Pour obtenir des conseils sur la migration, consultez Migrer de ASP.NET Core dans .NET 10 à ASP.NET Core dans .NET 11. Pour l’annonce officielle du changement cassant, consultez Blazor le rendu côté serveur délègue la validation antifalsification au middleware.
Troubleshooting
Symptôme : Les requêtes de même origine effectuées depuis un navigateur réussissent, mais les envois de formulaires entre origines différentes renvoient 400 - Bad Request sans corps de réponse.
Cause : L’intergiciel CSRF a enregistré un verdict invalide pour la requête inter-origine, et un composant de traitement de formulaire, tel qu’une action MVC, une liaison de formulaire d’API minimale ou une soumission de formulaire SSR Blazor, a appliqué ce verdict au moyen de 400 - Bad Request. Il s’agit du comportement par défaut attendu pour les points de terminaison qui traitent les formulaires.
Résolution: Choisissez l’une des options suivantes, en fonction du scénario :
- Si l’origine appelante est connue et approuvée, autorisez-la via CORS.
- Si le point de terminaison n’est pas accessible depuis le navigateur ou utilise une authentification non-cookie, excluez-le avec
.DisableAntiforgery()ou[IgnoreAntiforgeryToken]. - Si l’ensemble de l’application doit être exclue (par exemple, pendant une fenêtre de migration), désactivez-la globalement.
Diagnostic : Le middleware enregistre chaque verdict invalide au niveau Debug, dans la catégorie Microsoft.AspNetCore.Antiforgery.CsrfProtectionMiddleware, sous le nom d’événement CsrfValidationFailed. Activer la Debug journalisation pour cette catégorie dans appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Antiforgery.CsrfProtectionMiddleware": "Debug"
}
}
}
Un verdict enregistré apparaît ensuite dans le journal comme suit :
dbug: Microsoft.AspNetCore.Antiforgery.CsrfProtectionMiddleware[1]
Cross-origin CSRF protection marked request POST /widgets from origin 'https://attacker.example.com' as invalid.
Reproduction en local : Utilisez curl avec un en-tête Origin explicite pour simuler une requête de navigateur inter-origine vers un point de terminaison de formulaire :
curl -i -X POST https://localhost:{PORT}/widgets \
-H "Origin: https://attacker.example.com" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "name=test"
Remplacez {PORT} par le port HTTPS local de l’application. On observe le 400 - Bad Request, car le point de terminaison effectue la liaison avec le formulaire, ce qui impose le verdict enregistré. Un point de terminaison qui n’est pas un formulaire renvoie sa réponse habituelle, puisque rien ne consulte le verdict. Sans l’en-tête Origin , la même requête est autorisée, car curl elle n’envoie Sec-Fetch-Site pas non plus et une demande avec aucun en-tête n’est traité comme un client non-navigateur.
Le système antiforgery basé sur les jetons décrit dans le reste de cet article précède ce middleware et reste disponible. Pour la plupart des applications, la protection automatique est suffisante pour elle-même. Pour obtenir des conseils sur le moment où conserver le système basé sur les jetons et comment migrer, consultez Migrer de ASP.NET Core dans .NET 10 à ASP.NET Core dans .NET 11.
Anti-contrefaçon dans ASP.NET Core
Avertissement
ASP.NET Core implémente l'anti-contrefaçon à l'aide de ASP.NET Core Data Protection. La pile de protection des données doit être configurée pour fonctionner dans une ferme de serveurs. Pour plus d'informations, voir Configuration de la protection des données.
Le middleware anti-contrefaçon est ajouté au conteneur d'injection de dépendances lorsque l'une des API suivantes est appelée dans Program.cs :
Pour plus d’informations, consultez Anti-falsification avec des API minimales.
FormTagHelper injecte des jetons antifalsification dans les éléments de formulaire HTML. Le balisage suivant dans un fichier Razor génère automatiquement des jetons anti-contrefaçon :
<form method="post">
<!-- ... -->
</form>
De même, IHtmlHelper.BeginForm génère des jetons anti-contrefaçon par défaut si la méthode du formulaire n'est pas GET.
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML se produit lorsque la balise <form> contient l'attribut method="post" et que l'une des conditions suivantes est vraie :
- L'attribut action est vide (
action=""). - L'attribut action n'est pas fourni (
<form method="post">).
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML peut être désactivée :
Désactivez explicitement les jetons anti-contrefaçon avec l'attribut
asp-antiforgery:<form method="post" asp-antiforgery="false"> <!-- ... --> </form>L’élément de formulaire est exclu des Tag Helpers à l’aide du symbole d’exclusion ! du Tag Helper :
<!form method="post"> <!-- ... --> </!form>Supprimez le
FormTagHelperde la vue. LeFormTagHelperpeut être supprimé d'une vue en ajoutant la directive suivante à la vue Razor :@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
Note
Razor Pages sont automatiquement protégées contre XSRF/CSRF. Pour plus d'informations, voir XSRF/CSRF Razor et Pages.
L'approche la plus courante pour se protéger contre les attaques CSRF consiste à utiliser le Synchronizer Token Pattern (STP). STP est utilisé lorsque l'utilisateur demande une page avec des données de formulaire :
- Le serveur envoie un jeton associé à l’identité de l’utilisateur actuel au client.
- Le client renvoie le jeton au serveur pour vérification.
- Si le serveur reçoit un jeton qui ne correspond pas à l’identité de l’utilisateur authentifié, la demande est rejetée.
Le jeton est unique et imprévisible. Le jeton peut également être utilisé pour assurer le bon séquencement d'une série de requêtes (par exemple, assurer la séquence de requête de : page 1 > page 2 > page 3). Tous les formulaires des modèles ASP.NET Core MVC et Pages Razor génèrent des jetons anti-contrefaçon. La paire d'exemples de vue suivante génère des jetons anti-contrefaçon :
<form asp-action="Index" asp-controller="Home" method="post">
<!-- ... -->
</form>
@using (Html.BeginForm("Index", "Home"))
{
<!-- ... -->
}
Ajoutez explicitement un jeton anti-contrefaçon à un élément <form> sans utiliser les Tag Helpers avec le helper HTML @Html.AntiForgeryToken :
<form asp-action="Index" asp-controller="Home" method="post">
@Html.AntiForgeryToken()
<!-- ... -->
</form>
Dans chacun des cas précédents, ASP.NET Core ajoute un champ de formulaire masqué similaire à l'exemple suivant :
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
ASP.NET Core inclut trois filtres pour travailler avec des jetons anti-contrefaçon :
Protection anti-falsification avec AddControllers
L'appel AddControllers n'active pas les jetons anti-contrefaçon. AddControllersWithViews doit être appelé afin d’assurer une prise en charge intégrée des jetons antifalsification.
Plusieurs onglets de navigateur et le modèle de jeton de synchronisation
Plusieurs onglets ouverts avec des comptes utilisateurs différents, ou un onglet ouvert comme utilisateur anonyme, ne sont pas pris en charge.
Configurer l'anti-contrefaçon avec AntiforgeryOptions
Personnalisez AntiforgeryOptions dans le fichier de l’application Program :
builder.Services.AddAntiforgery(options =>
{
// Set Cookie properties using CookieBuilder properties†.
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.SuppressXFrameOptionsHeader = false;
});
Définissez les propriétés cookie anti-contrefaçon à l'aide des propriétés de la classe CookieBuilder, comme indiqué dans le tableau suivant.
| Option | Description |
|---|---|
| Cookie | Détermine les paramètres utilisés pour créer les cookies anti-contrefaçon. |
| FormFieldName | Nom du champ de formulaire masqué utilisé par le système anti-falsification pour générer des jetons anti-falsification dans les vues. |
| HeaderName | Le nom de l'en-tête utilisé par le système anti-contrefaçon. Si null, le système ne prend en compte que les données du formulaire. |
| SuppressXFrameOptionsHeader | Spécifie s'il faut supprimer la génération de l'en-tête X-Frame-Options. Par défaut, l'en-tête est généré avec une valeur de "SAMEORIGIN". La valeur par défaut est false. |
Certains navigateurs n’autorisent pas les points de terminaison non sécurisés à définir des cookies avec un indicateur « sécurisé » ou à remplacer des cookies dont l’indicateur « sécurisé » est défini (pour plus d’informations, consultez Dépréciation de la modification des cookies « sécurisés » à partir d’origines non sécurisées). Étant donné que le mélange de points de terminaison sécurisés et non sécurisés est un scénario courant dans les applications, ASP.NET Core assouplit la restriction relative à la politique de sécurité sur certains cookies, tels que l’antiforgery cookie, en définissant l'cookie de SecurePolicy sur CookieSecurePolicy.None. Même si un utilisateur malveillant vole un jeton anti-contrefaçon cookie, il doit également voler le jeton généralement envoyé via un champ de formulaire (plus courant) ou un en-tête de requête distinct (moins courant), ainsi que les informations d’authentification cookie.
Cookies liés à l’authentification ou à l’autorisation utilisent une stratégie plus forte que CookieSecurePolicy.None.
Si vous le souhaitez, vous pouvez sécuriser l’antifalsification cookie dans des environnements non-Development à l’aide du protocole SSL (Secure Sockets Layer), uniquement via HTTPS, avec le paramètre AntiforgeryOptions.Cookie suivant dans le fichier Program de l'application :
if (!builder.Environment.IsDevelopment())
{
builder.Services.AddAntiforgery(o =>
{
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
Pour plus d’informations, voir CookieAuthenticationOptions.
Générez des jetons anti-contrefaçon avec IAntiforgery
IAntiforgery fournit l'API pour configurer les fonctionnalités anti-contrefaçon.
IAntiforgery peut être demandé en Program.cs à l’aide de WebApplication.Services. L’exemple suivant utilise l’intergiciel depuis la page d’accueil de l’application pour générer un jeton anticontrefaçon et l’envoyer dans la réponse sous forme de cookie:
app.UseRouting();
app.UseAuthorization();
var antiforgery = app.Services.GetRequiredService<IAntiforgery>();
app.Use((context, next) =>
{
var requestPath = context.Request.Path.Value;
if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
|| string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
{
var tokenSet = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
new CookieOptions { HttpOnly = false });
}
return next(context);
});
L'exemple précédent définit un cookie nommé XSRF-TOKEN. Le client peut le lire cookie et fournir sa valeur sous forme d'en-tête attaché aux requêtes AJAX. Par exemple, Angular inclut une protection XSRF intégrée qui lit un cookie nommé XSRF-TOKEN par défaut.
Exiger une validation anti-contrefaçon
Le filtre d'action ValidateAntiForgeryToken peut être appliqué à une action individuelle, à un contrôleur ou globalement. Les requêtes adressées aux actions auxquelles ce filtre est appliqué sont bloquées, sauf si la requête inclut un jeton anti-contrefaçon valide :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
// ...
return RedirectToAction();
}
L'attribut ValidateAntiForgeryToken requiert un jeton pour les requêtes aux méthodes d'action qu'il marque, y compris les requêtes HTTP GET. Si l'attribut ValidateAntiForgeryToken est appliqué sur les contrôleurs de l'application, il peut être remplacé par l'attribut IgnoreAntiforgeryToken.
Valider automatiquement les jetons anti-contrefaçon pour les méthodes HTTP non sécurisées uniquement
Au lieu d'appliquer largement l'attribut ValidateAntiForgeryToken, puis de le remplacer par des attributs IgnoreAntiforgeryToken, l'attribut AutoValidateAntiforgeryToken peut être utilisé. Cet attribut fonctionne de la même manière que l'attribut ValidateAntiForgeryToken, sauf qu'il ne nécessite pas de jetons pour les requêtes effectuées à l'aide des méthodes HTTP suivantes :
- GET
- EN-TÊTE
- OPTIONS
- TRACE
Nous vous recommandons d'utiliser AutoValidateAntiforgeryToken largement pour les scénarios non API. Cet attribut garantit que les actions POST sont protégées par défaut. L'alternative consiste à ignorer les jetons anti-contrefaçon par défaut, sauf si ValidateAntiForgeryToken cela s'applique à des méthodes d'action individuelles. Dans ce scénario, il est plus probable qu'une méthode d'action POST ne soit pas protégée par erreur, laissant l'application vulnérable aux attaques CSRF. Tous les POST doivent envoyer le jeton anti-contrefaçon.
Les API n'ont pas de mécanisme automatique pour envoyer la non-cookie partie du jeton. L'implémentation dépend probablement de l'implémentation du code client. Quelques exemples sont présentés ci-dessous :
Exemple au niveau de la classe :
[AutoValidateAntiforgeryToken]
public class HomeController : Controller
Exemple global :
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
Remplacer les attributs anti-falsification globaux ou au niveau du contrôleur
Le filtre IgnoreAntiforgeryToken est utilisé pour éliminer le besoin d'un jeton anti-contrefaçon pour une action (ou contrôleur) donnée. Lorsqu'il est appliqué, ce filtre prend le pas sur les filtres ValidateAntiForgeryToken et AutoValidateAntiforgeryToken spécifiés à un niveau supérieur (au niveau global ou sur un contrôleur).
[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
// ...
return RedirectToAction();
}
Actualiser les jetons après l'authentification
Les jetons doivent être actualisés une fois l’utilisateur authentifié en redirigeant ce dernier vers une vue ou une page Razor Pages.
JavaScript, AJAX et SPA
Dans les applications HTML traditionnelles, les jetons anti-contrefaçon sont transmis au serveur à l'aide de champs de formulaire masqués. Dans les applications et les SPA modernes basés sur JavaScript, de nombreuses demandes sont effectuées par programmation. Ces requêtes AJAX peuvent utiliser d’autres techniques, telles que des en-têtes de demande ou des cookies, pour envoyer le jeton.
Si les cookies sont utilisés pour stocker des jetons d’authentification et pour authentifier les demandes d’API sur le serveur, CSRF est un problème potentiel. Si le stockage local est utilisé pour stocker le jeton, la vulnérabilité CSRF peut être atténuée car les valeurs du stockage local ne sont pas envoyées automatiquement au serveur à chaque requête. L'utilisation du stockage local pour stocker le jeton anti-contrefaçon sur le client et l'envoi du jeton en tant qu'en-tête de requête est une approche recommandée.
Blazor
Pour plus d’informations, consultez Authentification et autorisation ASP.NET Core Blazor.
JavaScript
En utilisant JavaScript avec des vues, le jeton peut être créé à l'aide d'un service à partir de la vue. Injectez le service IAntiforgery dans la vue et appelez GetAndStoreTokens :
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
@{
ViewData["Title"] = "JavaScript";
var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
<input id="RequestVerificationToken" type="hidden" value="@requestToken" />
<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>
@section Scripts {
<script>
document.addEventListener("DOMContentLoaded", () => {
const resultElement = document.getElementById("result");
document.getElementById("button").addEventListener("click", async () => {
const response = await fetch("@Url.Action("FetchEndpoint")", {
method: "POST",
headers: {
RequestVerificationToken:
document.getElementById("RequestVerificationToken").value
}
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
});
});
</script>
}
L'exemple précédent utilise JavaScript pour lire la valeur du champ masqué pour l'en-tête AJAX POST.
Cette approche élimine le besoin de traiter directement la définition des cookies par le serveur ou leur lecture par le client. Cependant, lorsque l’injection du service IAntiforgery n’est pas possible, utilisez JavaScript pour accéder aux jetons dans les cookies :
- Accéder aux jetons dans une requête supplémentaire au serveur, généralement
same-origin. - Utilisez le contenu de cookie pour créer un en-tête avec la valeur du jeton.
En supposant que le script envoie le jeton dans un en-tête de requête appelé X-XSRF-TOKEN, configurez le service de protection contre les falsifications de requête pour rechercher l’en-tête X-XSRF-TOKEN :
builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
L'exemple suivant ajoute un point de terminaison protégé qui écrit le jeton de requête dans un fichier JavaScript lisible cookie :
app.UseAuthorization();
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
var tokens = forgeryService.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!,
new CookieOptions { HttpOnly = false });
return Results.Ok();
}).RequireAuthorization();
L'exemple suivant utilise JavaScript pour effectuer une requête AJAX afin d'obtenir le jeton et effectuer une autre requête avec l'en-tête approprié :
var response = await fetch("/antiforgery/token", {
method: "GET",
headers: { "Authorization": authorizationToken }
});
if (response.ok) {
// https://developer.mozilla.org/docs/web/api/document/cookie
const xsrfToken = document.cookie
.split("; ")
.find(row => row.startsWith("XSRF-TOKEN="))
.split("=")[1];
response = await fetch("/JavaScript/FetchEndpoint", {
method: "POST",
headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
Note
Lorsque le jeton anti-contrefaçon est fourni dans l’en-tête de requête et dans la charge utile du formulaire, seul le jeton de l’en-tête est validé.
Anti-contrefaçon avec des API minimales
Appelez AddAntiforgery et UseAntiforgery(IApplicationBuilder) pour inscrire des services d’anti-falsification dans DI. Les jetons d’antifalsification sont utilisés pour atténuer les attaques de falsification de requête intersites.
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
app.MapGet("/", () => "Hello World!");
app.Run();
L’intergiciel d’antifalsification :
- Est-ce que not court-circuite l’exécution du reste du pipeline de requête.
- Définit l’élément IAntiforgeryValidationFeature dans la requête actuelle HttpContext.Features.
Le jeton d’antifalsification n’est validé que si :
- Le point de terminaison contient des métadonnées implémentant IAntiforgeryMetadata où
RequiresValidation=true. - La méthode HTTP associée au point de terminaison est une méthode HTTP appropriée de type POST, PUT ou PATCH.
- La requête est associée à un point de terminaison valide.
L’intergiciel Antiforgery ne court-circuite pas le pipeline de requête. Le code du point de terminaison s'exécute toujours, même si la validation du jeton échoue. Pour observer le résultat de la validation du jeton, résolvez IAntiforgeryValidationFeature à partir de HttpContext.Features et inspectez soit sa propriété IsValid ou Error pour les détails d’échec. Cette approche est utile lorsque les points de terminaison nécessitent une gestion personnalisée pour une validation anti-spoofing échouée.
Remarque : lorsqu’il est activé manuellement, l’intergiciel d’antifalsification doit s’exécuter après l’authentification et l’intergiciel d’autorisation pour empêcher la lecture des données de formulaire lorsque l’utilisateur n’est pas authentifié.
Par défaut, les API minimales qui acceptent les données de formulaire nécessitent une validation de jeton antiforgery et échouent avant d’exécuter le code de l’application si la validation antiforgery n’a pas réussi.
Considérez la méthode GenerateForm suivante :
public static string GenerateForm(string action,
AntiforgeryTokenSet token, bool UseToken=true)
{
string tokenInput = "";
if (UseToken)
{
tokenInput = $@"<input name=""{token.FormFieldName}""
type=""hidden"" value=""{token.RequestToken}"" />";
}
return $@"
<html><body>
<form action=""{action}"" method=""POST"" enctype=""multipart/form-data"">
{tokenInput}
<input type=""text"" name=""name"" />
<input type=""date"" name=""dueDate"" />
<input type=""checkbox"" name=""isCompleted"" />
<input type=""submit"" />
</form>
</body></html>
";
}
Le code précédent comporte trois arguments, l’action, le jeton d’anti-contrefaçon et un bool indiquant si le jeton doit être utilisé.
Considérez l’exemple de suivant :
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
// Pass token
app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
return Results.Content(MyHtml.GenerateForm("/todo", token), "text/html");
});
// Don't pass a token, fails
app.MapGet("/SkipToken", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
return Results.Content(MyHtml.GenerateForm("/todo",token, false ), "text/html");
});
// Post to /todo2. DisableAntiforgery on that endpoint so no token needed.
app.MapGet("/DisableAntiforgery", (HttpContext context, IAntiforgery antiforgery) =>
{
var token = antiforgery.GetAndStoreTokens(context);
return Results.Content(MyHtml.GenerateForm("/todo2", token, false), "text/html");
});
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));
app.MapPost("/todo2", ([FromForm] Todo todo) => Results.Ok(todo))
.DisableAntiforgery();
app.Run();
class Todo
{
public required string Name { get; set; }
public bool IsCompleted { get; set; }
public DateTime DueDate { get; set; }
}
public static class MyHtml
{
public static string GenerateForm(string action,
AntiforgeryTokenSet token, bool UseToken=true)
{
string tokenInput = "";
if (UseToken)
{
tokenInput = $@"<input name=""{token.FormFieldName}""
type=""hidden"" value=""{token.RequestToken}"" />";
}
return $@"
<html><body>
<form action=""{action}"" method=""POST"" enctype=""multipart/form-data"">
{tokenInput}
<input type=""text"" name=""name"" />
<input type=""date"" name=""dueDate"" />
<input type=""checkbox"" name=""isCompleted"" />
<input type=""submit"" />
</form>
</body></html>
";
}
}
Dans le code précédent, les publications vers :
-
/todonécessitent un jeton d’antifalsification valide. -
/todo2ne nécessitent pas de jeton d’antifalsification valide, car DisableAntiforgery est appelé.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));
app.MapPost("/todo2", ([FromForm] Todo todo) => Results.Ok(todo))
.DisableAntiforgery();
Avertissement
L’appel .DisableAntiforgery() désactive la protection contre la falsification de requête intersite (CSRF) pour le point de terminaison. Cela doit être utilisé uniquement lorsqu’un point de terminaison n’est pas vulnérable aux attaques CSRF, telles que :
- Points de terminaison qui ne sont pas appelants à partir d’un navigateur (par exemple, des API internes)
- Points de terminaison sécurisés sans authentification basée sur cookie (par exemple, jetons d'accès ou clés API)
- Points de terminaison internes ou d’infrastructure qui ne s’appuient pas sur les cookies utilisateur
Ne désactivez pas la validation antiréfacteur pour les points de terminaison accessibles par le navigateur qui s’appuient sur des cookies pour l’authentification ou qui traitent les données de formulaire soumises par l’utilisateur, car cela expose votre application aux attaques CSRF.
Un POST vers :
-
/todoprovenant du formulaire généré par le point de terminaison/réussit comme le jeton d’antifalsification est valide. -
/todoprovenant du formulaire généré par le/SkipTokenéchoue, car l’antifalsification n’est pas incluse. -
/todo2provenant du formulaire généré par le point de terminaison/DisableAntiforgeryréussit comme le jeton d’antifalsification n’est pas requis.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));
app.MapPost("/todo2", ([FromForm] Todo todo) => Results.Ok(todo))
.DisableAntiforgery();
Lorsqu’un formulaire est envoyé sans jeton d’antifalsification valide :
- Dans l’environnement
Development, une exception est levée. - Dans l’environnement
Production, un message est enregistré.
Limitations des méthodes HTTP et interaction HttpMethodOverrideMiddleware
Pour le pipeline antifalsification basé sur un intergiciel, AntiforgeryMiddleware et UseAntiforgery() valident les jetons antifalsification uniquement pour les requêtes HTTP POST, PUT et PATCH. D’autres méthodes HTTP, telles que DELETE, ne sont pas validées automatiquement.
Pour valider des jetons antiforgery pour d’autres méthodes HTTP, résolvez IAntiforgery à partir de l’id de domaine et appelez ValidateRequestAsync ou IsRequestValidAsync explicitement :
app.MapDelete("/item/{id}", async (int id, IAntiforgery antiforgery, HttpContext context) =>
{
await antiforgery.ValidateRequestAsync(context);
// Process the DELETE request
});
Avertissement
Lorsqu’elle HttpMethodOverrideMiddleware est configurée avec FormFieldName (mode formulaire-champ) et placée avant AntiforgeryMiddleware, une requête POST peut être remplacée par DELETE (ou une autre méthode non validée). Étant donné que AntiforgeryMiddleware valide uniquement POST, PUT et PATCH, la demande substituée contourne la validation antiforgery.
Pour protéger ces points de terminaison :
- Préférez placer
HttpMethodOverrideMiddlewareune fois la validation antiforgerie terminée lorsque votre pipeline l’autorise. - Évitez les surcharges de champs de formulaire pour les points de terminaison qui s’appuient sur une validation anti-falsification.
- Si la redéfinition du champ de formulaire doit s’exécuter en premier, validez explicitement le jeton anti-contrefaçon à l’aide de
IAntiforgery.ValidateRequestAsync.
Pour plus d’informations sur la configuration, consultez ASP.NET Core intergiciel.
Authentification Windows et cookies anti-contrefaçon
Lors de l’utilisation de l’authentification Windows, les points de terminaison d’application doivent être protégés contre les attaques CSRF de la même manière que pour les cookies. Le navigateur envoie implicitement le contexte d'authentification au serveur et les terminaux doivent être protégés contre les attaques CSRF.
Étendre l'anti-contrefaçon
Le type IAntiforgeryAdditionalDataProvider permet aux développeurs d'étendre le comportement du système anti-CSRF en effectuant des allers-retours de données supplémentaires dans chaque jeton. La méthode GetAdditionalData est appelée chaque fois qu'un jeton de champ est généré et la valeur de retour est intégrée dans le jeton généré. Un implémenteur pourrait renvoyer un horodatage, un nonce ou toute autre valeur, puis appeler ValidateAdditionalData pour valider ces données lorsque le jeton est validé. Le nom d'utilisateur du client est déjà intégré dans les jetons générés, il n'est donc pas nécessaire d'inclure ces informations. Si un jeton inclut des données supplémentaires mais qu'aucune IAntiForgeryAdditionalDataProvider n'est configurée, les données supplémentaires ne sont pas validées.
Ressources supplémentaires
La falsification de requêtes intersites (également connue sous le nom de XSRF ou CSRF) est une attaque contre les applications hébergées sur le Web par laquelle une application Web malveillante peut influencer l'interaction entre un navigateur client et une application Web qui fait confiance à ce navigateur. Ces attaques sont possibles car les navigateurs Web envoient automatiquement certains types de jetons d'authentification à chaque requête adressée à un site Web. Cette forme d'exploit est également connue sous le nom d'attaque en un clic ou de chevauchement de session, car l'attaque tire parti de la session précédemment authentifiée de l'utilisateur.
Un exemple d'attaque CSRF :
Un utilisateur se connecte
www.good-banking-site.example.comà l'aide de l'authentification par formulaire. Le serveur authentifie l'utilisateur et émet une réponse qui inclut une authentification cookie. Le site est vulnérable aux attaques car il fait confiance à toute requête qu'il reçoit avec une authentification valide cookie.L'utilisateur visite un site malveillant,
www.bad-crook-site.example.com.Le site malveillant,
www.bad-crook-site.example.com, contient un formulaire HTML similaire à l'exemple suivant :<h1>Congratulations! You're a Winner!</h1> <form action="https://www.good-banking-site.example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click to collect your prize!" /> </form>Notez que le formulaire
actionenvoie les données au site vulnérable, et non au site malveillant. Il s’agit de la partie « intersites » de CSRF.L'utilisateur sélectionne le bouton Soumettre. Le navigateur fait la demande et inclut automatiquement l'authentification cookie pour le domaine demandé,
www.good-banking-site.example.com.La requête s'exécute sur le serveur
www.good-banking-site.example.comavec le contexte d'authentification de l'utilisateur et peut effectuer toute action qu'un utilisateur authentifié est autorisé à effectuer.
En plus du scénario où l'utilisateur sélectionne le bouton pour soumettre le formulaire, le site malveillant pourrait :
- Exécutez un script qui soumet automatiquement le formulaire.
- Envoyez la soumission du formulaire en tant que requête AJAX.
- Masquez le formulaire à l'aide de CSS.
Ces scénarios alternatifs ne nécessitent aucune action ou entrée de la part de l'utilisateur autre que la visite initiale du site malveillant.
L'utilisation de HTTPS n'empêche pas une attaque CSRF. Le site malveillant peut envoyer https://www.good-banking-site.example.com/ une demande aussi facilement qu’elle peut envoyer une demande non sécurisée.
Certaines attaques ciblent les endpoints qui répondent aux requêtes GET, auquel cas une balise d'image peut être utilisée pour effectuer l'action. Cette forme d'attaque est courante sur les sites de forum qui autorisent les images mais bloquent JavaScript. Les applications qui changent d'état sur les requêtes GET, où des variables ou des ressources sont modifiées, sont vulnérables aux attaques malveillantes. Les requêtes GET qui changent d'état ne sont pas sécurisées. Une bonne pratique consiste à ne jamais modifier l'état d'une requête GET.
Les attaques CSRF sont possibles contre les applications Web qui utilisent des cookies pour l’authentification car :
- Les navigateurs stockent les cookies émis par une application Web.
- Les cookies stockés incluent les cookies de session pour les utilisateurs authentifiés.
- Les navigateurs envoient tous les cookies associés à un domaine à l’application web à chaque requête, quelle que soit la manière dont la demande à l’application a été générée dans le navigateur.
Cependant, les attaques CSRF ne se limitent pas à exploiter les cookies. Par exemple, les authentifications Basic et Digest sont également vulnérables. Une fois qu'un utilisateur s'est connecté avec l'authentification De base ou Digest, le navigateur envoie automatiquement les informations d'identification jusqu'à la fin de la session.
Dans ce contexte, la session fait référence à la session côté client au cours de laquelle l'utilisateur est authentifié. Il n'est pas lié aux sessions côté serveur ou ASP.NET Core intergiciel de session.
Les utilisateurs peuvent se protéger contre les vulnérabilités CSRF en prenant des précautions :
- Déconnectez-vous des applications Web lorsque vous avez fini de les utiliser.
- Effacez périodiquement les cookies des navigateurs.
Cependant, les vulnérabilités CSRF sont fondamentalement un problème avec l'application Web, pas avec l'utilisateur final.
Concepts de base de l’authentification
L'authentification basée sur Cookie est une forme d'authentification populaire. Les systèmes d'authentification basés sur des jetons gagnent en popularité, en particulier pour les applications à page unique (SPA).
authentification basée sur Cookie
Lorsqu'un utilisateur s'authentifie à l'aide de son nom d'utilisateur et de son mot de passe, il reçoit un jeton contenant un ticket d'authentification pouvant être utilisé pour l'authentification et l'autorisation. Le jeton est stocké sous la forme d'un cookie qui est envoyé avec chaque requête faite par le client. La génération et la validation de cette cookie opération sont effectuées par l’intergiciel cookie d’authentification. Le middleware sérialise un principal d’utilisateur dans un cookie chiffré. Lors des requêtes suivantes, le middleware valide le cookie, recrée le principal et attribue le principal à la propriété HttpContext.User.
Authentification basée sur un jeton
Lorsqu'un utilisateur est authentifié, il reçoit un jeton (et non un jeton anti-contrefaçon). Le jeton contient des informations sur l'utilisateur sous la forme de déclarations ou d'un jeton de référence qui dirige l'application vers les données d'état de l'utilisateur conservées par l'application. Lorsqu’un utilisateur tente d’accéder à une ressource qui nécessite une authentification, le jeton est envoyé à l’application avec un en-tête d’autorisation supplémentaire sous la forme d’un Bearer jeton. Cette approche rend l'application sans état. Dans chaque demande suivante, le jeton est transmis dans la requête de validation côté serveur. Ce token n'est pas chiffré; c'est encodé. Sur le serveur, le jeton est décodé pour accéder à ses informations. Pour envoyer le jeton lors de requêtes ultérieures, stockez le jeton dans le stockage local du navigateur. Placer un jeton dans le stockage local du navigateur, le récupérer et l'utiliser comme jeton porteur offre une protection contre les attaques CSRF. Cependant, si l'application était vulnérable à l'injection de script via XSS ou un fichier javascript externe compromis, un cyberattaquant pourrait récupérer n'importe quelle valeur du stockage local et se l'envoyer. ASP.NET Core code par défaut toutes les sorties côté serveur à partir de variables, ce qui réduit le risque de XSS. Si vous remplacez ce comportement en utilisant Html.Raw ou un code personnalisé avec une entrée non fiable, vous pouvez augmenter le risque de XSS.
Ne vous inquiétez pas de la vulnérabilité CSRF si le jeton est stocké dans le stockage local du navigateur. CSRF est un problème lorsque le jeton est stocké dans un fichier cookie. Pour plus d’informations, consultez le problème GitHub L’exemple de code SPA ajoute deux cookies.
Plusieurs applications hébergées sur un domaine
Les environnements d'hébergement partagé sont vulnérables au piratage de session, à la connexion CSRF et à d'autres attaques.
Bien que example1.contoso.net et example2.contoso.net soient des hôtes différents, il existe une relation d'approbation implicite entre les hôtes du domaine *.contoso.net. Cette relation de confiance implicite permet à des hôtes potentiellement non approuvés d’interagir avec les cookies entre eux (les stratégies de même origine qui régissent les requêtes AJAX ne s’appliquent pas nécessairement aux cookies HTTP).
Les attaques qui exploitent les cookies de confiance entre les applications hébergées sur le même domaine peuvent être évitées en ne partageant pas les domaines. Lorsque chaque application est hébergée sur son propre domaine, il n'y a pas de relation de confiance implicite cookie à exploiter.
Anti-contrefaçon dans ASP.NET Core
Avertissement
ASP.NET Core implémente l'anti-contrefaçon à l'aide de ASP.NET Core Data Protection. La pile de protection des données doit être configurée pour fonctionner dans une ferme de serveurs. Pour plus d'informations, voir Configuration de la protection des données.
Le middleware anti-contrefaçon est ajouté au conteneur d'injection de dépendances lorsque l'une des API suivantes est appelée dans Program.cs :
FormTagHelper injecte des jetons antifalsification dans les éléments de formulaire HTML. Le balisage suivant dans un fichier Razor génère automatiquement des jetons anti-contrefaçon :
<form method="post">
<!-- ... -->
</form>
De même, IHtmlHelper.BeginForm génère des jetons anti-contrefaçon par défaut si la méthode du formulaire n'est pas GET.
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML se produit lorsque la balise <form> contient l'attribut method="post" et que l'une des conditions suivantes est vraie :
- L'attribut action est vide (
action=""). - L'attribut action n'est pas fourni (
<form method="post">).
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML peut être désactivée :
Désactivez explicitement les jetons anti-contrefaçon avec l'attribut
asp-antiforgery:<form method="post" asp-antiforgery="false"> <!-- ... --> </form>L’élément de formulaire est exclu des Tag Helpers à l’aide du symbole d’exclusion ! du Tag Helper :
<!form method="post"> <!-- ... --> </!form>Supprimez le
FormTagHelperde la vue. LeFormTagHelperpeut être supprimé d'une vue en ajoutant la directive suivante à la vue Razor :@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
Note
Razor Pages sont automatiquement protégées contre XSRF/CSRF. Pour plus d'informations, voir XSRF/CSRF Razor et Pages.
L'approche la plus courante pour se défendre contre les attaques CSRF consiste à utiliser le Synchronizer Token Pattern (STP). STP est utilisé lorsque l'utilisateur demande une page avec des données de formulaire :
- Le serveur envoie un jeton associé à l’identité de l’utilisateur actuel au client.
- Le client renvoie le jeton au serveur pour vérification.
- Si le serveur reçoit un jeton qui ne correspond pas à l’identité de l’utilisateur authentifié, la demande est rejetée.
Le jeton est unique et imprévisible. Le jeton peut également être utilisé pour assurer le bon séquencement d'une série de requêtes (par exemple, assurer la séquence de requête de : page 1 > page 2 > page 3). Tous les formulaires des modèles ASP.NET Core MVC et Pages Razor génèrent des jetons anti-contrefaçon. La paire d'exemples de vue suivante génère des jetons anti-contrefaçon :
<form asp-action="Index" asp-controller="Home" method="post">
<!-- ... -->
</form>
@using (Html.BeginForm("Index", "Home"))
{
<!-- ... -->
}
Ajoutez explicitement un jeton anti-contrefaçon à un élément <form> sans utiliser les Tag Helpers avec le helper HTML @Html.AntiForgeryToken :
<form asp-action="Index" asp-controller="Home" method="post">
@Html.AntiForgeryToken()
<!-- ... -->
</form>
Dans chacun des cas précédents, ASP.NET Core ajoute un champ de formulaire masqué similaire à l'exemple suivant :
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
ASP.NET Core inclut trois filtres pour travailler avec des jetons anti-contrefaçon :
Protection anti-falsification avec AddControllers
L'appel AddControllers n'active pas les jetons anti-contrefaçon. AddControllersWithViews doit être appelé afin d’assurer une prise en charge intégrée des jetons antifalsification.
Plusieurs onglets de navigateur et le modèle de jeton de synchronisation
Avec le modèle de jeton de synchronisation, seule la page la plus récemment chargée contient un jeton anti-contrefaçon valide. L'utilisation de plusieurs onglets peut être problématique. Par exemple, si un utilisateur ouvre plusieurs onglets :
- Seul l'onglet chargé le plus récemment contient un jeton anti-contrefaçon valide.
- Les requêtes effectuées à partir d'onglets précédemment chargés échouent avec une erreur :
Antiforgery token validation failed. The antiforgery cookie token and request token do not match
Envisagez d'autres modèles de protection CSRF si cela pose un problème.
Configurer l'anti-contrefaçon avec AntiforgeryOptions
Personnalisez AntiforgeryOptions dans le fichier de l’application Program :
builder.Services.AddAntiforgery(options =>
{
// Set Cookie properties using CookieBuilder properties†.
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.SuppressXFrameOptionsHeader = false;
});
Définissez les propriétés cookie anti-contrefaçon à l'aide des propriétés de la classe CookieBuilder, comme indiqué dans le tableau suivant.
| Option | Description |
|---|---|
| Cookie | Détermine les paramètres utilisés pour créer les cookies anti-contrefaçon. |
| FormFieldName | Nom du champ de formulaire masqué utilisé par le système anti-falsification pour générer des jetons anti-falsification dans les vues. |
| HeaderName | Le nom de l'en-tête utilisé par le système anti-contrefaçon. Si null, le système ne prend en compte que les données du formulaire. |
| SuppressXFrameOptionsHeader | Spécifie s'il faut supprimer la génération de l'en-tête X-Frame-Options. Par défaut, l'en-tête est généré avec une valeur de "SAMEORIGIN". La valeur par défaut est false. |
Certains navigateurs n’autorisent pas les points de terminaison non sécurisés à définir des cookies avec un indicateur « sécurisé » ou à remplacer des cookies dont l’indicateur « sécurisé » est défini (pour plus d’informations, consultez Dépréciation de la modification des cookies « sécurisés » à partir d’origines non sécurisées). Étant donné que le mélange de points de terminaison sécurisés et non sécurisés est un scénario courant dans les applications, ASP.NET Core assouplit la restriction relative à la politique de sécurité sur certains cookies, tels que l’antiforgery cookie, en définissant l'cookie de SecurePolicy sur CookieSecurePolicy.None. Même si un utilisateur malveillant vole un jeton anti-contrefaçon cookie, il doit également voler le jeton généralement envoyé via un champ de formulaire (plus courant) ou un en-tête de requête distinct (moins courant), ainsi que les informations d’authentification cookie.
Cookies liés à l’authentification ou à l’autorisation utilisent une stratégie plus forte que CookieSecurePolicy.None.
Si vous le souhaitez, vous pouvez sécuriser l’antifalsification cookie dans des environnements non-Development à l’aide du protocole SSL (Secure Sockets Layer), uniquement via HTTPS, avec le paramètre AntiforgeryOptions.Cookie suivant dans le fichier Program de l'application :
if (!builder.Environment.IsDevelopment())
{
builder.Services.AddAntiforgery(o =>
{
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
Pour plus d’informations, voir CookieAuthenticationOptions.
Générez des jetons anti-contrefaçon avec IAntiforgery
IAntiforgery fournit l'API pour configurer les fonctionnalités anti-contrefaçon.
IAntiforgery peut être demandé en Program.cs à l’aide de WebApplication.Services. L’exemple suivant utilise l’intergiciel depuis la page d’accueil de l’application pour générer un jeton anticontrefaçon et l’envoyer dans la réponse sous forme de cookie:
app.UseRouting();
app.UseAuthorization();
var antiforgery = app.Services.GetRequiredService<IAntiforgery>();
app.Use((context, next) =>
{
var requestPath = context.Request.Path.Value;
if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
|| string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
{
var tokenSet = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
new CookieOptions { HttpOnly = false });
}
return next(context);
});
L'exemple précédent définit un cookie nommé XSRF-TOKEN. Le client peut le lire cookie et fournir sa valeur sous forme d'en-tête attaché aux requêtes AJAX. Par exemple, Angular inclut une protection XSRF intégrée qui lit un cookie nommé XSRF-TOKEN par défaut.
Exiger une validation anti-contrefaçon
Le filtre d'action ValidateAntiForgeryToken peut être appliqué à une action individuelle, à un contrôleur ou globalement. Les requêtes adressées aux actions auxquelles ce filtre est appliqué sont bloquées, sauf si la requête inclut un jeton anti-contrefaçon valide :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
// ...
return RedirectToAction();
}
L'attribut ValidateAntiForgeryToken requiert un jeton pour les requêtes aux méthodes d'action qu'il marque, y compris les requêtes HTTP GET. Si l'attribut ValidateAntiForgeryToken est appliqué sur les contrôleurs de l'application, il peut être remplacé par l'attribut IgnoreAntiforgeryToken.
Valider automatiquement les jetons anti-contrefaçon pour les méthodes HTTP non sécurisées uniquement
Au lieu d'appliquer largement l'attribut ValidateAntiForgeryToken, puis de le remplacer par des attributs IgnoreAntiforgeryToken, l'attribut AutoValidateAntiforgeryToken peut être utilisé. Cet attribut fonctionne de la même manière que l'attribut ValidateAntiForgeryToken, sauf qu'il ne nécessite pas de jetons pour les requêtes effectuées à l'aide des méthodes HTTP suivantes :
- GET
- EN-TÊTE
- OPTIONS
- TRACE
Nous vous recommandons d'utiliser AutoValidateAntiforgeryToken largement pour les scénarios non API. Cet attribut garantit que les actions POST sont protégées par défaut. L'alternative consiste à ignorer les jetons anti-contrefaçon par défaut, sauf si ValidateAntiForgeryToken cela s'applique à des méthodes d'action individuelles. Dans ce scénario, il est plus probable qu'une méthode d'action POST ne soit pas protégée par erreur, laissant l'application vulnérable aux attaques CSRF. Tous les POST doivent envoyer le jeton anti-contrefaçon.
Les API n'ont pas de mécanisme automatique pour envoyer la non-cookie partie du jeton. L'implémentation dépend probablement de l'implémentation du code client. Quelques exemples sont présentés ci-dessous :
Exemple au niveau de la classe :
[AutoValidateAntiforgeryToken]
public class HomeController : Controller
Exemple global :
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
Remplacer les attributs anti-falsification globaux ou au niveau du contrôleur
Le filtre IgnoreAntiforgeryToken est utilisé pour éliminer le besoin d'un jeton anti-contrefaçon pour une action (ou contrôleur) donnée. Lorsqu'il est appliqué, ce filtre prend le pas sur les filtres ValidateAntiForgeryToken et AutoValidateAntiforgeryToken spécifiés à un niveau supérieur (au niveau global ou sur un contrôleur).
[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
// ...
return RedirectToAction();
}
Actualiser les jetons après l'authentification
Les jetons doivent être actualisés une fois l’utilisateur authentifié en redirigeant ce dernier vers une vue ou une page Razor Pages.
JavaScript, AJAX et SPA
Dans les applications HTML traditionnelles, les jetons anti-contrefaçon sont transmis au serveur à l'aide de champs de formulaire masqués. Dans les applications et les SPA modernes basés sur JavaScript, de nombreuses demandes sont effectuées par programmation. Ces requêtes AJAX peuvent utiliser d’autres techniques (telles que des en-têtes de requête ou des cookies) pour envoyer le jeton.
Si les cookies sont utilisés pour stocker des jetons d’authentification et pour authentifier les demandes d’API sur le serveur, CSRF est un problème potentiel. Si le stockage local est utilisé pour stocker le jeton, la vulnérabilité CSRF peut être atténuée car les valeurs du stockage local ne sont pas envoyées automatiquement au serveur à chaque requête. L'utilisation du stockage local pour stocker le jeton anti-contrefaçon sur le client et l'envoi du jeton en tant qu'en-tête de requête est une approche recommandée.
JavaScript
En utilisant JavaScript avec des vues, le jeton peut être créé à l'aide d'un service à partir de la vue. Injectez le service IAntiforgery dans la vue et appelez GetAndStoreTokens :
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
@{
ViewData["Title"] = "JavaScript";
var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
<input id="RequestVerificationToken" type="hidden" value="@requestToken" />
<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>
@section Scripts {
<script>
document.addEventListener("DOMContentLoaded", () => {
const resultElement = document.getElementById("result");
document.getElementById("button").addEventListener("click", async () => {
const response = await fetch("@Url.Action("FetchEndpoint")", {
method: "POST",
headers: {
RequestVerificationToken:
document.getElementById("RequestVerificationToken").value
}
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
});
});
</script>
}
L'exemple précédent utilise JavaScript pour lire la valeur du champ masqué pour l'en-tête AJAX POST.
Cette approche élimine le besoin de traiter directement la définition des cookies par le serveur ou leur lecture par le client. Cependant, lorsque l’injection du service IAntiforgery n’est pas possible, utilisez JavaScript pour accéder aux jetons dans les cookies :
- Accéder aux jetons dans une requête supplémentaire au serveur, généralement
same-origin. - Utilisez le contenu de cookie pour créer un en-tête avec la valeur du jeton.
En supposant que le script envoie le jeton dans un en-tête de requête appelé X-XSRF-TOKEN, configurez le service de protection contre les falsifications de requête pour rechercher l’en-tête X-XSRF-TOKEN :
builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
L'exemple suivant ajoute un point de terminaison protégé qui écrit le jeton de requête dans un fichier JavaScript lisible cookie :
app.UseAuthorization();
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
var tokens = forgeryService.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!,
new CookieOptions { HttpOnly = false });
return Results.Ok();
}).RequireAuthorization();
L'exemple suivant utilise JavaScript pour effectuer une requête AJAX afin d'obtenir le jeton et effectuer une autre requête avec l'en-tête approprié :
var response = await fetch("/antiforgery/token", {
method: "GET",
headers: { "Authorization": authorizationToken }
});
if (response.ok) {
// https://developer.mozilla.org/docs/web/api/document/cookie
const xsrfToken = document.cookie
.split("; ")
.find(row => row.startsWith("XSRF-TOKEN="))
.split("=")[1];
response = await fetch("/JavaScript/FetchEndpoint", {
method: "POST",
headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
Note
Lorsque le jeton anti-contrefaçon est fourni dans l’en-tête de requête et dans la charge utile du formulaire, seul le jeton de l’en-tête est validé.
Anti-contrefaçon avec des API minimales
Minimal APIs ne prend pas en charge l'utilisation des filtres inclus (ValidateAntiForgeryToken, AutoValidateAntiforgeryToken, IgnoreAntiforgeryToken), mais IAntiforgery fournit les API requises pour valider une requête.
L'exemple suivant crée un filtre qui valide le jeton anti-contrefaçon :
internal static class AntiForgeryExtensions
{
public static TBuilder ValidateAntiforgery<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
{
return builder.AddEndpointFilter(routeHandlerFilter: async (context, next) =>
{
try
{
var antiForgeryService = context.HttpContext.RequestServices.GetRequiredService<IAntiforgery>();
await antiForgeryService.ValidateRequestAsync(context.HttpContext);
}
catch (AntiforgeryValidationException)
{
return Results.BadRequest("Antiforgery token validation failed.");
}
return await next(context);
});
}
}
Le filtre peut ensuite être appliqué à un point de terminaison :
app.MapPost("api/upload", (IFormFile name) => Results.Accepted())
.RequireAuthorization()
.ValidateAntiforgery();
Authentification Windows et cookies anti-contrefaçon
Lors de l’utilisation de l’authentification Windows, les points de terminaison d’application doivent être protégés contre les attaques CSRF de la même manière que pour les cookies. Le navigateur envoie implicitement le contexte d'authentification au serveur et les terminaux doivent être protégés contre les attaques CSRF.
Étendre l'anti-contrefaçon
Le type IAntiforgeryAdditionalDataProvider permet aux développeurs d'étendre le comportement du système anti-CSRF en effectuant des allers-retours de données supplémentaires dans chaque jeton. La méthode GetAdditionalData est appelée chaque fois qu'un jeton de champ est généré et la valeur de retour est intégrée dans le jeton généré. Un implémenteur pourrait renvoyer un horodatage, un nonce ou toute autre valeur, puis appeler ValidateAdditionalData pour valider ces données lorsque le jeton est validé. Le nom d'utilisateur du client est déjà intégré dans les jetons générés, il n'est donc pas nécessaire d'inclure ces informations. Si un jeton inclut des données supplémentaires mais qu'aucune IAntiForgeryAdditionalDataProvider n'est configurée, les données supplémentaires ne sont pas validées.
Ressources supplémentaires
La falsification de requêtes intersites (également connue sous le nom de XSRF ou CSRF) est une attaque contre les applications hébergées sur le Web par laquelle une application Web malveillante peut influencer l'interaction entre un navigateur client et une application Web qui fait confiance à ce navigateur. Ces attaques sont possibles car les navigateurs Web envoient automatiquement certains types de jetons d'authentification à chaque requête adressée à un site Web. Cette forme d'exploit est également connue sous le nom d'attaque en un clic ou de chevauchement de session, car l'attaque tire parti de la session précédemment authentifiée de l'utilisateur.
Un exemple d'attaque CSRF :
Un utilisateur se connecte
www.good-banking-site.example.comà l'aide de l'authentification par formulaire. Le serveur authentifie l'utilisateur et émet une réponse qui inclut une authentification cookie. Le site est vulnérable aux attaques car il fait confiance à toute requête qu'il reçoit avec une authentification valide cookie.L'utilisateur visite un site malveillant,
www.bad-crook-site.example.com.Le site malveillant,
www.bad-crook-site.example.com, contient un formulaire HTML similaire à l'exemple suivant :<h1>Congratulations! You're a Winner!</h1> <form action="https://www.good-banking-site.example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click to collect your prize!" /> </form>Notez que le formulaire
actionenvoie les données au site vulnérable, et non au site malveillant. Il s’agit de la partie « intersites » de CSRF.L'utilisateur sélectionne le bouton Soumettre. Le navigateur fait la demande et inclut automatiquement l'authentification cookie pour le domaine demandé,
www.good-banking-site.example.com.La requête s'exécute sur le serveur
www.good-banking-site.example.comavec le contexte d'authentification de l'utilisateur et peut effectuer toute action qu'un utilisateur authentifié est autorisé à effectuer.
En plus du scénario où l'utilisateur sélectionne le bouton pour soumettre le formulaire, le site malveillant pourrait :
- Exécutez un script qui soumet automatiquement le formulaire.
- Envoyez la soumission du formulaire en tant que requête AJAX.
- Masquez le formulaire à l'aide de CSS.
Ces scénarios alternatifs ne nécessitent aucune action ou entrée de la part de l'utilisateur autre que la visite initiale du site malveillant.
L'utilisation de HTTPS n'empêche pas une attaque CSRF. Le site malveillant peut envoyer https://www.good-banking-site.example.com/ une demande aussi facilement qu’elle peut envoyer une demande non sécurisée.
Certaines attaques ciblent les endpoints qui répondent aux requêtes GET, auquel cas une balise d'image peut être utilisée pour effectuer l'action. Cette forme d'attaque est courante sur les sites de forum qui autorisent les images mais bloquent JavaScript. Les applications qui changent d'état sur les requêtes GET, où des variables ou des ressources sont modifiées, sont vulnérables aux attaques malveillantes. Les requêtes GET qui changent d'état ne sont pas sécurisées. Une bonne pratique consiste à ne jamais modifier l'état d'une requête GET.
Les attaques CSRF sont possibles contre les applications Web qui utilisent des cookies pour l’authentification car :
- Les navigateurs stockent les cookies émis par une application Web.
- Les cookies stockés incluent les cookies de session pour les utilisateurs authentifiés.
- Les navigateurs envoient tous les cookies associés à un domaine à l’application web à chaque requête, quelle que soit la manière dont la demande à l’application a été générée dans le navigateur.
Cependant, les attaques CSRF ne se limitent pas à exploiter les cookies. Par exemple, les authentifications Basic et Digest sont également vulnérables. Une fois qu'un utilisateur s'est connecté avec l'authentification De base ou Digest, le navigateur envoie automatiquement les informations d'identification jusqu'à la fin de la session.
Dans ce contexte, la session fait référence à la session côté client au cours de laquelle l'utilisateur est authentifié. Il n'est pas lié aux sessions côté serveur ou ASP.NET Core intergiciel de session.
Les utilisateurs peuvent se protéger contre les vulnérabilités CSRF en prenant des précautions :
- Déconnectez-vous des applications Web lorsque vous avez fini de les utiliser.
- Effacez périodiquement les cookies des navigateurs.
Cependant, les vulnérabilités CSRF sont fondamentalement un problème avec l'application Web, pas avec l'utilisateur final.
Concepts de base de l’authentification
L'authentification basée sur Cookie est une forme d'authentification populaire. Les systèmes d'authentification basés sur des jetons gagnent en popularité, en particulier pour les applications à page unique (SPA).
authentification basée sur Cookie
Lorsqu'un utilisateur s'authentifie à l'aide de son nom d'utilisateur et de son mot de passe, il reçoit un jeton contenant un ticket d'authentification pouvant être utilisé pour l'authentification et l'autorisation. Le jeton est stocké sous la forme d'un cookie qui est envoyé avec chaque requête faite par le client. La génération et la validation de cette cookie opération sont effectuées par l’intergiciel cookie d’authentification. Le middleware sérialise un principal d’utilisateur dans un cookie chiffré. Lors des requêtes suivantes, le middleware valide le cookie, recrée le principal et attribue le principal à la propriété HttpContext.User.
Authentification basée sur un jeton
Lorsqu'un utilisateur est authentifié, il reçoit un jeton (et non un jeton anti-contrefaçon). Le jeton contient des informations sur l'utilisateur sous la forme de déclarations ou d'un jeton de référence qui dirige l'application vers les données d'état de l'utilisateur conservées par l'application. Lorsqu’un utilisateur tente d’accéder à une ressource qui nécessite une authentification, le jeton est envoyé à l’application avec un en-tête d’autorisation supplémentaire sous la forme d’un Bearer jeton. Cette approche rend l'application sans état. Dans chaque demande suivante, le jeton est transmis dans la requête de validation côté serveur. Ce token n'est pas chiffré; c'est encodé. Sur le serveur, le jeton est décodé pour accéder à ses informations. Pour envoyer le jeton lors de requêtes ultérieures, stockez le jeton dans le stockage local du navigateur. Ne vous inquiétez pas de la vulnérabilité CSRF si le jeton est stocké dans le stockage local du navigateur. CSRF est un problème lorsque le jeton est stocké dans un fichier cookie. Pour plus d’informations, consultez le problème GitHub L’exemple de code SPA ajoute deux cookies.
Plusieurs applications hébergées sur un domaine
Les environnements d'hébergement partagé sont vulnérables au piratage de session, à la connexion CSRF et à d'autres attaques.
Bien que example1.contoso.net et example2.contoso.net soient des hôtes différents, il existe une relation d'approbation implicite entre les hôtes du domaine *.contoso.net. Cette relation de confiance implicite permet à des hôtes potentiellement non approuvés d’interagir avec les cookies entre eux (les stratégies de même origine qui régissent les requêtes AJAX ne s’appliquent pas nécessairement aux cookies HTTP).
Les attaques qui exploitent les cookies de confiance entre les applications hébergées sur le même domaine peuvent être évitées en ne partageant pas les domaines. Lorsque chaque application est hébergée sur son propre domaine, il n'y a pas de relation de confiance implicite cookie à exploiter.
Anti-contrefaçon dans ASP.NET Core
Avertissement
ASP.NET Core implémente l'anti-contrefaçon à l'aide de ASP.NET Core Data Protection. La pile de protection des données doit être configurée pour fonctionner dans une ferme de serveurs. Pour plus d'informations, voir Configuration de la protection des données.
Le middleware anti-contrefaçon est ajouté au conteneur d'injection de dépendances lorsque l'une des API suivantes est appelée dans Program.cs :
FormTagHelper injecte des jetons antifalsification dans les éléments de formulaire HTML. Le balisage suivant dans un fichier Razor génère automatiquement des jetons anti-contrefaçon :
<form method="post">
<!-- ... -->
</form>
De même, IHtmlHelper.BeginForm génère des jetons anti-contrefaçon par défaut si la méthode du formulaire n'est pas GET.
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML se produit lorsque la balise <form> contient l'attribut method="post" et que l'une des conditions suivantes est vraie :
- L'attribut action est vide (
action=""). - L'attribut action n'est pas fourni (
<form method="post">).
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML peut être désactivée :
Désactivez explicitement les jetons anti-contrefaçon avec l'attribut
asp-antiforgery:<form method="post" asp-antiforgery="false"> <!-- ... --> </form>L’élément de formulaire est exclu des Tag Helpers à l’aide du symbole d’exclusion ! du Tag Helper :
<!form method="post"> <!-- ... --> </!form>Supprimez le
FormTagHelperde la vue. LeFormTagHelperpeut être supprimé d'une vue en ajoutant la directive suivante à la vue Razor :@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
Note
Razor Pages sont automatiquement protégées contre XSRF/CSRF. Pour plus d'informations, voir XSRF/CSRF Razor et Pages.
L'approche la plus courante pour se défendre contre les attaques CSRF consiste à utiliser le Synchronizer Token Pattern (STP). STP est utilisé lorsque l'utilisateur demande une page avec des données de formulaire :
- Le serveur envoie un jeton associé à l’identité de l’utilisateur actuel au client.
- Le client renvoie le jeton au serveur pour vérification.
- Si le serveur reçoit un jeton qui ne correspond pas à l’identité de l’utilisateur authentifié, la demande est rejetée.
Le jeton est unique et imprévisible. Le jeton peut également être utilisé pour assurer le bon séquencement d'une série de requêtes (par exemple, assurer la séquence de requête de : page 1 > page 2 > page 3). Tous les formulaires des modèles ASP.NET Core MVC et Pages Razor génèrent des jetons anti-contrefaçon. La paire d'exemples de vue suivante génère des jetons anti-contrefaçon :
<form asp-action="Index" asp-controller="Home" method="post">
<!-- ... -->
</form>
@using (Html.BeginForm("Index", "Home"))
{
<!-- ... -->
}
Ajoutez explicitement un jeton anti-contrefaçon à un élément <form> sans utiliser les Tag Helpers avec le helper HTML @Html.AntiForgeryToken :
<form asp-action="Index" asp-controller="Home" method="post">
@Html.AntiForgeryToken()
<!-- ... -->
</form>
Dans chacun des cas précédents, ASP.NET Core ajoute un champ de formulaire masqué similaire à l'exemple suivant :
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
ASP.NET Core inclut trois filtres pour travailler avec des jetons anti-contrefaçon :
Protection anti-falsification avec AddControllers
L'appel AddControllers n'active pas les jetons anti-contrefaçon. AddControllersWithViews doit être appelé afin d’assurer une prise en charge intégrée des jetons antifalsification.
Plusieurs onglets de navigateur et le modèle de jeton de synchronisation
Avec le modèle de jeton de synchronisation, seule la page la plus récemment chargée contient un jeton anti-contrefaçon valide. L'utilisation de plusieurs onglets peut être problématique. Par exemple, si un utilisateur ouvre plusieurs onglets :
- Seul l'onglet chargé le plus récemment contient un jeton anti-contrefaçon valide.
- Les requêtes effectuées à partir d'onglets précédemment chargés échouent avec une erreur :
Antiforgery token validation failed. The antiforgery cookie token and request token do not match
Envisagez d'autres modèles de protection CSRF si cela pose un problème.
Configurer l'anti-contrefaçon avec AntiforgeryOptions
Personnalisez AntiforgeryOptions dans le fichier de l’application Program :
builder.Services.AddAntiforgery(options =>
{
// Set Cookie properties using CookieBuilder properties†.
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.SuppressXFrameOptionsHeader = false;
});
Définissez les propriétés cookie anti-contrefaçon à l'aide des propriétés de la classe CookieBuilder, comme indiqué dans le tableau suivant.
| Option | Description |
|---|---|
| Cookie | Détermine les paramètres utilisés pour créer les cookies anti-contrefaçon. |
| FormFieldName | Nom du champ de formulaire masqué utilisé par le système anti-falsification pour générer des jetons anti-falsification dans les vues. |
| HeaderName | Le nom de l'en-tête utilisé par le système anti-contrefaçon. Si null, le système ne prend en compte que les données du formulaire. |
| SuppressXFrameOptionsHeader | Spécifie s'il faut supprimer la génération de l'en-tête X-Frame-Options. Par défaut, l'en-tête est généré avec une valeur de "SAMEORIGIN". La valeur par défaut est false. |
Certains navigateurs n’autorisent pas les points de terminaison non sécurisés à définir des cookies avec un indicateur « sécurisé » ou à remplacer des cookies dont l’indicateur « sécurisé » est défini (pour plus d’informations, consultez Dépréciation de la modification des cookies « sécurisés » à partir d’origines non sécurisées). Étant donné que le mélange de points de terminaison sécurisés et non sécurisés est un scénario courant dans les applications, ASP.NET Core assouplit la restriction relative à la politique de sécurité sur certains cookies, tels que l’antiforgery cookie, en définissant l'cookie de SecurePolicy sur CookieSecurePolicy.None. Même si un utilisateur malveillant vole un jeton anti-contrefaçon cookie, il doit également voler le jeton généralement envoyé via un champ de formulaire (plus courant) ou un en-tête de requête distinct (moins courant), ainsi que les informations d’authentification cookie.
Cookies liés à l’authentification ou à l’autorisation utilisent une stratégie plus forte que CookieSecurePolicy.None.
Si vous le souhaitez, vous pouvez sécuriser l’antifalsification cookie dans des environnements non-Development à l’aide du protocole SSL (Secure Sockets Layer), uniquement via HTTPS, avec le paramètre AntiforgeryOptions.Cookie suivant dans le fichier Program de l'application :
if (!builder.Environment.IsDevelopment())
{
builder.Services.AddAntiforgery(o =>
{
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
Pour plus d’informations, voir CookieAuthenticationOptions.
Générez des jetons anti-contrefaçon avec IAntiforgery
IAntiforgery fournit l'API pour configurer les fonctionnalités anti-contrefaçon.
IAntiforgery peut être demandé en Program.cs à l’aide de WebApplication.Services. L’exemple suivant utilise l’intergiciel depuis la page d’accueil de l’application pour générer un jeton anticontrefaçon et l’envoyer dans la réponse sous forme de cookie:
app.UseRouting();
app.UseAuthorization();
var antiforgery = app.Services.GetRequiredService<IAntiforgery>();
app.Use((context, next) =>
{
var requestPath = context.Request.Path.Value;
if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
|| string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
{
var tokenSet = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
new CookieOptions { HttpOnly = false });
}
return next(context);
});
L'exemple précédent définit un cookie nommé XSRF-TOKEN. Le client peut le lire cookie et fournir sa valeur sous forme d'en-tête attaché aux requêtes AJAX. Par exemple, Angular inclut une protection XSRF intégrée qui lit un cookie nommé XSRF-TOKEN par défaut.
Exiger une validation anti-contrefaçon
Le filtre d'action ValidateAntiForgeryToken peut être appliqué à une action individuelle, à un contrôleur ou globalement. Les requêtes adressées aux actions auxquelles ce filtre est appliqué sont bloquées, sauf si la requête inclut un jeton anti-contrefaçon valide :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
// ...
return RedirectToAction();
}
L'attribut ValidateAntiForgeryToken requiert un jeton pour les requêtes aux méthodes d'action qu'il marque, y compris les requêtes HTTP GET. Si l'attribut ValidateAntiForgeryToken est appliqué sur les contrôleurs de l'application, il peut être remplacé par l'attribut IgnoreAntiforgeryToken.
Valider automatiquement les jetons anti-contrefaçon pour les méthodes HTTP non sécurisées uniquement
Au lieu d'appliquer largement l'attribut ValidateAntiForgeryToken, puis de le remplacer par des attributs IgnoreAntiforgeryToken, l'attribut AutoValidateAntiforgeryToken peut être utilisé. Cet attribut fonctionne de la même manière que l'attribut ValidateAntiForgeryToken, sauf qu'il ne nécessite pas de jetons pour les requêtes effectuées à l'aide des méthodes HTTP suivantes :
- GET
- EN-TÊTE
- OPTIONS
- TRACE
Nous vous recommandons d'utiliser AutoValidateAntiforgeryToken largement pour les scénarios non API. Cet attribut garantit que les actions POST sont protégées par défaut. L'alternative consiste à ignorer les jetons anti-contrefaçon par défaut, sauf si ValidateAntiForgeryToken cela s'applique à des méthodes d'action individuelles. Dans ce scénario, il est plus probable qu'une méthode d'action POST ne soit pas protégée par erreur, laissant l'application vulnérable aux attaques CSRF. Tous les POST doivent envoyer le jeton anti-contrefaçon.
Les API n'ont pas de mécanisme automatique pour envoyer la non-cookie partie du jeton. L'implémentation dépend probablement de l'implémentation du code client. Quelques exemples sont présentés ci-dessous :
Exemple au niveau de la classe :
[AutoValidateAntiforgeryToken]
public class HomeController : Controller
Exemple global :
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
Remplacer les attributs anti-falsification globaux ou au niveau du contrôleur
Le filtre IgnoreAntiforgeryToken est utilisé pour éliminer le besoin d'un jeton anti-contrefaçon pour une action (ou contrôleur) donnée. Lorsqu'il est appliqué, ce filtre prend le pas sur les filtres ValidateAntiForgeryToken et AutoValidateAntiforgeryToken spécifiés à un niveau supérieur (au niveau global ou sur un contrôleur).
[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
// ...
return RedirectToAction();
}
Actualiser les jetons après l'authentification
Les jetons doivent être actualisés une fois l’utilisateur authentifié en redirigeant ce dernier vers une vue ou une page Razor Pages.
JavaScript, AJAX et SPA
Dans les applications HTML traditionnelles, les jetons anti-contrefaçon sont transmis au serveur à l'aide de champs de formulaire masqués. Dans les applications et les SPA modernes basés sur JavaScript, de nombreuses demandes sont effectuées par programmation. Ces requêtes AJAX peuvent utiliser d’autres techniques (telles que des en-têtes de requête ou des cookies) pour envoyer le jeton.
Si les cookies sont utilisés pour stocker des jetons d’authentification et pour authentifier les demandes d’API sur le serveur, CSRF est un problème potentiel. Si le stockage local est utilisé pour stocker le jeton, la vulnérabilité CSRF peut être atténuée car les valeurs du stockage local ne sont pas envoyées automatiquement au serveur à chaque requête. L'utilisation du stockage local pour stocker le jeton anti-contrefaçon sur le client et l'envoi du jeton en tant qu'en-tête de requête est une approche recommandée.
JavaScript
En utilisant JavaScript avec des vues, le jeton peut être créé à l'aide d'un service à partir de la vue. Injectez le service IAntiforgery dans la vue et appelez GetAndStoreTokens :
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
@{
ViewData["Title"] = "JavaScript";
var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
<input id="RequestVerificationToken" type="hidden" value="@requestToken" />
<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>
@section Scripts {
<script>
document.addEventListener("DOMContentLoaded", () => {
const resultElement = document.getElementById("result");
document.getElementById("button").addEventListener("click", async () => {
const response = await fetch("@Url.Action("FetchEndpoint")", {
method: "POST",
headers: {
RequestVerificationToken:
document.getElementById("RequestVerificationToken").value
}
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
});
});
</script>
}
L'exemple précédent utilise JavaScript pour lire la valeur du champ masqué pour l'en-tête AJAX POST.
Cette approche élimine le besoin de traiter directement la définition des cookies par le serveur ou leur lecture par le client. Cependant, lorsque l’injection du service IAntiforgery n’est pas possible, JavaScript peut également accéder au jeton dans les cookies, obtenu à partir d’une requête supplémentaire au serveur (généralement same-origin), et utiliser le contenu des cookie pour créer un en-tête avec la valeur du jeton.
En supposant que le script envoie le jeton dans un en-tête de requête appelé X-XSRF-TOKEN, configurez le service de protection contre les falsifications de requête pour rechercher l’en-tête X-XSRF-TOKEN :
builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
L'exemple suivant ajoute un point de terminaison protégé qui écrira le jeton de requête dans un JavaScript lisible cookie :
app.UseAuthorization();
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
var tokens = forgeryService.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!,
new CookieOptions { HttpOnly = false });
return Results.Ok();
}).RequireAuthorization();
L'exemple suivant utilise JavaScript pour effectuer une requête AJAX afin d'obtenir le jeton et effectuer une autre requête avec l'en-tête approprié :
var response = await fetch("/antiforgery/token", {
method: "GET",
headers: { "Authorization": authorizationToken }
});
if (response.ok) {
// https://developer.mozilla.org/docs/web/api/document/cookie
const xsrfToken = document.cookie
.split("; ")
.find(row => row.startsWith("XSRF-TOKEN="))
.split("=")[1];
response = await fetch("/JavaScript/FetchEndpoint", {
method: "POST",
headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
Authentification Windows et cookies anti-contrefaçon
Lors de l’utilisation de l’authentification Windows, les points de terminaison d’application doivent être protégés contre les attaques CSRF de la même manière que pour les cookies. Le navigateur envoie implicitement le contexte d'authentification au serveur et les terminaux doivent donc être protégés contre les attaques CSRF.
Étendre l'anti-contrefaçon
Le type IAntiforgeryAdditionalDataProvider permet aux développeurs d'étendre le comportement du système anti-CSRF en effectuant des allers-retours de données supplémentaires dans chaque jeton. La méthode GetAdditionalData est appelée chaque fois qu'un jeton de champ est généré et la valeur de retour est intégrée dans le jeton généré. Un implémenteur pourrait renvoyer un horodatage, un nonce ou toute autre valeur, puis appeler ValidateAdditionalData pour valider ces données lorsque le jeton est validé. Le nom d'utilisateur du client est déjà intégré dans les jetons générés, il n'est donc pas nécessaire d'inclure ces informations. Si un jeton inclut des données supplémentaires mais qu'aucune IAntiForgeryAdditionalDataProvider n'est configurée, les données supplémentaires ne sont pas validées.
Ressources supplémentaires
La falsification de requêtes intersites (également connue sous le nom de XSRF ou CSRF) est une attaque contre les applications hébergées sur le Web par laquelle une application Web malveillante peut influencer l'interaction entre un navigateur client et une application Web qui fait confiance à ce navigateur. Ces attaques sont possibles car les navigateurs Web envoient automatiquement certains types de jetons d'authentification à chaque requête adressée à un site Web. Cette forme d'exploit est également connue sous le nom d'attaque en un clic ou de chevauchement de session, car l'attaque tire parti de la session précédemment authentifiée de l'utilisateur.
Un exemple d'attaque CSRF :
Un utilisateur se connecte
www.good-banking-site.example.comà l'aide de l'authentification par formulaire. Le serveur authentifie l'utilisateur et émet une réponse qui inclut une authentification cookie. Le site est vulnérable aux attaques car il fait confiance à toute requête qu'il reçoit avec une authentification valide cookie.L'utilisateur visite un site malveillant,
www.bad-crook-site.example.com.Le site malveillant,
www.bad-crook-site.example.com, contient un formulaire HTML similaire à l'exemple suivant :<h1>Congratulations! You're a Winner!</h1> <form action="https://www.good-banking-site.example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click to collect your prize!" /> </form>Notez que le formulaire
actionenvoie les données au site vulnérable, et non au site malveillant. Il s’agit de la partie « intersites » de CSRF.L'utilisateur sélectionne le bouton Soumettre. Le navigateur fait la demande et inclut automatiquement l'authentification cookie pour le domaine demandé,
www.good-banking-site.example.com.La requête s'exécute sur le serveur
www.good-banking-site.example.comavec le contexte d'authentification de l'utilisateur et peut effectuer toute action qu'un utilisateur authentifié est autorisé à effectuer.
En plus du scénario où l'utilisateur sélectionne le bouton pour soumettre le formulaire, le site malveillant pourrait :
- Exécutez un script qui soumet automatiquement le formulaire.
- Envoyez la soumission du formulaire en tant que requête AJAX.
- Masquez le formulaire à l'aide de CSS.
Ces scénarios alternatifs ne nécessitent aucune action ou entrée de la part de l'utilisateur autre que la visite initiale du site malveillant.
L'utilisation de HTTPS n'empêche pas une attaque CSRF. Le site malveillant peut envoyer https://www.good-banking-site.example.com/ une demande aussi facilement qu’elle peut envoyer une demande non sécurisée.
Certaines attaques ciblent les endpoints qui répondent aux requêtes GET, auquel cas une balise d'image peut être utilisée pour effectuer l'action. Cette forme d'attaque est courante sur les sites de forum qui autorisent les images mais bloquent JavaScript. Les applications qui changent d'état sur les requêtes GET, où des variables ou des ressources sont modifiées, sont vulnérables aux attaques malveillantes. Les requêtes GET qui changent d'état ne sont pas sécurisées. Une bonne pratique consiste à ne jamais modifier l'état d'une requête GET.
Les attaques CSRF sont possibles contre les applications Web qui utilisent des cookies pour l’authentification car :
- Les navigateurs stockent les cookies émis par une application Web.
- Les cookies stockés incluent les cookies de session pour les utilisateurs authentifiés.
- Les navigateurs envoient tous les cookies associés à un domaine à l’application web à chaque requête, quelle que soit la manière dont la demande à l’application a été générée dans le navigateur.
Cependant, les attaques CSRF ne se limitent pas à exploiter les cookies. Par exemple, les authentifications Basic et Digest sont également vulnérables. Une fois qu'un utilisateur s'est connecté avec l'authentification De base ou Digest, le navigateur envoie automatiquement les informations d'identification jusqu'à la fin de la session.
Dans ce contexte, la session fait référence à la session côté client au cours de laquelle l'utilisateur est authentifié. Il n'est pas lié aux sessions côté serveur ou ASP.NET Core intergiciel de session.
Les utilisateurs peuvent se protéger contre les vulnérabilités CSRF en prenant des précautions :
- Déconnectez-vous des applications Web lorsque vous avez fini de les utiliser.
- Effacez périodiquement les cookies des navigateurs.
Cependant, les vulnérabilités CSRF sont fondamentalement un problème avec l'application Web, pas avec l'utilisateur final.
Concepts de base de l’authentification
L'authentification basée sur Cookie est une forme d'authentification populaire. Les systèmes d'authentification basés sur des jetons gagnent en popularité, en particulier pour les applications à page unique (SPA).
authentification basée sur Cookie
Lorsqu'un utilisateur s'authentifie à l'aide de son nom d'utilisateur et de son mot de passe, il reçoit un jeton contenant un ticket d'authentification pouvant être utilisé pour l'authentification et l'autorisation. Le jeton est stocké sous la forme d'un cookie qui est envoyé avec chaque requête faite par le client. La génération et la validation de cette cookie opération sont effectuées par l’intergiciel cookie d’authentification. Le middleware sérialise un principal d’utilisateur dans un cookie chiffré. Lors des requêtes suivantes, le middleware valide le cookie, recrée le principal et attribue le principal à la propriété HttpContext.User.
Authentification basée sur un jeton
Lorsqu'un utilisateur est authentifié, il reçoit un jeton (et non un jeton anti-contrefaçon). Le jeton contient des informations sur l'utilisateur sous la forme de déclarations ou d'un jeton de référence qui dirige l'application vers les données d'état de l'utilisateur conservées par l'application. Lorsqu’un utilisateur tente d’accéder à une ressource qui nécessite une authentification, le jeton est envoyé à l’application avec un en-tête d’autorisation supplémentaire sous la forme d’un Bearer jeton. Cette approche rend l'application sans état. Dans chaque demande suivante, le jeton est transmis dans la requête de validation côté serveur. Ce token n'est pas chiffré; c'est encodé. Sur le serveur, le jeton est décodé pour accéder à ses informations. Pour envoyer le jeton lors de requêtes ultérieures, stockez le jeton dans le stockage local du navigateur. Ne vous inquiétez pas de la vulnérabilité CSRF si le jeton est stocké dans le stockage local du navigateur. CSRF est un problème lorsque le jeton est stocké dans un fichier cookie. Pour plus d’informations, consultez le problème GitHub L’exemple de code SPA ajoute deux cookies.
Plusieurs applications hébergées sur un domaine
Les environnements d'hébergement partagé sont vulnérables au piratage de session, à la connexion CSRF et à d'autres attaques.
Bien que example1.contoso.net et example2.contoso.net soient des hôtes différents, il existe une relation d'approbation implicite entre les hôtes du domaine *.contoso.net. Cette relation de confiance implicite permet à des hôtes potentiellement non approuvés d’interagir avec les cookies entre eux (les stratégies de même origine qui régissent les requêtes AJAX ne s’appliquent pas nécessairement aux cookies HTTP).
Les attaques qui exploitent les cookies de confiance entre les applications hébergées sur le même domaine peuvent être évitées en ne partageant pas les domaines. Lorsque chaque application est hébergée sur son propre domaine, il n'y a pas de relation de confiance implicite cookie à exploiter.
Configuration anti-contrefaçon ASP.NET Core
Avertissement
ASP.NET Core implémente l'anti-contrefaçon à l'aide de ASP.NET Core Data Protection. La pile de protection des données doit être configurée pour fonctionner dans une ferme de serveurs. Pour plus d'informations, voir Configuration de la protection des données.
Le middleware anti-contrefaçon est ajouté au conteneur d'injection de dépendances lorsque l'une des API suivantes est appelée dans Startup.ConfigureServices :
Dans ASP.NET Core 2.0 ou version ultérieure, le FormTagHelper injecte des jetons anti-contrefaçon dans les éléments de formulaire HTML. Le balisage suivant dans un fichier Razor génère automatiquement des jetons anti-contrefaçon :
<form method="post">
...
</form>
De même, IHtmlHelper.BeginForm génère des jetons anti-contrefaçon par défaut si la méthode du formulaire n'est pas GET.
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML se produit lorsque la balise <form> contient l'attribut method="post" et que l'une des conditions suivantes est vraie :
- L'attribut action est vide (
action=""). - L'attribut action n'est pas fourni (
<form method="post">).
La génération automatique de jetons anti-contrefaçon pour les éléments de formulaire HTML peut être désactivée :
Désactivez explicitement les jetons anti-contrefaçon avec l'attribut
asp-antiforgery:<form method="post" asp-antiforgery="false"> ... </form>L’élément de formulaire est exclu des Tag Helpers à l’aide du symbole d’exclusion ! du Tag Helper :
<!form method="post"> ... </!form>Supprimez le
FormTagHelperde la vue. LeFormTagHelperpeut être supprimé d'une vue en ajoutant la directive suivante à la vue Razor :@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
Note
Razor Pages sont automatiquement protégées contre XSRF/CSRF. Pour plus d'informations, voir XSRF/CSRF Razor et Pages.
L'approche la plus courante pour se défendre contre les attaques CSRF consiste à utiliser le Synchronizer Token Pattern (STP). STP est utilisé lorsque l'utilisateur demande une page avec des données de formulaire :
- Le serveur envoie un jeton associé à l’identité de l’utilisateur actuel au client.
- Le client renvoie le jeton au serveur pour vérification.
- Si le serveur reçoit un jeton qui ne correspond pas à l’identité de l’utilisateur authentifié, la demande est rejetée.
Le jeton est unique et imprévisible. Le jeton peut également être utilisé pour assurer le bon séquencement d'une série de requêtes (par exemple, assurer la séquence de requête de : page 1 > page 2 > page 3). Tous les formulaires des modèles ASP.NET Core MVC et Pages Razor génèrent des jetons anti-contrefaçon. La paire d'exemples de vue suivante génère des jetons anti-contrefaçon :
<form asp-controller="Todo" asp-action="Create" method="post">
...
</form>
@using (Html.BeginForm("Create", "Todo"))
{
...
}
Ajoutez explicitement un jeton anti-contrefaçon à un élément <form> sans utiliser les Tag Helpers avec le helper HTML @Html.AntiForgeryToken :
<form action="/" method="post">
@Html.AntiForgeryToken()
</form>
Dans chacun des cas précédents, ASP.NET Core ajoute un champ de formulaire masqué similaire à l'exemple suivant :
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
ASP.NET Core inclut trois filtres pour travailler avec des jetons anti-contrefaçon :
Options anti-contrefaçon
Personnaliser AntiforgeryOptions en Startup.ConfigureServices :
services.AddAntiforgery(options =>
{
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.SuppressXFrameOptionsHeader = false;
});
Définissez les propriétés cookie anti-contrefaçon à l'aide des propriétés de la classe CookieBuilder, comme indiqué dans le tableau suivant.
| Option | Description |
|---|---|
| Cookie | Détermine les paramètres utilisés pour créer les cookies anti-contrefaçon. |
| FormFieldName | Nom du champ de formulaire masqué utilisé par le système anti-falsification pour générer des jetons anti-falsification dans les vues. |
| HeaderName | Le nom de l'en-tête utilisé par le système anti-contrefaçon. Si null, le système ne prend en compte que les données du formulaire. |
| SuppressXFrameOptionsHeader | Spécifie s'il faut supprimer la génération de l'en-tête X-Frame-Options. Par défaut, l'en-tête est généré avec une valeur de "SAMEORIGIN". La valeur par défaut est false. |
Certains navigateurs n’autorisent pas les points de terminaison non sécurisés à définir des cookies avec un indicateur « sécurisé » ou à remplacer des cookies dont l’indicateur « sécurisé » est défini (pour plus d’informations, consultez Dépréciation de la modification des cookies « sécurisés » à partir d’origines non sécurisées). Étant donné que le mélange de points de terminaison sécurisés et non sécurisés est un scénario courant dans les applications, ASP.NET Core assouplit la restriction relative à la politique de sécurité sur certains cookies, tels que l’antiforgery cookie, en définissant l'cookie de SecurePolicy sur CookieSecurePolicy.None. Même si un utilisateur malveillant vole un jeton anti-contrefaçon cookie, il doit également voler le jeton généralement envoyé via un champ de formulaire (plus courant) ou un en-tête de requête distinct (moins courant), ainsi que les informations d’authentification cookie.
Cookies liés à l’authentification ou à l’autorisation utilisent une stratégie plus forte que CookieSecurePolicy.None.
Si vous le souhaitez, vous pouvez sécuriser l'antifalsification cookie dans des environnements non-Development en utilisant le protocole Secure Sockets Layer (SSL), via HTTPS uniquement, avec le paramètre de propriété suivant AntiforgeryOptions.Cookie dans la classe de l’application Startup :
public class Startup
{
public Startup(IConfiguration configuration, IHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public IConfiguration Configuration { get; }
public IHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
// Other services are registered here
if (!Environment.IsDevelopment())
{
services.AddAntiforgery(o =>
{
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Request processing pipeline
}
}
Pour plus d’informations, voir CookieAuthenticationOptions.
Configurer les fonctionnalités anti-contrefaçon avec IAntiforgery
IAntiforgery fournit l'API pour configurer les fonctionnalités anti-contrefaçon.
IAntiforgery peut être demandé dans la méthode Configure de la classe Startup.
Dans l’exemple suivant :
- L'intergiciel de la page d'accueil de l'application est utilisé pour générer un jeton anti-falsification et l'envoyer dans la réponse sous la forme d'un cookie.
- Le jeton de requête est envoyé en tant que cookie lisible par JavaScript avec la convention d’affectation de nom Angular par défaut décrite dans la section AngularJS.
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
app.Use(next => context =>
{
string path = context.Request.Path.Value;
if (string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
return next(context);
});
}
Exiger une validation anti-contrefaçon
ValidateAntiForgeryToken est un filtre d'action qui peut être appliqué à une action individuelle, à un contrôleur ou globalement. Les requêtes adressées aux actions auxquelles ce filtre est appliqué sont bloquées à moins que la demande n'inclue un jeton anti-contrefaçon valide.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
{
ManageMessageId? message = ManageMessageId.Error;
var user = await GetCurrentUserAsync();
if (user != null)
{
var result =
await _userManager.RemoveLoginAsync(
user, account.LoginProvider, account.ProviderKey);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
}
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
L'attribut ValidateAntiForgeryToken requiert un jeton pour les requêtes aux méthodes d'action qu'il marque, y compris les requêtes HTTP GET. Si l'attribut ValidateAntiForgeryToken est appliqué sur les contrôleurs de l'application, il peut être remplacé par l'attribut IgnoreAntiforgeryToken.
Note
ASP.NET Core ne prend pas en charge l'ajout automatique de jetons anti-contrefaçon aux requêtes GET.
Valider automatiquement les jetons anti-contrefaçon pour les méthodes HTTP non sécurisées uniquement
Les applications ASP.NET Core ne génèrent pas de jetons anti-contrefaçon pour les méthodes HTTP sécurisées (GET, HEAD, OPTIONS et TRACE). Au lieu d'appliquer largement l'attribut ValidateAntiForgeryToken, puis de le remplacer par des attributs IgnoreAntiforgeryToken, l'attribut AutoValidateAntiforgeryToken peut être utilisé. Cet attribut fonctionne de la même manière que l'attribut ValidateAntiForgeryToken, sauf qu'il ne nécessite pas de jetons pour les requêtes effectuées à l'aide des méthodes HTTP suivantes :
- GET
- EN-TÊTE
- OPTIONS
- TRACE
Nous vous recommandons d'utiliser AutoValidateAntiforgeryToken largement pour les scénarios non API. Cet attribut garantit que les actions POST sont protégées par défaut. L'alternative consiste à ignorer les jetons anti-contrefaçon par défaut, sauf si ValidateAntiForgeryToken cela s'applique à des méthodes d'action individuelles. Dans ce scénario, il est plus probable qu'une méthode d'action POST ne soit pas protégée par erreur, laissant l'application vulnérable aux attaques CSRF. Tous les POST doivent envoyer le jeton anti-contrefaçon.
Les API n'ont pas de mécanisme automatique pour envoyer la non-cookie partie du jeton. L'implémentation dépend probablement de l'implémentation du code client. Quelques exemples sont présentés ci-dessous :
Exemple au niveau de la classe :
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
Exemple global :
services.AddControllersWithViews(options =>
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));
Remplacer les attributs anti-falsification globaux ou au niveau du contrôleur
Le filtre IgnoreAntiforgeryToken est utilisé pour éliminer le besoin d'un jeton anti-contrefaçon pour une action (ou contrôleur) donnée. Lorsqu'il est appliqué, ce filtre prend le pas sur les filtres ValidateAntiForgeryToken et AutoValidateAntiforgeryToken spécifiés à un niveau supérieur (au niveau global ou sur un contrôleur).
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
{
// no antiforgery token required
}
}
Actualiser les jetons après l'authentification
Les jetons doivent être actualisés une fois l’utilisateur authentifié en redirigeant ce dernier vers une vue ou une page Razor Pages.
JavaScript, AJAX et SPA
Dans les applications HTML traditionnelles, les jetons anti-contrefaçon sont transmis au serveur à l'aide de champs de formulaire masqués. Dans les applications et les SPA modernes basés sur JavaScript, de nombreuses demandes sont effectuées par programmation. Ces requêtes AJAX peuvent utiliser d’autres techniques (telles que des en-têtes de requête ou des cookies) pour envoyer le jeton.
Si les cookies sont utilisés pour stocker des jetons d’authentification et pour authentifier les demandes d’API sur le serveur, CSRF est un problème potentiel. Si le stockage local est utilisé pour stocker le jeton, la vulnérabilité CSRF peut être atténuée car les valeurs du stockage local ne sont pas envoyées automatiquement au serveur à chaque requête. L'utilisation du stockage local pour stocker le jeton anti-contrefaçon sur le client et l'envoi du jeton en tant qu'en-tête de requête est une approche recommandée.
JavaScript
En utilisant JavaScript avec des vues, le jeton peut être créé à l'aide d'un service à partir de la vue. Injectez le service IAntiforgery dans la vue et appelez GetAndStoreTokens :
@{
ViewData["Title"] = "AJAX Demo";
}
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
public string GetAntiXsrfRequestToken()
{
return Xsrf.GetAndStoreTokens(Context).RequestToken;
}
}
<input type="hidden" id="RequestVerificationToken"
name="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>
<div class="row">
<p><input type="button" id="antiforgery" value="Antiforgery"></p>
<script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == XMLHttpRequest.DONE) {
if (xhttp.status == 200) {
alert(xhttp.responseText);
} else {
alert('There was an error processing the AJAX request.');
}
}
};
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("antiforgery").onclick = function () {
xhttp.open('POST', '@Url.Action("Antiforgery", "Home")', true);
xhttp.setRequestHeader("RequestVerificationToken",
document.getElementById('RequestVerificationToken').value);
xhttp.send();
}
});
</script>
</div>
Cette approche élimine le besoin de traiter directement la définition des cookies par le serveur ou leur lecture par le client.
L'exemple précédent utilise JavaScript pour lire la valeur du champ masqué pour l'en-tête AJAX POST.
JavaScript peut également accéder aux jetons dans les cookies et utiliser le contenu des cookie pour créer un en-tête avec la valeur du jeton.
context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken,
new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = false });
En supposant que le script demande que le jeton soit envoyé dans un en-tête appelé X-CSRF-TOKEN, configurez le service antifalsification pour rechercher l'en-tête X-CSRF-TOKEN :
services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");
L'exemple suivant utilise JavaScript pour effectuer une requête AJAX avec l'en-tête approprié :
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
var csrfToken = getCookie("CSRF-TOKEN");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (xhttp.readyState === XMLHttpRequest.DONE) {
if (xhttp.status === 204) {
alert('Todo item is created successfully.');
} else {
alert('There was an error processing the AJAX request.');
}
}
};
xhttp.open('POST', '/api/items', true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({ "name": "Learn C#" }));
AngularJS
AngularJS utilise une convention pour traiter CSRF. Si le serveur envoie un cookie avec le nom XSRF-TOKEN, le service AngularJS $http ajoute la valeur cookie à un en-tête lorsqu’il envoie une requête au serveur. Ce processus est automatique. Le client n'a pas besoin de définir explicitement l'en-tête. Le nom de l'en-tête est X-XSRF-TOKEN. Le serveur doit détecter cet en-tête et valider son contenu.
Pour que l'API ASP.NET Core fonctionne avec cette convention au démarrage de votre application :
- Configurez votre application pour fournir un jeton dans un cookie appelé
XSRF-TOKEN. - Configurez le service anti-contrefaçon pour rechercher un en-tête nommé
X-XSRF-TOKEN, qui est le nom d'en-tête par défaut d'Angular pour l'envoi du jeton XSRF.
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
app.Use(next => context =>
{
string path = context.Request.Path.Value;
if (
string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
return next(context);
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
}
Note
Lorsque le jeton anti-contrefaçon est fourni dans l’en-tête de requête et dans la charge utile du formulaire, seul le jeton de l’en-tête est validé.
Authentification Windows et cookies anti-contrefaçon
Lors de l’utilisation de l’authentification Windows, les points de terminaison d’application doivent être protégés contre les attaques CSRF de la même manière que pour les cookies. Le navigateur envoie implicitement le contexte d'authentification au serveur et les terminaux doivent donc être protégés contre les attaques CSRF.
Étendre l'anti-contrefaçon
Le type IAntiforgeryAdditionalDataProvider permet aux développeurs d'étendre le comportement du système anti-CSRF en effectuant des allers-retours de données supplémentaires dans chaque jeton. La méthode GetAdditionalData est appelée chaque fois qu'un jeton de champ est généré et la valeur de retour est intégrée dans le jeton généré. Un implémenteur pourrait renvoyer un horodatage, un nonce ou toute autre valeur, puis appeler ValidateAdditionalData pour valider ces données lorsque le jeton est validé. Le nom d'utilisateur du client est déjà intégré dans les jetons générés, il n'est donc pas nécessaire d'inclure ces informations. Si un jeton inclut des données supplémentaires mais qu'aucune IAntiForgeryAdditionalDataProvider n'est configurée, les données supplémentaires ne sont pas validées.