Prévention des attaques de falsification de requête intersite (CSRF) dans ASP.NET application MVC
La falsification de requête intersite (CSRF) est une attaque par laquelle un site malveillant envoie une demande à un site vulnérable où l’utilisateur est actuellement connecté
Voici un exemple d’attaque CSRF :
Un utilisateur se connecte à l’aide de
www.example.com
l’authentification par formulaire.Le serveur authentifie l’utilisateur. La réponse du serveur inclut un cookie d’authentification.
Sans se déconnecter, l’utilisateur visite un site web malveillant. Ce site malveillant contient le formulaire HTML suivant :
<h1>You Are a Winner!</h1> <form action="http://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 Me"/> </form>
Notez que l’action de formulaire est postées sur le site vulnérable, et non sur le site malveillant. Il s’agit de la partie « intersite » de CSRF.
L’utilisateur clique sur le bouton Envoyer. Le navigateur inclut le cookie d’authentification avec la demande.
La requête s’exécute sur le serveur avec le contexte d’authentification de l’utilisateur et peut faire tout ce qu’un utilisateur authentifié est autorisé à faire.
Bien que cet exemple exige que l’utilisateur clique sur le bouton du formulaire, la page malveillante peut tout aussi bien exécuter un script qui envoie automatiquement le formulaire. De plus, l’utilisation de SSL n’empêche pas une attaque CSRF, car le site malveillant peut envoyer une requête « https:// ».
En règle générale, les attaques CSRF sont possibles contre les sites web qui utilisent des cookies pour l’authentification, car les navigateurs envoient tous les cookies pertinents au site web de destination. Toutefois, les attaques CSRF ne se limitent pas à l’exploitation des cookies. Par exemple, l’authentification de base et l’authentification Digest sont également vulnérables. Après 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.
Jetons anti-falsification
Pour empêcher les attaques CSRF, ASP.NET MVC utilise des jetons anti-falsification, également appelés jetons de vérification de demande.
- Le client demande une page HTML qui contient un formulaire.
- Le serveur inclut deux jetons dans la réponse. Un jeton est envoyé sous forme de cookie. L’autre est placé dans un champ de formulaire masqué. Les jetons sont générés de manière aléatoire afin qu’un adversaire ne puisse pas deviner les valeurs.
- Lorsque le client envoie le formulaire, il doit renvoyer les deux jetons au serveur. Le client envoie le jeton de cookie sous forme de cookie, et il envoie le jeton de formulaire à l’intérieur des données du formulaire. (Un client de navigateur effectue automatiquement cette opération lorsque l’utilisateur envoie le formulaire.)
- Si une requête n’inclut pas les deux jetons, le serveur l’interdit.
Voici un exemple de formulaire HTML avec un jeton de formulaire masqué :
<form action="/Home/Test" method="post">
<input name="__RequestVerificationToken" type="hidden"
value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />
<input type="submit" value="Submit" />
</form>
Les jetons anti-falsification fonctionnent parce que la page malveillante ne peut pas lire les jetons de l’utilisateur, en raison de stratégies de même origine. (Les stratégies de même origine empêchent les documents hébergés sur deux sites différents d’accéder au contenu de l’autre. Ainsi, dans l’exemple précédent, la page malveillante peut envoyer des requêtes à example.com, mais elle ne peut pas lire la réponse.)
Pour empêcher les attaques CSRF, utilisez des jetons anti-falsification avec n’importe quel protocole d’authentification où le navigateur envoie silencieusement les informations d’identification une fois que l’utilisateur s’est connecté. Cela inclut les protocoles d’authentification basés sur les cookies, tels que l’authentification par formulaire, ainsi que les protocoles tels que l’authentification de base et l’authentification Digest.
Vous devez exiger des jetons anti-falsification pour toutes les méthodes non sécuritaires (POST, PUT, DELETE). Assurez-vous également que les méthodes sûres (GET, HEAD) n’ont pas d’effets secondaires. En outre, si vous activez la prise en charge inter-domaines, comme CORS ou JSONP, même les méthodes sécurisées telles que GET sont potentiellement vulnérables aux attaques CSRF, ce qui permet à l’attaquant de lire des données potentiellement sensibles.
Jetons anti-falsification dans ASP.NET MVC
Pour ajouter les jetons anti-falsification à une page Razor, utilisez la méthode d’assistance HtmlHelper.AntiForgeryToken :
@using (Html.BeginForm("Manage", "Account")) {
@Html.AntiForgeryToken()
}
Cette méthode ajoute le champ de formulaire masqué et définit également le jeton de cookie.
Anti-CSRF et AJAX
le jeton de formulaire peut se révéler problématique pour les requêtes AJAX, car une requête AJAX risque d’envoyer des données JSON, et non des données de formulaire HTML. L’une des solutions consiste à envoyer les jetons dans un en-tête HTTP personnalisé. Le code ci-après utilise la syntaxe Razor pour générer les jetons, puis ajoute les jetons à une demande AJAX. Les jetons sont générés sur le serveur en appelant AntiForgery.GetTokens.
<script>
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$.ajax("api/values", {
type: "post",
contentType: "application/json",
data: { }, // JSON data goes here
dataType: "json",
headers: {
'RequestVerificationToken': '@TokenHeaderValue()'
}
});
</script>
Lorsque vous traitez la demande, extrayez les jetons de l’en-tête de la demande. Appelez ensuite la méthode AntiForgery.Validate pour valider les jetons. La méthode Validate lève une exception si les jetons ne sont pas valides.
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}