Condividi tramite


Prevenire attacchi da Cross-Site Request Forgery (XSRF/CSRF) in ASP.NET Core

Di Fiyaz Hasan e Rick Anderson

La falsificazione di richiesta intersito è un attacco contro le app ospitate sul Web, in cui un'app Web dannosa può influenzare l'interazione tra un browser client e un'app Web che ritiene affidabile quel browser. Questi attacchi sono possibili perché i Web browser inviano automaticamente alcuni tipi di token di autenticazione con ogni richiesta a un sito Web. Questa forma di exploit è nota anche come attacco con un clic o guida di sessione perché l'attacco sfrutta la sessione autenticata in precedenza dell'utente. La falsificazione di richiesta inter-sito è nota anche come XSRF o CSRF.

Un esempio di attacco CSRF:

  1. Un utente accede a www.good-banking-site.example.com usando l'autenticazione basata su form. Il server autentica l'utente e rilascia una risposta che include un'autenticazione cookie. Il sito è vulnerabile all'attacco perché considera attendibile qualsiasi richiesta ricevuta con un'autenticazione cookievalida.

  2. L'utente visita un sito dannoso, www.bad-crook-site.example.com.

    Il sito dannoso, www.bad-crook-site.example.com, contiene un modulo HTML simile all'esempio seguente:

    <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>
    

    Si noti che il modulo del action viene inviato al sito vulnerabile, non al sito dannoso. Questa è la parte "cross-site" di CSRF.

  3. L'utente seleziona il pulsante invia. Il browser effettua la richiesta e include automaticamente l'autenticazione cookie per il dominio richiesto, www.good-banking-site.example.com.

  4. La richiesta viene eseguita sul www.good-banking-site.example.com server con il contesto di autenticazione dell'utente e può eseguire qualsiasi azione che un utente autenticato possa eseguire.

Oltre allo scenario in cui l'utente seleziona il pulsante per inviare il modulo, il sito dannoso potrebbe:

  • Eseguire uno script che invia automaticamente il modulo.
  • Inviare il modulo come richiesta AJAX.
  • Nascondere il modulo usando CSS.

Questi scenari alternativi non richiedono alcuna azione o input da parte dell'utente diverso da visitare inizialmente il sito dannoso.

L'uso di HTTPS non impedisce un attacco CSRF. Il sito dannoso può inviare una richiesta https://www.good-banking-site.example.com/ con la stessa facilità con cui può inviare una richiesta non sicura.

Alcuni attacchi usano endpoint che rispondono alle richieste GET, nel qual caso è possibile usare un tag immagine per eseguire l'azione. Questa forma di attacco è comune nei siti del forum che consentono immagini ma bloccano JavaScript. Le app che cambiano lo stato nelle richieste GET, in cui le variabili o le risorse vengono modificate, sono vulnerabili ad attacchi dannosi. Le richieste GET che cambiano lo stato non sono sicure. Una procedura consigliata consiste nel non modificare mai lo stato in una richiesta GET.

Gli attacchi CSRF sono possibili contro le app Web che usano cookie per l'autenticazione perché:

  • I browser archivia i cookie rilasciati da un'app Web.
  • I cookie archiviati includono cookie di sessione per gli utenti autenticati.
  • I browser inviano tutti i cookie associati a un dominio all'app Web ogni richiesta indipendentemente dalla modalità di generazione della richiesta all'app all'interno del browser.

Tuttavia, gli attacchi CSRF non sono limitati a sfruttare i cookie. Ad esempio, anche l'autenticazione di base e digest è vulnerabile. Dopo che un utente accede con l'autenticazione di base o digest, il browser invia automaticamente le credenziali fino al termine della sessione.

In questo contesto, la sessione fa riferimento alla sessione sul lato client durante la quale l'utente viene autenticato. Non è correlato alle sessioni sul lato server o al middleware di sessione di ASP.NET Core.

Gli utenti possono proteggersi dalle vulnerabilità CSRF adottando precauzioni:

  • Disconnettersi dalle app Web al termine dell'uso.
  • Cancellare periodicamente i cookie del browser.

Tuttavia, le vulnerabilità CSRF sono fondamentalmente un problema con l'app Web, non con l'utente finale.

Nozioni fondamentali di autenticazione

CookieL'autenticazione basata su è una forma comune di autenticazione. I sistemi di autenticazione basati su token sono in continua crescita, in particolare per le applicazioni a pagina singola.

Quando un utente esegue l'autenticazione usando il nome utente e la password, viene rilasciato un token contenente un ticket di autenticazione. Il token può essere usato per l'autenticazione e l'autorizzazione. Il token viene archiviato come oggetto cookie inviato con ogni richiesta eseguita dal client. La generazione e la convalida di cookie vengono eseguite con il Middleware di Autenticazione Cookie. Il middleware serializza un'entità utente in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida cookie, ricrea il principale e assegna il principale alla proprietà HttpContext.User.

Autenticazione basata su token

Quando un utente viene autenticato, viene rilasciato un token (non un token antiforgery). Il token contiene le informazioni utente nella forma di asserzioni oppure di un token di riferimento che reindirizza l'app allo stato utente gestito dall'app. Quando un utente tenta di accedere a una risorsa che richiede l'autenticazione, il token viene inviato all'app con un'intestazione di autorizzazione aggiuntiva sotto forma di token Bearer. Questo approccio rende l'applicazione senza stato. In ogni richiesta successiva, il token viene passato nella richiesta di convalida lato server. Questo token non è crittografato, ma viene codificato. Nel server il token viene decodificato per accedere alle informazioni. Per inviare il token alle richieste successive, archiviare il token nella risorsa di archiviazione locale del browser. Posizionare un token nell'archiviazione locale del browser e recuperarlo e usarlo come token di connessione fornisce protezione dagli attacchi CSRF. Tuttavia, se l'app è vulnerabile all'inserimento di script tramite XSS o un file JavaScript esterno compromesso, un cyberattacker potrebbe recuperare qualsiasi valore dall'archiviazione locale e inviarlo a se stessi. ASP.NET Core codifica tutti gli output lato server dalle variabili per impostazione predefinita, riducendo il rischio di XSS. Se si esegue l'override di questo comportamento usando Html.Raw o codice personalizzato con input non attendibile, è possibile aumentare il rischio di XSS.

Non preoccuparsi della vulnerabilità CSRF se il token viene archiviato nella risorsa di archiviazione locale del browser. CSRF è un problema quando il token viene memorizzato in un cookie oggetto. Per altre informazioni, vedere l'esempio di codice SPA relativo al problema di GitHub che aggiunge due cookie.

Più app ospitate in un dominio

Gli ambienti di hosting condivisi sono vulnerabili all'hijacking della sessione, all'accesso CSRF e ad altri attacchi.

Anche se example1.contoso.net e example2.contoso.net sono host diversi, esiste una relazione di trust implicita tra gli host nel *.contoso.net dominio. Questa relazione di trust implicita consente agli host potenzialmente non attendibili di influire sui cookie degli altri (i criteri di stessa origine che regolano le richieste AJAX non si applicano necessariamente ai cookie HTTP).

Gli attacchi che sfruttano i cookie attendibili tra app ospitate nello stesso dominio possono essere impediti non condividendo domini. Quando ogni app è ospitata nel proprio dominio, non esiste alcuna relazione di trust implicita cookie da sfruttare.

Antiforgery in ASP.NET Core

Avviso

ASP.NET Core implementa l'antiforgeria usando ASP.NET Core Data Protection. Lo stack di protezione dati deve essere configurato per funzionare in una server farm. Per altre informazioni, vedere Configurazione della protezione dei dati.

Il middleware antiforgery viene aggiunto al container di Dependency injection quando viene chiamata una delle API seguenti in Program.cs:

Per altre informazioni, vedere Antiforgery con Minimal APIs.

FormTagHelper inserisce token antifalsificazione negli elementi del modulo HTML. Il markup seguente in un file Razor genera automaticamente token anti-contraffazione.

<form method="post">
    <!-- ... -->
</form>

Analogamente, IHtmlHelper.BeginForm genera automaticamente token antiforgery se il metodo del form non è GET.

La generazione automatica di token antiforgery per gli elementi del modulo HTML si verifica quando il <form> tag contiene l'attributo method="post" e uno dei seguenti è true:

  • L'attributo action è vuoto (action="").
  • L'attributo action non viene fornito (<form method="post">).

La generazione automatica di token antiforgery per gli elementi del modulo HTML può essere disabilitata:

  • Disabilita esplicitamente i token antiforgery con l'attributo asp-antiforgery

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • L'elemento del modulo è esplicitamente disattivato dagli helper tag usando il simbolo di rifiuto esplicito dell'helper tag:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Rimuovere l'oggetto FormTagHelper dalla visualizzazione. L'elemento FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente alla vista Razor.

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

Nota

Razor Le pagine vengono protette automaticamente da XSRF/CSRF. Per altre informazioni, vedere XSRF/CSRF e Razor Pages.

L'approccio più comune per proteggersi dagli attacchi CSRF consiste nell'usare il Pattern Token di Sincronizzazione (STP). STP viene usato quando l'utente richiede una pagina con dati del modulo:

  1. Il server invia un token associato all'identità dell'utente corrente al client.
  2. Il client invia il token al server per la verifica.
  3. Se il server riceve un token che non corrisponde all'identità dell'utente autenticato, la richiesta viene rifiutata.

Il token è univoco e imprevedibile. Il token può essere usato anche per garantire una sequenziazione corretta di una serie di richieste ( ad esempio, verificando la sequenza di richiesta di: pagina 1 > pagina 2 > pagina 3). Tutti i moduli nei modelli ASP.NET Core MVC e Razor Pages generano token antiforgery. La coppia di esempi di visualizzazione seguente genera token antiforgery:

<form asp-action="Index" asp-controller="Home" method="post">
    <!-- ... -->
</form>

@using (Html.BeginForm("Index", "Home"))
{
    <!-- ... -->
}

Aggiungere in modo esplicito un token antiforgery a un elemento <form> senza utilizzare i Tag Helper con l'helper HTML @Html.AntiForgeryToken:

<form asp-action="Index" asp-controller="Home" method="post">
    @Html.AntiForgeryToken()

    <!-- ... -->
</form>

In ognuno dei casi precedenti, ASP.NET Core aggiunge un campo modulo nascosto simile all'esempio seguente:

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core include tre filtri per l'uso di token antiforgery:

Anticontraffazione con AddControllers

La chiamata AddControllersnon abilita i token antiforgery. AddControllersWithViews deve essere chiamato per avere il supporto integrato per i token antiforgery.

Schede multiple del browser e modello di token di sincronizzazione

Non sono supportate più schede aperte con accesso come utenti diversi o una anonima.

Configurare l'antiforgeria con AntiforgeryOptions

Personalizzare AntiforgeryOptions in Program.cs:

builder.Services.AddAntiforgery(options =>
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

Impostare le proprietà antiforgery Cookie usando le proprietà della classe CookieBuilder, come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie antiforgery.
FormFieldName Nome del campo di modulo nascosto utilizzato dal sistema antiforgery per la generazione dei token antiforgery nelle viste.
HeaderName Nome dell'intestazione utilizzata dal sistema antiforgery. Se null, il sistema considera solo i dati del modulo.
SuppressXFrameOptionsHeader Specifica se eliminare la generazione dell'intestazione X-Frame-Options . Per impostazione predefinita, l'intestazione viene generata con il valore "SAMEORIGIN". Il valore predefinito è false.

Per ulteriori informazioni, vedere CookieAuthenticationOptions.

Generare token antiforgery con IAntiforgery

IAntiforgery fornisce l'API per configurare le funzionalità di protezione contro contraffazioni. IAntiforgery può essere richiesto in Program.cs tramite WebApplication.Services. L'esempio seguente utilizza un middleware collegato alla pagina iniziale dell'app per generare un token antifrode e inviarlo nella risposta come 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);
});

Nell'esempio precedente viene impostato un cookie denominato XSRF-TOKEN. Il client può leggerlo cookie e specificarne il valore come intestazione allegata alle richieste AJAX. Ad esempio, Angular include una protezione XSRF integrata che legge un cookie oggetto denominato XSRF-TOKEN per impostazione predefinita.

Richiedi la convalida anticontraffazione

Il filtro di azione ValidateAntiForgeryToken può essere applicato a una singola azione, a un controller o a livello globale. Le richieste effettuate alle azioni con questo filtro applicato vengono bloccate a meno che la richiesta non includa un token antiforgery valido:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
    // ...

    return RedirectToAction();
}

L'attributo ValidateAntiForgeryToken richiede un token per le richieste ai metodi di azione contrassegnati, incluse le richieste HTTP GET. Se l'attributo ValidateAntiForgeryToken viene applicato nei controller dell'app, può essere sovrascritto con l'attributo IgnoreAntiforgeryToken.

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Anziché applicare in modo ampio l'attributo ValidateAntiForgeryToken e quindi eseguire l'override con gli attributi IgnoreAntiforgeryToken, è possibile usare l'attributo AutoValidateAntiforgeryToken. Questo attributo funziona in modo identico all'attributo ValidateAntiForgeryToken , ad eccezione del fatto che non richiede token per le richieste effettuate usando i metodi HTTP seguenti:

  • GET
  • HEAD
  • OPZIONI
  • TRACE

Si raccomanda l'uso di AutoValidateAntiforgeryToken su larga scala per scenari non API. Questo attributo garantisce che le azioni POST siano protette per impostazione predefinita. L'alternativa consiste nell'ignorare i token antiforgery per impostazione predefinita, salvo che ValidateAntiForgeryToken sia applicato ai singoli metodi di azione. In questo scenario è più probabile che un metodo di azione POST venga lasciato non protetto per errore, lasciando l'app vulnerabile agli attacchi CSRF. Tutti i POST devono inviare il token antiforgery.

Le API non hanno un meccanismo automatico per l'invio dellacookie parte non del token. L'implementazione dipende probabilmente dall'implementazione del codice client. Di seguito sono riportati alcuni esempi:

Esempio a livello di classe:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Esempio globale:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

Sostituire gli attributi antiforgery globali o del controller

Il filtro IgnoreAntiforgeryToken viene usato per eliminare la necessità di un token antiforgery per una determinata azione (o controller). Se applicato, questo filtro sostituisce i filtri ValidateAntiForgeryToken e AutoValidateAntiforgeryToken specificati a un livello superiore (a livello globale o in un controller).

[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
    // ...

    return RedirectToAction();
}

Aggiornare i token dopo l'autenticazione

I token devono essere aggiornati dopo l'autenticazione dell'utente reindirizzando l'utente a una pagina di visualizzazione o Razor pagine.

JavaScript, AJAX e SPA

Nelle app tradizionali basate su HTML, i token antiforgery vengono passati al server utilizzando campi modulo nascosti. Nelle app moderne basate su JavaScript e nelle applicazioni a pagina singola (SPA), molte richieste vengono effettuate in modo programmatico. Queste richieste AJAX possono usare altre tecniche, ad esempio intestazioni di richiesta o cookie, per inviare il token.

Se i cookie vengono usati per archiviare i token di autenticazione e per autenticare le richieste API nel server, CSRF è un potenziale problema. Se l'archiviazione locale viene usata per archiviare il token, la vulnerabilità CSRF potrebbe essere mitigata perché i valori dell'archiviazione locale non vengono inviati automaticamente al server con ogni richiesta. L'uso dell'archiviazione locale per archiviare il token antiforgery nel client e inviare il token come intestazione di richiesta è un approccio consigliato.

Blazor

Per ulteriori informazioni, vedere autenticazione e autorizzazione di ASP.NET CoreBlazor.

JavaScript

Usando JavaScript con visualizzazioni, il token può essere creato usando un servizio dall'interno della visualizzazione. Inserire il IAntiforgery servizio nella visualizzazione e chiamare 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'esempio precedente usa JavaScript per leggere il valore del campo nascosto per l'intestazione POST AJAX.

Questo approccio elimina la necessità di gestire direttamente l'impostazione dei cookie dal server o di leggerli dal client. Tuttavia, quando non è possibile iniettare il servizio IAntiforgery, utilizzare JavaScript per accedere ai token nei cookie.

  • I token di accesso vengono solitamente inviati in una richiesta aggiuntiva al server, tipicamente same-origin.
  • Usa il contenuto di cookie per creare un'intestazione con il valore del token.

Supponendo che lo script invii il token in un'intestazione di richiesta denominata X-XSRF-TOKEN, configurare il servizio antiforgery per cercare l'intestazione X-XSRF-TOKEN :

builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

L'esempio seguente aggiunge un endpoint protetto che scrive il token di richiesta in un codice JavaScript leggibile 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'esempio seguente usa JavaScript per effettuare una richiesta AJAX per ottenere il token e effettuare un'altra richiesta con l'intestazione appropriata:

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}`
}

Nota

Quando il token antifalsificazione viene fornito sia nell'intestazione della richiesta che nel payload del modulo, viene convalidato solo il token presente nell'intestazione.

Antifalsificazione con API minimaliste

Chiama AddAntiforgery e UseAntiforgery(IApplicationBuilder) per registrare i servizi antiforgery in DI. I token antifalsificazione vengono usati per attenuare i attacchi di cross-site request forgery.

var builder = WebApplication.CreateBuilder();

builder.Services.AddAntiforgery();

var app = builder.Build();

app.UseAntiforgery();

app.MapGet("/", () => "Hello World!");

app.Run();

Il middleware antifrode:

Il token antiforgery viene convalidato solo se:

  • L'endpoint contiene i metadati che implementano IAntiforgeryMetadata in cui RequiresValidation=true.
  • Il metodo HTTP associato all'endpoint è un metodo HTTP pertinente. I metodi pertinenti sono tutti metodi HTTP ad eccezione di TRACE, OPTIONS, HEAD e GET.
  • La richiesta è associata a un endpoint valido.

Nota: se abilitato manualmente, il middleware antiforgery deve essere eseguito dopo il middleware di autenticazione e autorizzazione per impedire la lettura dei dati del modulo quando l'utente non è autenticato.

Per impostazione predefinita, le API minime che accettano i dati del modulo richiedono la convalida del token antiforgery.

Si consideri il metodo seguente GenerateForm :

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>
";
}

Il codice precedente ha tre argomenti, l'azione, il token antiforgery e un bool che indica se il token deve essere usato.

Si consideri l'esempio seguente:

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>
    ";
    }
}

Nel codice precedente, invia a:

  • /todo richiede un token antiforgery valido.
  • /todo2 non richiedono un token antiforgery valido dato che DisableAntiforgery viene chiamato.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

app.MapPost("/todo2", ([FromForm] Todo todo) => Results.Ok(todo))
                                                .DisableAntiforgery();

Invio di un POST a:

  • /todo dal modulo generato dall'endpoint / ha esito positivo perché il token antiforgery è valido.
  • /todo dal modulo generato da /SkipToken fallisce perché l'anticontraffazione non è inclusa.
  • /todo2 dal modulo generato dall'endpoint ha esito positivo perché l'antiforgery /DisableAntiforgery non è obbligatorio.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

app.MapPost("/todo2", ([FromForm] Todo todo) => Results.Ok(todo))
                                                .DisableAntiforgery();

Quando un modulo viene inviato senza un token antiforgery valido:

  • Nell'ambiente di sviluppo viene generata un'eccezione.
  • Nell'ambiente di produzione viene registrato un messaggio.

Autenticazione di Windows e cookie anti-forgery

Quando si usa l'autenticazione di Windows, gli endpoint dell'applicazione devono essere protetti dagli attacchi CSRF nello stesso modo usato per i cookie. Il browser invia implicitamente il contesto di autenticazione al server e gli endpoint devono essere protetti contro gli attacchi CSRF.

Estendere l'anti-contraffazione

Il tipo IAntiforgeryAdditionalDataProvider consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF includendo dati aggiuntivi in ogni token. Il GetAdditionalData metodo viene chiamato ogni volta che viene generato un token di campo e il valore restituito viene incorporato all'interno del token generato. Un implementatore può restituire un timestamp, un nonce o qualsiasi altro valore e quindi chiamare ValidateAdditionalData per convalidare questi dati quando il token viene convalidato. Il nome utente del client è già incorporato nei token generati, quindi non è necessario includere queste informazioni. Se un token include dati supplementari ma non IAntiForgeryAdditionalDataProvider è configurato, i dati supplementari non vengono convalidati.

Risorse aggiuntive

La falsificazione di richiesta intersito (nota anche come XSRF o CSRF) è un attacco contro le applicazioni web ospitate, in cui un'applicazione web malevola può interagire nell'interazione tra un browser client e un'applicazione web che considera attendibile quel browser. Questi attacchi sono possibili perché i Web browser inviano automaticamente alcuni tipi di token di autenticazione con ogni richiesta a un sito Web. Questa forma di exploit è nota anche come attacco con un clic o guida di sessione perché l'attacco sfrutta la sessione autenticata in precedenza dell'utente.

Esempio di attacco CSRF:

  1. Un utente accede a www.good-banking-site.example.com utilizzando l'autenticazione tramite form. Il server autentica l'utente e rilascia una risposta che include un'autenticazione cookie. Il sito è vulnerabile all'attacco perché considera attendibile qualsiasi richiesta ricevuta con un'autenticazione cookievalida.

  2. L'utente visita un sito dannoso, www.bad-crook-site.example.com.

    Il sito dannoso, www.bad-crook-site.example.com, contiene un modulo HTML simile all'esempio seguente:

    <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>
    

    Si noti che il modulo action invia al sito vulnerabile, non al sito maligno. Questa è la parte "cross-site" di CSRF.

  3. L'utente seleziona il pulsante invia. Il browser effettua la richiesta e include automaticamente l'autenticazione cookie per il dominio richiesto, www.good-banking-site.example.com.

  4. La richiesta viene eseguita sul www.good-banking-site.example.com server con il contesto di autenticazione dell'utente e può eseguire qualsiasi azione che un utente autenticato possa eseguire.

Oltre allo scenario in cui l'utente seleziona il pulsante per inviare il modulo, il sito dannoso potrebbe:

  • Eseguire uno script che invia automaticamente il modulo.
  • Inviare il modulo come richiesta AJAX.
  • Nascondere il modulo usando CSS.

Questi scenari alternativi non richiedono alcuna azione o input da parte dell'utente diverso da visitare inizialmente il sito dannoso.

L'uso di HTTPS non impedisce un attacco CSRF. Il sito dannoso può inviare una richiesta https://www.good-banking-site.example.com/ con la stessa facilità con cui può inviare una richiesta non sicura.

Alcuni attacchi usano endpoint che rispondono alle richieste GET, nel qual caso è possibile usare un tag immagine per eseguire l'azione. Questa forma di attacco è comune nei siti del forum che consentono immagini ma bloccano JavaScript. Le app che cambiano lo stato nelle richieste GET, in cui le variabili o le risorse vengono modificate, sono vulnerabili ad attacchi dannosi. Le richieste GET che cambiano lo stato non sono sicure. Una procedura consigliata consiste nel non modificare mai lo stato in una richiesta GET.

Gli attacchi CSRF sono possibili contro le app Web che usano cookie per l'autenticazione perché:

  • I browser archivia i cookie rilasciati da un'app Web.
  • I cookie archiviati includono cookie di sessione per gli utenti autenticati.
  • I browser inviano tutti i cookie associati a un dominio all'app Web ogni richiesta indipendentemente dalla modalità di generazione della richiesta all'app all'interno del browser.

Tuttavia, gli attacchi CSRF non sono limitati a sfruttare i cookie. Ad esempio, anche l'autenticazione di base e digest è vulnerabile. Dopo che un utente accede con l'autenticazione di base o digest, il browser invia automaticamente le credenziali fino al termine della sessione.

In questo contesto, la sessione fa riferimento alla sessione sul lato client durante la quale l'utente viene autenticato. Non è correlato alle sessioni lato server o al middleware di sessione di ASP.NET Core.

Gli utenti possono proteggersi dalle vulnerabilità CSRF adottando precauzioni:

  • Disconnettersi dalle app Web al termine dell'uso.
  • Cancellare periodicamente i cookie del browser.

Tuttavia, le vulnerabilità CSRF sono fondamentalmente un problema con l'app Web, non con l'utente finale.

Nozioni fondamentali di autenticazione

CookieL'autenticazione basata su è una forma comune di autenticazione. I sistemi di autenticazione basati su token sono in continua crescita, in particolare per le applicazioni a pagina singola.

Quando un utente esegue l'autenticazione usando il nome utente e la password, viene rilasciato un token contenente un ticket di autenticazione che può essere usato per l'autenticazione e l'autorizzazione. Il token viene archiviato come oggetto cookie inviato con ogni richiesta eseguita dal client. La generazione e la convalida di cookie vengono eseguite dal middleware di autenticazione Cookie. Il middleware serializza un principale utente in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida cookie, ricrea il principale e assegna il principale alla proprietà HttpContext.User.

Autenticazione basata su token

Quando un utente viene autenticato, viene rilasciato un token (non un token antiforgery). Il token contiene informazioni sull'utente sotto forma di attestazioni o come un token di riferimento che indirizza l'app allo stato utente gestito all'interno dell'app. Quando un utente tenta di accedere a una risorsa che richiede l'autenticazione, il token viene inviato all'app con un'intestazione di autorizzazione aggiuntiva sotto forma di token Bearer. Questo approccio rende l'app senza stato. In ogni richiesta successiva, il token viene passato nella richiesta di convalida lato server. Questo token non è crittografato, ma viene codificato. Nel server il token viene decodificato per accedere alle informazioni. Per inviare il token alle richieste successive, archiviare il token nella risorsa di archiviazione locale del browser. Posizionare un token nell'archiviazione locale del browser e recuperarlo e usarlo come token di connessione fornisce protezione dagli attacchi CSRF. Tuttavia, se l'app è vulnerabile all'inserimento di script tramite XSS o un file Javascript esterno compromesso, un cyberattacker potrebbe recuperare qualsiasi valore dall'archiviazione locale e inviarlo a se stessi. ASP.NET Core codifica tutti gli output lato server dalle variabili per impostazione predefinita, riducendo il rischio di XSS. Se si esegue l'override di questo comportamento usando Html.Raw o codice personalizzato con input non attendibile, è possibile aumentare il rischio di XSS.

Non preoccuparsi della vulnerabilità CSRF se il token viene archiviato nella risorsa di archiviazione locale del browser. CSRF è un problema quando il token viene archiviato in un cookie. Per ulteriori informazioni, consulta il problema su GitHub SPA code sample aggiunge due cookie.

Più app ospitate in un dominio

Gli ambienti di hosting condivisi sono vulnerabili all'hijacking della sessione, all'accesso CSRF e ad altri attacchi.

Anche se example1.contoso.net e example2.contoso.net sono host diversi, esiste una relazione di trust implicita tra gli host nel *.contoso.net dominio. Questa relazione di trust implicita consente agli host potenzialmente non attendibili di influire sui cookie degli altri (i criteri di stessa origine che regolano le richieste AJAX non si applicano necessariamente ai cookie HTTP).

Gli attacchi che sfruttano i cookie attendibili tra app ospitate nello stesso dominio possono essere impediti non condividendo domini. Quando ogni app è ospitata nel proprio dominio, non esiste alcuna relazione di trust implicita cookie da sfruttare.

Antifalsificazione in ASP.NET Core

Avviso

ASP.NET Core implementa l'antiforgeria usando ASP.NET Core Data Protection. Lo stack di protezione dati deve essere configurato per funzionare in una server farm. Per altre informazioni, vedere Configurazione della protezione dei dati.

Il middleware antiforgery viene aggiunto al contenitore per l'iniezione delle dipendenze quando viene chiamata una delle API seguenti in Program.cs:

FormTagHelper inserisce token antifalsificazione negli elementi del modulo HTML. Il markup seguente in un file Razor genera automaticamente i token antiforgery.

<form method="post">
    <!-- ... -->
</form>

Analogamente, IHtmlHelper.BeginForm genera token anticontraffazione di default se il metodo del modulo non è GET.

La generazione automatica di token antiforgery per gli elementi del modulo HTML si verifica quando il <form> tag contiene l'attributo method="post" e uno dei seguenti è true:

  • L'attributo action è vuoto (action="").
  • L'attributo action non viene fornito (<form method="post">).

La generazione automatica di token antiforgery per gli elementi del modulo HTML può essere disabilitata:

  • Disabilitare in modo esplicito i token antiforgery con l'attributo asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • L'elemento

    è escluso dai Tag Helper usando il simbolo di disattivazione: !

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Rimuovere l'oggetto FormTagHelper dalla visualizzazione. L'oggetto FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente nella vista Razor:

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

Nota

Razor Le pagine vengono protette automaticamente da XSRF/CSRF. Per altre informazioni, vedere XSRF/CSRF e Razor Pages.

L'approccio più comune per difendersi dagli attacchi CSRF consiste nell'usare il modello token di sincronizzazione (STP). STP viene usato quando l'utente richiede una pagina con dati del modulo:

  1. Il server invia un token associato all'identità dell'utente corrente al client.
  2. Il client invia il token al server per la verifica.
  3. Se il server riceve un token che non corrisponde all'identità dell'utente autenticato, la richiesta viene rifiutata.

Il token è univoco e imprevedibile. Il token può essere usato anche per garantire una sequenziazione corretta di una serie di richieste ( ad esempio, verificando la sequenza di richiesta di: pagina 1 > pagina 2 > pagina 3). Tutti i moduli nei modelli ASP.NET Core MVC e Razor Pages generano token antiforgery. La coppia di esempi di visualizzazione seguente genera token antiforgery:

<form asp-action="Index" asp-controller="Home" method="post">
    <!-- ... -->
</form>

@using (Html.BeginForm("Index", "Home"))
{
    <!-- ... -->
}

Aggiungere in modo esplicito un token antiforgery a un elemento <form> senza usare gli Helper Tag, con l'helper HTML @Html.AntiForgeryToken.

<form asp-action="Index" asp-controller="Home" method="post">
    @Html.AntiForgeryToken()

    <!-- ... -->
</form>

In ognuno dei casi precedenti, ASP.NET Core aggiunge un campo modulo nascosto simile all'esempio seguente:

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core include tre filtri per l'uso di token antifalsificazione:

Anti-contraffazione con AddControllers

La chiamata AddControllersnon abilita i token antiforgery. AddControllersWithViews deve essere chiamato per avere il supporto predefinito del token antiforgery.

Più schede del browser e schema del token di sincronizzazione

Con il modello token del programma di sincronizzazione, solo la pagina caricata più di recente contiene un token antiforgery valido. L'uso di più schede può essere problematico. Ad esempio, se un utente apre più schede:

  • Solo la scheda caricata più di recente contiene un token antiforgery valido.
  • Le richieste effettuate dalle schede caricate in precedenza hanno esito negativo con un errore: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Si considerino modelli alternativi di protezione CSRF se questo costituisce un problema.

Configurare l'antifrode con AntiforgeryOptions

Personalizzare AntiforgeryOptions in Program.cs:

builder.Services.AddAntiforgery(options =>
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

Impostare le proprietà antiforgery Cookie usando le proprietà della classe CookieBuilder, come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Stabilisce le impostazioni utilizzate per creare i cookie antifalsificazione.
FormFieldName Nome del campo modulo nascosto utilizzato dal sistema antiforgery per eseguire il rendering dei token antiforgery nelle visualizzazioni.
HeaderName Nome dell'intestazione utilizzata dal sistema di anticontraffazione. Se null, il sistema considera solo i dati del modulo.
SuppressXFrameOptionsHeader Specifica se eliminare la generazione dell'intestazione X-Frame-Options . Per impostazione predefinita, l'intestazione viene generata con il valore "SAMEORIGIN". Il valore predefinito è false.

Per ulteriori informazioni, vedere CookieAuthenticationOptions.

Generare token anticontraffazione con IAntiforgery

IAntiforgery fornisce l'API per configurare le funzionalità antiforgery. IAntiforgery può essere richiesto in Program.cs tramite WebApplication.Services. L'esempio seguente utilizza un middleware collegato alla pagina iniziale dell'app per generare un token antifrode e inviarlo nella risposta come 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);
});

Nell'esempio precedente viene impostato un cookie denominato XSRF-TOKEN. Il client può leggerlo cookie e specificarne il valore come intestazione allegata alle richieste AJAX. Ad esempio, Angular include la protezione XSRF predefinita che legge un cookie oggetto denominato XSRF-TOKEN per impostazione predefinita.

Richiedi convalida anticontraffazione

Il filtro di azione ValidateAntiForgeryToken può essere applicato a una singola azione, a un controller o a livello globale. Le richieste effettuate alle azioni con questo filtro applicato vengono bloccate a meno che la richiesta non includa un token antiforgery valido:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
    // ...

    return RedirectToAction();
}

L'attributo ValidateAntiForgeryToken richiede un token per le richieste ai metodi di azione contrassegnati, incluse le richieste HTTP GET. Se l'attributo ValidateAntiForgeryToken viene applicato nell'ambito dei controller dell'app, può essere sovrascritto con l'attributo IgnoreAntiforgeryToken.

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Anziché applicare l'attributo ValidateAntiForgeryToken a livello generale e quindi eseguire l'override con gli attributi IgnoreAntiforgeryToken, è possibile usare l'attributo AutoValidateAntiforgeryToken. Questo attributo funziona in modo identico all'attributo ValidateAntiForgeryToken , ad eccezione del fatto che non richiede token per le richieste effettuate usando i metodi HTTP seguenti:

  • GET
  • HEAD
  • OPZIONI
  • TRACE

È consigliabile l'uso di AutoValidateAntiforgeryToken su larga scala per scenari non relativi a API. Questo attributo garantisce che le azioni POST siano protette per impostazione predefinita. L'alternativa è ignorare i token antiforgery per impostazione predefinita, a meno che ValidateAntiForgeryToken venga applicato ai singoli metodi di azione. In questo scenario è più probabile che un metodo di azione POST venga lasciato non protetto per errore, lasciando l'app vulnerabile agli attacchi CSRF. Tutti i POST devono inviare il token antiforgery.

Le API non hanno un meccanismo automatico per l'invio dellacookie parte non del token. L'implementazione dipende probabilmente dall'implementazione del codice client. Di seguito sono riportati alcuni esempi:

Esempio a livello di classe:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Esempio globale:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

Effettuare l'override degli attributi antiforgery globali o del controller

Il filtro IgnoreAntiforgeryToken viene usato per eliminare la necessità di un token antiforgery per una determinata azione (o controller). Se applicato, questo filtro esegue l'override dei filtri ValidateAntiForgeryToken e AutoValidateAntiforgeryToken specificati a un livello superiore (globalmente o su un controller).

[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
    // ...

    return RedirectToAction();
}

Aggiornare i token dopo l'autenticazione

I token devono essere aggiornati dopo l'autenticazione dell'utente reindirizzando l'utente a una pagina di visualizzazione o Razor pagine.

JavaScript, AJAX e SPA

Nelle app tradizionali basate su HTML, i token antiforgery vengono passati al server usando campi nascosti del modulo. Nelle app moderne basate su JavaScript e nelle applicazioni a pagina singola, molte richieste vengono effettuate a livello di codice. Queste richieste AJAX possono usare altre tecniche (ad esempio intestazioni di richiesta o cookie) per inviare il token.

Se i cookie vengono usati per archiviare i token di autenticazione e per autenticare le richieste API nel server, CSRF è un potenziale problema. Se l'archiviazione locale viene usata per archiviare il token, la vulnerabilità CSRF potrebbe essere mitigata perché i valori dell'archiviazione locale non vengono inviati automaticamente al server con ogni richiesta. L'uso dell'archiviazione locale per archiviare il token antiforgery nel client e inviare il token come intestazione di richiesta è un approccio consigliato.

JavaScript

Usando JavaScript con visualizzazioni, il token può essere creato usando un servizio dall'interno della visualizzazione. Inserire il IAntiforgery servizio nella visualizzazione e chiamare 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'esempio precedente usa JavaScript per leggere il valore del campo nascosto per l'intestazione POST AJAX.

Questo approccio elimina la necessità di gestire direttamente l'impostazione dei cookie dal server o di leggerli dal client. Tuttavia, quando l'iniezione del servizio IAntiforgery non è possibile, usa JavaScript per accedere ai token nei cookie.

  • I token di accesso in una richiesta aggiuntiva al server sono generalmente same-origin.
  • Usare i contenuti di cookie per creare un'intestazione con il valore del token.

Nel caso in cui lo script invii il token in un'intestazione di richiesta chiamata X-XSRF-TOKEN, configurare il servizio anti-contraffazione per cercare l'intestazione X-XSRF-TOKEN.

builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

L'esempio seguente aggiunge un endpoint protetto che scrive il token di richiesta in un codice JavaScript leggibile 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'esempio seguente usa JavaScript per effettuare una richiesta AJAX per ottenere il token e effettuare un'altra richiesta con l'intestazione appropriata:

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}`
}

Nota

Quando il token antifalsificazione è presente sia nell'header della richiesta che nel payload del modulo, viene convalidato solo il token presente nell'header.

Protezione antifrode con API minimali

Minimal APIs non supportano l'utilizzo dei filtri inclusi (ValidateAntiForgeryToken, AutoValidateAntiforgeryToken, IgnoreAntiforgeryToken), ma IAntiforgery fornisce le API necessarie per convalidare una richiesta.

Nell'esempio seguente viene creato un filtro che convalida il token antiforgery:

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);

        });
    }
}

Il filtro può quindi essere applicato a un endpoint:

app.MapPost("api/upload", (IFormFile name) => Results.Accepted())
    .RequireAuthorization()
    .ValidateAntiforgery();

Autenticazione Windows e cookie anti-manomissione

Quando si usa l'autenticazione di Windows, gli endpoint dell'applicazione devono essere protetti dagli attacchi CSRF nello stesso modo usato per i cookie. Il browser invia in modo implicito il contesto di autenticazione al server e gli endpoint devono essere protetti dagli attacchi CSRF.

Estendere le misure antifalsificazione

Il tipo IAntiforgeryAdditionalDataProvider consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF trasmettendo e ricevendo dati aggiuntivi con ogni token. Il GetAdditionalData metodo viene chiamato ogni volta che viene generato un token di campo e il valore restituito viene incorporato all'interno del token generato. Un implementatore può restituire un timestamp, un nonce o qualsiasi altro valore e quindi chiamare ValidateAdditionalData per convalidare questi dati quando il token viene convalidato. Il nome utente del client è già incorporato nei token generati, quindi non è necessario includere queste informazioni. Se un token include dati supplementari ma non IAntiForgeryAdditionalDataProvider è configurato, i dati supplementari non vengono convalidati.

Risorse aggiuntive

La falsificazione di richieste intersito (nota anche come XSRF o CSRF) è un attacco contro le applicazioni web ospitate sul Web in cui un'applicazione web dannosa può influenzare l'interazione tra un browser client e un'applicazione web che si fida di quel browser. Questi attacchi sono possibili perché i Web browser inviano automaticamente alcuni tipi di token di autenticazione con ogni richiesta a un sito Web. Questa forma di exploit è nota anche come attacco con un clic o guida di sessione perché l'attacco sfrutta la sessione autenticata in precedenza dell'utente.

Un esempio di attacco CSRF:

  1. Un utente accede a www.good-banking-site.example.com utilizzando l'autenticazione basata su moduli. Il server autentica l'utente e rilascia una risposta che include un'autenticazione cookie. Il sito è vulnerabile all'attacco perché considera attendibile qualsiasi richiesta ricevuta con un'autenticazione cookievalida.

  2. L'utente visita un sito dannoso, www.bad-crook-site.example.com.

    Il sito dannoso, www.bad-crook-site.example.com, contiene un modulo HTML simile all'esempio seguente:

    <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>
    

    Si noti che il modulo action invia al sito vulnerabile, non al sito dannoso. Questa è la parte "cross-site" di CSRF.

  3. L'utente seleziona il pulsante invia. Il browser effettua la richiesta e include automaticamente l'autenticazione cookie per il dominio richiesto, www.good-banking-site.example.com.

  4. La richiesta viene eseguita sul www.good-banking-site.example.com server con il contesto di autenticazione dell'utente e può eseguire qualsiasi azione che un utente autenticato possa eseguire.

Oltre allo scenario in cui l'utente seleziona il pulsante per inviare il modulo, il sito dannoso potrebbe:

  • Eseguire uno script che invia automaticamente il modulo.
  • Inviare il modulo come richiesta AJAX.
  • Nascondere il modulo usando CSS.

Questi scenari alternativi non richiedono alcuna azione o input da parte dell'utente diverso da visitare inizialmente il sito dannoso.

L'uso di HTTPS non impedisce un attacco CSRF. Il sito dannoso può inviare una richiesta https://www.good-banking-site.example.com/ con la stessa facilità con cui può inviare una richiesta non sicura.

Alcuni attacchi usano endpoint che rispondono alle richieste GET, nel qual caso è possibile usare un tag immagine per eseguire l'azione. Questa forma di attacco è comune nei siti del forum che consentono immagini ma bloccano JavaScript. Le app che cambiano lo stato nelle richieste GET, in cui le variabili o le risorse vengono modificate, sono vulnerabili ad attacchi dannosi. Le richieste GET che cambiano lo stato non sono sicure. Una procedura consigliata consiste nel non modificare mai lo stato in una richiesta GET.

Gli attacchi CSRF sono possibili contro le app Web che usano cookie per l'autenticazione perché:

  • I browser archivia i cookie rilasciati da un'app Web.
  • I cookie archiviati includono cookie di sessione per gli utenti autenticati.
  • I browser inviano tutti i cookie associati a un dominio all'app Web ogni richiesta indipendentemente dalla modalità di generazione della richiesta all'app all'interno del browser.

Tuttavia, gli attacchi CSRF non sono limitati a sfruttare i cookie. Ad esempio, anche l'autenticazione di base e digest è vulnerabile. Dopo che un utente accede con l'autenticazione di base o digest, il browser invia automaticamente le credenziali fino al termine della sessione.

In questo contesto, la sessione fa riferimento alla sessione sul lato client durante la quale l'utente viene autenticato. Non è correlato alle sessioni lato server o al Middleware delle sessioni di ASP.NET Core.

Gli utenti possono proteggersi dalle vulnerabilità CSRF adottando precauzioni:

  • Disconnettersi dalle app Web al termine dell'uso.
  • Cancellare periodicamente i cookie del browser.

Tuttavia, le vulnerabilità CSRF sono fondamentalmente un problema con l'app Web, non con l'utente finale.

Nozioni fondamentali di autenticazione

L'autenticazione basata su Cookie è una forma comune di autenticazione. I sistemi di autenticazione basati su token sono in continua crescita, in particolare per le applicazioni a pagina singola.

Quando un utente esegue l'autenticazione usando il nome utente e la password, viene rilasciato un token contenente un ticket di autenticazione che può essere usato per l'autenticazione e l'autorizzazione. Il token viene archiviato come oggetto cookie inviato con ogni richiesta eseguita dal client. La generazione e la convalida di questo cookie vengono eseguite dal Cookie Middleware di Autenticazione. Il middleware serializza un principale utente in un cookie crittografato. Nelle richieste successive, il middleware convalida cookie, ricrea il principale e assegna il principale alla proprietà HttpContext.User.

Autenticazione basata su token

Quando un utente viene autenticato, viene rilasciato un token (non un token antiforgery). Il token contiene informazioni utente sotto forma di attestazioni o di un token di riferimento che indirizza l'app allo stato dell'utente mantenuto dall'app. Quando un utente tenta di accedere a una risorsa che richiede l'autenticazione, il token viene inviato all'app con un'intestazione di autorizzazione aggiuntiva sotto forma di token Bearer. Questo approccio rende l'app priva di stato. In ogni richiesta successiva, il token viene passato nella richiesta di convalida lato server. Questo token non è crittografato, ma viene codificato. Nel server il token viene decodificato per accedere alle informazioni. Per inviare il token alle richieste successive, archiviare il token nella risorsa di archiviazione locale del browser. Non preoccuparsi della vulnerabilità CSRF se il token viene archiviato nella risorsa di archiviazione locale del browser. CSRF è una preoccupazione quando il token viene archiviato in un cookie. Per altre informazioni, vedere l'esempio di codice SPA relativo al problema di GitHub che aggiunge due cookie.

Più app ospitate in un dominio

Gli ambienti di hosting condivisi sono vulnerabili all'hijacking della sessione, all'accesso CSRF e ad altri attacchi.

Anche se example1.contoso.net e example2.contoso.net sono host diversi, esiste una relazione di trust implicita tra gli host nel *.contoso.net dominio. Questa relazione di trust implicita consente agli host potenzialmente non attendibili di influire sui cookie degli altri (i criteri di stessa origine che regolano le richieste AJAX non si applicano necessariamente ai cookie HTTP).

Gli attacchi che sfruttano i cookie attendibili tra app ospitate nello stesso dominio possono essere impediti non condividendo domini. Quando ogni app è ospitata nel proprio dominio, non esiste alcuna relazione di trust implicita cookie da sfruttare.

Anticontraffazione in ASP.NET Core

Avviso

ASP.NET Core implementa l'antiforgeria usando ASP.NET Core Data Protection. Lo stack di protezione dati deve essere configurato per funzionare in una server farm. Per altre informazioni, vedere Configurazione della protezione dei dati.

Il middleware antiforgery viene aggiunto al contenitore di iniezione delle dipendenze quando viene chiamata una delle API seguenti in Program.cs.

FormTagHelper inserisce token antifalsificazione negli elementi del modulo HTML. Il markup seguente in un file Razor genera automaticamente i token antifalsificazione.

<form method="post">
    <!-- ... -->
</form>

Analogamente, IHtmlHelper.BeginForm genera token antifalsificazione per impostazione predefinita se il metodo del form non è GET.

La generazione automatica di token antiforgery per gli elementi del modulo HTML si verifica quando il <form> tag contiene l'attributo method="post" e uno dei seguenti è true:

  • L'attributo action è vuoto (action="").
  • L'attributo action non viene fornito (<form method="post">).

La generazione automatica di token antiforgery per gli elementi del modulo HTML può essere disabilitata:

  • Disabilitare in modo esplicito i token antiforgery con l'attributo asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • L'elemento del modulo è escluso dai Tag Helpers utilizzando il simbolo di esclusione.

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Rimuovere l'oggetto FormTagHelper dalla visualizzazione. L'oggetto FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente nella visualizzazione Razor.

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

Nota

Razor Le pagine vengono protette automaticamente da XSRF/CSRF. Per altre informazioni, vedere XSRF/CSRF e Razor Pages.

L'approccio più comune per difendersi dagli attacchi CSRF consiste nell'usare il Pattern Token di Sincronizzazione (STP). STP viene usato quando l'utente richiede una pagina con dati del modulo:

  1. Il server invia un token associato all'identità dell'utente corrente al client.
  2. Il client invia il token al server per la verifica.
  3. Se il server riceve un token che non corrisponde all'identità dell'utente autenticato, la richiesta viene rifiutata.

Il token è univoco e imprevedibile. Il token può essere usato anche per garantire una sequenziazione corretta di una serie di richieste ( ad esempio, verificando la sequenza di richiesta di: pagina 1 > pagina 2 > pagina 3). Tutte le form nei modelli ASP.NET Core MVC e nelle pagine Razor generano token antiforgery. La coppia di esempi di visualizzazione seguente genera token antiforgery:

<form asp-action="Index" asp-controller="Home" method="post">
    <!-- ... -->
</form>

@using (Html.BeginForm("Index", "Home"))
{
    <!-- ... -->
}

Aggiungere in modo esplicito un token antiforgery a un elemento <form> senza usare i tag helper con l'helper HTML @Html.AntiForgeryToken.

<form asp-action="Index" asp-controller="Home" method="post">
    @Html.AntiForgeryToken()

    <!-- ... -->
</form>

In ognuno dei casi precedenti, ASP.NET Core aggiunge un campo modulo nascosto simile all'esempio seguente:

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core include tre filtri per l'uso di token antiforgery:

Antiforgery con AddControllers

La chiamata AddControllersnon abilita i token antiforgery. AddControllersWithViews deve essere chiamato per avere il supporto predefinito del token antiforgery.

Più schede del browser e modello di token del programma di sincronizzazione

Con il modello token del programma di sincronizzazione, solo la pagina caricata più di recente contiene un token antiforgery valido. L'uso di più schede può essere problematico. Ad esempio, se un utente apre più schede:

  • Solo la scheda caricata più di recente contiene un token antiforgery valido.
  • Le richieste effettuate dalle schede caricate in precedenza hanno esito negativo con un errore: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Se questo rappresenta un problema, prendere in considerazione modelli di protezione CSRF alternativi.

Configura l'antifalsificazione con AntiforgeryOptions

Personalizzare AntiforgeryOptions in Program.cs:

builder.Services.AddAntiforgery(options =>
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

Impostare le proprietà antiforgery Cookie usando le proprietà della classe CookieBuilder, come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie anti-contraffazione.
FormFieldName Nome del campo di modulo nascosto utilizzato dal sistema antiforgery per renderizzare i token antiforgery nelle viste.
HeaderName Nome dell'intestazione utilizzata dal sistema anti-falsificazione. Se null, il sistema considera solo i dati del modulo.
SuppressXFrameOptionsHeader Specifica se eliminare la generazione dell'intestazione X-Frame-Options . Per impostazione predefinita, l'intestazione viene generata con il valore "SAMEORIGIN". Il valore predefinito è false.

Per ulteriori informazioni, vedere CookieAuthenticationOptions.

Generare token anti-falsificazione con IAntiforgery

IAntiforgery fornisce l'API per configurare le funzionalità anti-contraffazione. IAntiforgery può essere richiesto in Program.cs tramite WebApplication.Services. L'esempio seguente utilizza un middleware collegato alla pagina iniziale dell'app per generare un token antifrode e inviarlo nella risposta come 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);
});

Nell'esempio precedente viene impostato un cookie denominato XSRF-TOKEN. Il client può leggerlo cookie e specificarne il valore come intestazione collegata alle richieste AJAX. Ad esempio, Angular include la protezione XSRF integrata che legge un cookie denominato XSRF-TOKEN di default.

Richiedi convalida anticontraffazione

Il filtro di azione ValidateAntiForgeryToken può essere applicato a una singola azione, a un controller o a livello globale. Le richieste effettuate alle azioni con questo filtro applicato vengono bloccate a meno che la richiesta non includa un token antiforgery valido:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
    // ...

    return RedirectToAction();
}

L'attributo ValidateAntiForgeryToken richiede un token per le richieste ai metodi di azione contrassegnati, incluse le richieste HTTP GET. Se l'attributo ValidateAntiForgeryToken viene applicato su tutti i controller dell'app, può essere sovrascritto con l'attributo IgnoreAntiforgeryToken.

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Anziché applicare l'attributo in modo generale e poi sovrascriverlo con gli attributi ValidateAntiForgeryTokenIgnoreAntiforgeryToken, è possibile usare l'attributo AutoValidateAntiforgeryToken. Questo attributo funziona in modo identico all'attributo ValidateAntiForgeryToken , ad eccezione del fatto che non richiede token per le richieste effettuate usando i metodi HTTP seguenti:

  • GET
  • HEAD
  • OPZIONI
  • traccia

È consigliabile usare AutoValidateAntiforgeryToken in modo esteso per scenari che non coinvolgono API. Questo attributo garantisce che le azioni POST siano protette per impostazione predefinita. L'alternativa consiste nell'ignorare i token antiforgery per impostazione predefinita, a meno che ValidateAntiForgeryToken non venga applicato ai singoli metodi di azione. In questo scenario è più probabile che un metodo di azione POST venga lasciato non protetto per errore, lasciando l'app vulnerabile agli attacchi CSRF. Tutti i POST devono inviare il token antiforgery.

Le API non hanno un meccanismo automatico per l'invio dellacookie parte non del token. L'implementazione dipende probabilmente dall'implementazione del codice client. Di seguito sono riportati alcuni esempi:

Esempio a livello di classe:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Esempio globale:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

Sostituire gli attributi antiforgery globali o del controller

Il filtro IgnoreAntiforgeryToken viene usato per eliminare la necessità di un token antiforgery per una determinata azione (o controller). Quando applicato, questo filtro esegue l'override dei filtri ValidateAntiForgeryToken e AutoValidateAntiforgeryToken specificati a un livello superiore (a livello globale, o in un controller).

[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
    // ...

    return RedirectToAction();
}

Aggiornare i token dopo l'autenticazione

I token devono essere aggiornati dopo l'autenticazione dell'utente reindirizzando l'utente a una pagina di visualizzazione o una pagina Razor Pages.

JavaScript, AJAX e SPA

Nelle app tradizionali in HTML, i token antiforgery vengono passati al server tramite campi di modulo nascosti. Nelle app moderne basate su JavaScript e nelle applicazioni a pagina singola, molte richieste vengono effettuate programmaticamente. Queste richieste AJAX possono usare altre tecniche (ad esempio intestazioni di richiesta o cookie) per inviare il token.

Se i cookie vengono usati per archiviare i token di autenticazione e per autenticare le richieste API nel server, CSRF è un potenziale problema. Se l'archiviazione locale viene usata per archiviare il token, la vulnerabilità CSRF potrebbe essere mitigata perché i valori dell'archiviazione locale non vengono inviati automaticamente al server con ogni richiesta. L'uso dell'archiviazione locale per archiviare il token antiforgery nel client e inviare il token come intestazione di richiesta è un approccio consigliato.

JavaScript

Usando JavaScript con visualizzazioni, il token può essere creato usando un servizio dall'interno della visualizzazione. Inserire il IAntiforgery servizio nella visualizzazione e chiamare 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'esempio precedente usa JavaScript per leggere il valore del campo nascosto per l'intestazione POST AJAX.

Questo approccio elimina la necessità di gestire direttamente l'impostazione dei cookie dal server o di leggerli dal client. Tuttavia, quando iniettare il servizio IAntiforgery non è possibile, JavaScript può anche accedere al token nei cookie, ottenuto da una richiesta aggiuntiva al server (in genere same-origin), e usare il contenuto di cookie per creare un'intestazione con il valore del token.

Supponendo che lo script invii il token in un'intestazione di richiesta chiamata X-XSRF-TOKEN, configura il servizio antiforgery per individuare l'intestazione X-XSRF-TOKEN.

builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

L'esempio seguente aggiunge un endpoint protetto che scriverà il token di richiesta in un codice JavaScript leggibile 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'esempio seguente usa JavaScript per effettuare una richiesta AJAX per ottenere il token e effettuare un'altra richiesta con l'intestazione appropriata:

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}`
}

cookie di autenticazione di Windows e cookie antifalsificazione

Quando si usa l'autenticazione di Windows, gli endpoint dell'applicazione devono essere protetti dagli attacchi CSRF nello stesso modo usato per i cookie. Il browser invia in modo implicito il contesto di autenticazione al server e quindi gli endpoint devono essere protetti dagli attacchi CSRF.

Estendere l'anti-falsificazione

Il IAntiforgeryAdditionalDataProvider tipo consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF trasferendo i dati aggiuntivi in ogni token. Il GetAdditionalData metodo viene chiamato ogni volta che viene generato un token di campo e il valore restituito viene incorporato all'interno del token generato. Un implementatore può restituire un timestamp, un nonce o qualsiasi altro valore e quindi chiamare ValidateAdditionalData per convalidare questi dati quando il token viene convalidato. Il nome utente del client è già incorporato nei token generati, quindi non è necessario includere queste informazioni. Se un token include dati supplementari ma non IAntiForgeryAdditionalDataProvider è configurato, i dati supplementari non vengono convalidati.

Risorse aggiuntive

La Cross-Site Request Forgery (nota anche come XSRF o CSRF) è un attacco contro le applicazioni web ospitate in cui un'applicazione web dannosa può influenzare l'interazione tra un browser client e un'applicazione web che considera attendibile quel browser. Questi attacchi sono possibili perché i Web browser inviano automaticamente alcuni tipi di token di autenticazione con ogni richiesta a un sito Web. Questa forma di exploit è nota anche come attacco con un clic o guida di sessione perché l'attacco sfrutta la sessione autenticata in precedenza dell'utente.

Esempio di attacco CSRF:

  1. Un utente accede a www.good-banking-site.example.com utilizzando l'autenticazione basata su moduli. Il server autentica l'utente e rilascia una risposta che include un'autenticazione cookie. Il sito è vulnerabile all'attacco perché considera attendibile qualsiasi richiesta ricevuta con un'autenticazione cookievalida.

  2. L'utente visita un sito dannoso, www.bad-crook-site.example.com.

    Il sito dannoso, www.bad-crook-site.example.com, contiene un modulo HTML simile all'esempio seguente:

    <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>
    

    Si noti che il modulo invia al sito vulnerabile, non al sito dannoso. Questa è la parte "cross-site" di CSRF.

  3. L'utente seleziona il pulsante invia. Il browser effettua la richiesta e include automaticamente l'autenticazione cookie per il dominio richiesto, www.good-banking-site.example.com.

  4. La richiesta viene eseguita sul www.good-banking-site.example.com server con il contesto di autenticazione dell'utente e può eseguire qualsiasi azione che un utente autenticato possa eseguire.

Oltre allo scenario in cui l'utente seleziona il pulsante per inviare il modulo, il sito dannoso potrebbe:

  • Eseguire uno script che invia automaticamente il modulo.
  • Inviare il modulo come richiesta AJAX.
  • Nascondere il modulo usando CSS.

Questi scenari alternativi non richiedono alcuna azione o input da parte dell'utente diverso da visitare inizialmente il sito dannoso.

L'uso di HTTPS non impedisce un attacco CSRF. Il sito dannoso può inviare una richiesta https://www.good-banking-site.example.com/ con la stessa facilità con cui può inviare una richiesta non sicura.

Alcuni attacchi usano endpoint che rispondono alle richieste GET, nel qual caso è possibile usare un tag immagine per eseguire l'azione. Questa forma di attacco è comune nei siti del forum che consentono immagini ma bloccano JavaScript. Le app che cambiano lo stato nelle richieste GET, in cui le variabili o le risorse vengono modificate, sono vulnerabili ad attacchi dannosi. Le richieste GET che cambiano lo stato non sono sicure. Una procedura consigliata consiste nel non modificare mai lo stato in una richiesta GET.

Gli attacchi CSRF sono possibili contro le app Web che usano cookie per l'autenticazione perché:

  • I browser archivia i cookie rilasciati da un'app Web.
  • I cookie archiviati includono cookie di sessione per gli utenti autenticati.
  • I browser inviano tutti i cookie associati a un dominio all'app Web ogni richiesta indipendentemente dalla modalità di generazione della richiesta all'app all'interno del browser.

Tuttavia, gli attacchi CSRF non sono limitati a sfruttare i cookie. Ad esempio, anche l'autenticazione di base e digest è vulnerabile. Dopo che un utente accede con l'autenticazione di base o digest, il browser invia automaticamente le credenziali fino al termine della sessione.

In questo contesto, la sessione fa riferimento alla sessione sul lato client durante la quale l'utente viene autenticato. Non è correlato alle sessioni lato server o al middleware di sessione di ASP.NET Core.

Gli utenti possono proteggersi dalle vulnerabilità CSRF adottando precauzioni:

  • Disconnettersi dalle app Web al termine dell'uso.
  • Cancellare periodicamente i cookie del browser.

Tuttavia, le vulnerabilità CSRF sono fondamentalmente un problema con l'app Web, non con l'utente finale.

Nozioni fondamentali di autenticazione

L'autenticazione basata su Cookie è una forma comune di autenticazione. I sistemi di autenticazione basati su token sono in continua crescita, in particolare per le applicazioni a pagina singola.

Quando un utente esegue l'autenticazione usando il nome utente e la password, viene rilasciato un token contenente un ticket di autenticazione che può essere usato per l'autenticazione e l'autorizzazione. Il token viene archiviato come oggetto cookie inviato con ogni richiesta eseguita dal client. La generazione e la convalida di cookie vengono eseguite dal middleware di autenticazione Cookie. Il middleware serializza un "user principal" in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida cookie, ricrea il principale e assegna il principale alla proprietà HttpContext.User.

Autenticazione basata su token

Quando un utente viene autenticato, viene rilasciato un token (non un token antiforgery). Il token contiene informazioni utente sotto forma di attestazioni oppure di un token di riferimento che indirizza l'app allo stato utente mantenuto nell'app. Quando un utente tenta di accedere a una risorsa che richiede l'autenticazione, il token viene inviato all'app con un'intestazione di autorizzazione extra sotto forma di Bearer token. Questo approccio rende l'app senza stato. In ogni richiesta successiva, il token viene passato nella richiesta di convalida lato server. Questo token non è crittografato, ma viene codificato. Nel server il token viene decodificato per accedere alle informazioni. Per inviare il token alle richieste successive, archiviare il token nella risorsa di archiviazione locale del browser. Non preoccuparsi della vulnerabilità CSRF se il token viene archiviato nella risorsa di archiviazione locale del browser. CSRF è un problema quando il token viene archiviato in un cookie oggetto. Per altre informazioni, vedere l'esempio di codice SPA relativo al problema di GitHub che aggiunge due cookie.

Più app ospitate in un dominio

Gli ambienti di hosting condivisi sono vulnerabili all'hijacking della sessione, all'accesso CSRF e ad altri attacchi.

Anche se example1.contoso.net e example2.contoso.net sono host diversi, esiste una relazione di trust implicita tra gli host nel *.contoso.net dominio. Questa relazione di trust implicita consente agli host potenzialmente non attendibili di influire sui cookie degli altri (i criteri di stessa origine che regolano le richieste AJAX non si applicano necessariamente ai cookie HTTP).

Gli attacchi che sfruttano i cookie attendibili tra app ospitate nello stesso dominio possono essere impediti non condividendo domini. Quando ogni app è ospitata nel proprio dominio, non esiste alcuna relazione di trust implicita cookie da sfruttare.

configurazione antiforgery di ASP.NET Core

Avviso

ASP.NET Core implementa l'antiforgeria usando ASP.NET Core Data Protection. Lo stack di protezione dati deve essere configurato per funzionare in una server farm. Per altre informazioni, vedere Configurazione della protezione dei dati.

Il middleware antiforgery viene aggiunto al contenitore di iniezione delle dipendenze quando viene chiamata una delle API seguenti in Startup.ConfigureServices:

In ASP.NET Core 2.0 o versione successiva, FormTagHelper inserisce automaticamente i token antiforgery negli elementi del modulo HTML. Il seguente markup in un file Razor genera automaticamente token anti-falsificazione:

<form method="post">
    ...
</form>

Analogamente, IHtmlHelper.BeginForm genera token antiforgery per impostazione predefinita se il metodo del modulo non è GET.

La generazione automatica di token antiforgery per gli elementi del modulo HTML si verifica quando il <form> tag contiene l'attributo method="post" e uno dei seguenti è true:

  • L'attributo action è vuoto (action="").
  • L'attributo action non viene fornito (<form method="post">).

La generazione automatica di token antiforgery per gli elementi del modulo HTML può essere disabilitata:

  • Disabilitare in modo esplicito i token antiforgery con l'attributo asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • L'elemento del modulo è escluso dai Tag Helper utilizzando il simbolo di opt-out:

    <!form method="post">
        ...
    </!form>
    
  • Rimuovere l'oggetto FormTagHelper dalla visualizzazione. L'oggetto FormTagHelper può essere rimosso da una vista aggiungendo la seguente direttiva alla vista Razor.

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

Nota

Razor Le pagine vengono protette automaticamente da XSRF/CSRF. Per altre informazioni, vedere XSRF/CSRF e Razor Pages.

L'approccio più comune per difendersi dagli attacchi CSRF consiste nell'usare il Modello del Token di Sincronizzazione (STP). STP viene usato quando l'utente richiede una pagina con dati del modulo:

  1. Il server invia un token associato all'identità dell'utente corrente al client.
  2. Il client invia il token al server per la verifica.
  3. Se il server riceve un token che non corrisponde all'identità dell'utente autenticato, la richiesta viene rifiutata.

Il token è univoco e imprevedibile. Il token può essere usato anche per garantire una sequenziazione corretta di una serie di richieste ( ad esempio, verificando la sequenza di richiesta di: pagina 1 > pagina 2 > pagina 3). Tutti i moduli nei modelli ASP.NET Core MVC e Razor Pages generano token antiforgery. La coppia di esempi di visualizzazione seguente genera token antiforgery:

<form asp-controller="Todo" asp-action="Create" method="post">
    ...
</form>

@using (Html.BeginForm("Create", "Todo"))
{
    ...
}

Aggiungere esplicitamente un token antiforgery a un elemento <form> senza usare i Tag Helper con l'helper HTML @Html.AntiForgeryToken:

<form action="/" method="post">
    @Html.AntiForgeryToken()
</form>

In ognuno dei casi precedenti, ASP.NET Core aggiunge un campo modulo nascosto simile all'esempio seguente:

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core include tre filtri per l'uso di token antiforgery:

Opzioni antiforgery

Personalizza AntiforgeryOptions in Startup.ConfigureServices

services.AddAntiforgery(options => 
{
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

Impostare le proprietà antiforgery Cookie usando le proprietà della classe CookieBuilder, come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie antifalsificazione.
FormFieldName Nome del campo nascosto del modulo utilizzato dal sistema antiforgery per visualizzare i token antiforgery nelle visualizzazioni.
HeaderName Nome dell'intestazione utilizzata dal sistema antiforgery. Se null, il sistema considera solo i dati del modulo.
SuppressXFrameOptionsHeader Specifica se eliminare la generazione dell'intestazione X-Frame-Options . Per impostazione predefinita, l'intestazione viene generata con il valore "SAMEORIGIN". Il valore predefinito è false.

Per ulteriori informazioni, vedere CookieAuthenticationOptions.

Configurare le opzioni di protezione contro la contraffazione con IAntiforgery

IAntiforgery fornisce l'API per configurare le funzionalità antifalsificazione. IAntiforgery può essere richiesto nel Configure metodo della Startup classe .

Nell'esempio seguente :

  • Il middleware dalla pagina iniziale dell'app viene usato per generare un token antiforgery e inviarlo nella risposta come cookie.
  • Il token di richiesta viene inviato come JavaScript leggibile con la convenzione di denominazione cookie Angular predefinita descritta nella sezione 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);
    });
}

Richiedi validazione antiforgeria

ValidateAntiForgeryToken è un filtro azione che può essere applicato a una singola azione, a un controller o a livello globale. Le richieste effettuate alle azioni con questo filtro applicato vengono bloccate a meno che la richiesta non includa un token antiforgery valido.

[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'attributo ValidateAntiForgeryToken richiede un token per le richieste ai metodi di azione contrassegnati, incluse le richieste HTTP GET. Se l'attributo ValidateAntiForgeryToken viene applicato attraverso i controller dell'app, può essere sottoposto a override con l'attributo IgnoreAntiforgeryToken.

Nota

ASP.NET Core non supporta l'aggiunta automatica di token antiforgery alle richieste GET.

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Le app ASP.NET Core non generano token antiforgery per i metodi HTTP sicuri (GET, HEAD, OPTIONS e TRACE). Anziché applicare ampiamente l'attributo ValidateAntiForgeryToken e quindi sovrascriverlo con gli attributi IgnoreAntiforgeryToken, è possibile usare l'attributo AutoValidateAntiforgeryToken. Questo attributo funziona in modo identico all'attributo ValidateAntiForgeryToken , ad eccezione del fatto che non richiede token per le richieste effettuate usando i metodi HTTP seguenti:

  • GET
  • HEAD
  • OPZIONI
  • traccia

È consigliabile usare AutoValidateAntiforgeryToken ampiamente per scenari non API. Questo attributo garantisce che le azioni POST siano protette per impostazione predefinita. L'alternativa consiste nell'ignorare i token antiforgery per impostazione predefinita, a meno che ValidateAntiForgeryToken non sia applicato ai singoli metodi di azione. In questo scenario è più probabile che un metodo di azione POST venga lasciato non protetto per errore, lasciando l'app vulnerabile agli attacchi CSRF. Tutti i POST devono inviare il token antiforgery.

Le API non hanno un meccanismo automatico per l'invio dellacookie parte non del token. L'implementazione dipende probabilmente dall'implementazione del codice client. Di seguito sono riportati alcuni esempi:

Esempio a livello di classe:

[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{

Esempio globale:

services.AddControllersWithViews(options =>
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));

Sovrascrivere gli attributi antiforgery globali o del controllore

Il filtro IgnoreAntiforgeryToken viene usato per eliminare la necessità di un token antiforgery per una determinata azione (o controller). Se applicato, questo filtro sostituisce i filtri ValidateAntiForgeryToken e AutoValidateAntiforgeryToken specificati a un livello superiore (a livello globale o in un controller).

[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
    [HttpPost]
    [IgnoreAntiforgeryToken]
    public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
    {
        // no antiforgery token required
    }
}

Aggiornare i token dopo l'autenticazione

I token devono essere aggiornati dopo l'autenticazione dell'utente reindirizzando l'utente a una pagina di visualizzazione o Razor pagine.

JavaScript, AJAX e SPA

Nelle app tradizionali basate su HTML, i token antiforgery vengono passati al server usando campi modulo nascosti. Nelle app moderne basate su JavaScript e nelle applicazioni a pagina singola, molte richieste vengono effettuate a livello di codice. Queste richieste AJAX possono usare altre tecniche (ad esempio intestazioni di richiesta o cookie) per inviare il token.

Se i cookie vengono usati per archiviare i token di autenticazione e per autenticare le richieste API nel server, CSRF è un potenziale problema. Se l'archiviazione locale viene usata per archiviare il token, la vulnerabilità CSRF potrebbe essere mitigata perché i valori dell'archiviazione locale non vengono inviati automaticamente al server con ogni richiesta. L'uso dell'archiviazione locale per archiviare il token antiforgery nel client e inviare il token come intestazione di richiesta è un approccio consigliato.

JavaScript

Usando JavaScript con visualizzazioni, il token può essere creato usando un servizio dall'interno della visualizzazione. Inserire il IAntiforgery servizio nella visualizzazione e chiamare 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>

Questo approccio elimina la necessità di gestire direttamente l'impostazione dei cookie dal server o di leggerli dal client.

L'esempio precedente usa JavaScript per leggere il valore del campo nascosto per l'intestazione POST AJAX.

JavaScript può anche accedere ai token nei cookie e usare il contenuto di cookie per creare un'intestazione con il valore del token.

context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, 
    new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = false });

Supponendo che lo script richieda di inviare il token in un'intestazione denominata X-CSRF-TOKEN, configurare il servizio antiforgery per cercare l'intestazione X-CSRF-TOKEN :

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

L'esempio seguente usa JavaScript per effettuare una richiesta AJAX con l'intestazione appropriata:

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 usa una convenzione per gestire CSRF. Se il server invia un cookie con il nome XSRF-TOKEN, il servizio AngularJS $http aggiunge il valore cookie a un'intestazione quando invia una richiesta al server. Questo processo è automatico. Il client non deve impostare l'intestazione in modo esplicito. Il nome dell'header è X-XSRF-TOKEN. Il server deve rilevare questa intestazione e convalidarne il contenuto.

Affinché ASP.NET'API Core funzioni con questa convenzione nell'avvio dell'applicazione:

  • Configurare l'app per fornire un token in un cookie denominato XSRF-TOKEN.
  • Configurare il servizio antiforgery per cercare un'intestazione denominata X-XSRF-TOKEN, ovvero il nome di intestazione predefinito di Angular per l'invio del token 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");
}

Nota

Quando il token antiforgery viene fornito sia nell'intestazione della richiesta che nel payload del modulo, viene convalidato solo il token nell'intestazione.

autenticazione Windows e cookie anticontraffazione

Quando si usa l'autenticazione di Windows, gli endpoint dell'applicazione devono essere protetti dagli attacchi CSRF nello stesso modo usato per i cookie. Il browser invia in modo implicito il contesto di autenticazione al server e quindi gli endpoint devono essere protetti dagli attacchi CSRF.

Estendere l'antifalsificazione

Il tipo IAntiforgeryAdditionalDataProvider consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF trasferendo dati aggiuntivi in ciascun token. Il GetAdditionalData metodo viene chiamato ogni volta che viene generato un token di campo e il valore restituito viene incorporato all'interno del token generato. Un implementatore può restituire un timestamp, un nonce o qualsiasi altro valore e quindi chiamare ValidateAdditionalData per convalidare questi dati quando il token viene convalidato. Il nome utente del client è già incorporato nei token generati, quindi non è necessario includere queste informazioni. Se un token include dati supplementari ma non IAntiForgeryAdditionalDataProvider è configurato, i dati supplementari non vengono convalidati.

Risorse aggiuntive