Condividi tramite


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

Di Fiyaz Hasan e Rick Anderson

La richiesta intersito falsa è 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 considera attendibile tale 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 richiesta intersito falsa è nota anche come XSRF o CSRF.

Esempio di attacco CSRF:

  1. Un utente accede usando www.good-banking-site.example.com 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://good-banking-site.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 i post del action modulo nel sito vulnerabile, non nel 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 https://www.good-banking-site.com/ richiesta con la stessa facilità che 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 ASP.NET middleware della sessione principale.

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 questa operazione cookie vengono eseguite con il Cookie middleware di autenticazione. Il middleware serializza un'entità utente in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida , cookiericrea l'entità e assegna l'entità alla HttpContext.User proprietà .

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 un token di riferimento che punta l'app allo stato utente gestito 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 aggiuntiva sotto forma di token di connessione. 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 cookieoggetto . 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 contenitore di inserimento delle dipendenze quando viene chiamata una delle API seguenti in Program.cs:

Per altre informazioni, vedere Antiforgery con API minime.

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

<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 è 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'oggetto FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente alla Razor vista:

    @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 alla protezione 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'utente identity 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'utente identityautenticato, 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 <form> elemento senza usare helper tag con l'helper @Html.AntiForgeryTokenHTML :

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

Antiforgeria con AddControllers

La chiamata AddControllers non 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 è garantita che contenga un token antiforgery valido. Le app che vogliono supportare più schede devono testare i browser supportati e registrare i fallimenti. L'uso di più schede può essere problematico. Ad esempio, se un utente apre più schede, le richieste provenienti da schede precedentemente caricate potrebbero fallire con un errore: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Prendere in considerazione modelli di protezione CSRF alternativi se si tratta di un problema.

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 CookieBuilder classe , come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie antiforgery.
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 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à antiforgery. IAntiforgery può essere richiesto in Program.cs tramite WebApplication.Services. L'esempio seguente usa il middleware dalla pagina dell'app home per generare un token antiforgery 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 oggetto 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 predefinita che legge un cookie oggetto denominato XSRF-TOKEN per impostazione predefinita.

Richiedi convalida antiforgeria

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 tra i controller dell'app, può essere sottoposto a override con l'attributo IgnoreAntiforgeryToken .

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Anziché applicare l'attributo a livello generale e quindi eseguirne l'override ValidateAntiForgeryToken con IgnoreAntiforgeryToken gli attributi, è 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
  • OPTIONS
  • TRACE

È consigliabile usare su AutoValidateAntiforgeryToken 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, a meno che non 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());
});

Eseguire l'override degli attributi antiforgery globali o 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 e ValidateAntiForgeryToken i AutoValidateAntiforgeryToken filtri 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 usando campi modulo nascosti. Nelle app moderne basate su JavaScript e nei contratti 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.

Blazor

Per altre informazioni, vedere Blazor di base ASP.NET.

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 si inserisce il IAntiforgery servizio non è possibile, usare JavaScript per accedere ai token nei cookie:

  • I token di accesso in una richiesta aggiuntiva al server, in same-origingenere .
  • Usare il cookiecontenuto di 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 antiforgery viene fornito sia nell'intestazione della richiesta che nel payload del modulo, viene convalidato solo il token nell'intestazione.

Antiforgery con API minime

Chiama AddAntiforgery e UseAntiforgery(IApplicationBuilder) per registrare i servizi antiforgery in DI. I token antiforgery vengono usati per attenuare gli attacchi falsi di richiesta intersito.

var builder = WebApplication.CreateBuilder();

builder.Services.AddAntiforgery();

var app = builder.Build();

app.UseAntiforgery();

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

app.Run();

Middleware antiforgery:

  • Non esegue il corto circuito dell'esecuzione rest della pipeline di richiesta.
  • Imposta IAntiforgeryValidationFeature in HttpContext.Features della richiesta corrente.

Il token antiforgery viene convalidato solo se:

  • L'endpoint contiene metadati che implementano IAntiforgeryMetadata dove 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 perché DisableAntiforgery viene chiamato .
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

Post per:

  • /todo dal modulo generato dall'endpoint / ha esito positivo perché il token antiforgery è valido.
  • /todo dal modulo generato da ha /SkipToken esito negativo perché l'antiforgeria 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 antiforgery

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 agli endpoint deve essere protetto dagli attacchi CSRF.

Estendere l'antiforgeria

Il IAntiforgeryAdditionalDataProvider tipo consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF eseguendo il round trip dei 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 richiesta intersito falsa (nota anche come XSRF o CSRF) è 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 considera attendibile tale 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 usando www.good-banking-site.example.com 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://good-banking-site.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 i post del action modulo nel sito vulnerabile, non nel 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 https://www.good-banking-site.com/ richiesta altrettanto facilmente come 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 ASP.NET middleware della sessione principale.

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 questa operazione cookie viene eseguita dal Cookie middleware di autenticazione. Il middleware serializza un'entità utente in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida , cookiericrea l'entità e assegna l'entità alla HttpContext.User proprietà .

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 un token di riferimento che punta l'app allo stato utente gestito 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 aggiuntiva sotto forma di token di connessione. 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 cookieoggetto . 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 contenitore di inserimento 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 Razor file genera automaticamente token antiforgery:

<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 è 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'oggetto FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente alla Razor vista:

    @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'utente identity 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'utente identityautenticato, 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 <form> elemento senza usare helper tag con l'helper @Html.AntiForgeryTokenHTML :

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

Antiforgeria con AddControllers

La chiamata AddControllers non 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

Prendere in considerazione modelli di protezione CSRF alternativi se si tratta di un problema.

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 CookieBuilder classe , come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie antiforgery.
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 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à antiforgery. IAntiforgery può essere richiesto in Program.cs tramite WebApplication.Services. L'esempio seguente usa il middleware dalla pagina dell'app home per generare un token antiforgery 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 oggetto 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 predefinita che legge un cookie oggetto denominato XSRF-TOKEN per impostazione predefinita.

Richiedi convalida antiforgeria

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 tra i controller dell'app, può essere sottoposto a override con l'attributo IgnoreAntiforgeryToken .

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Anziché applicare l'attributo a livello generale e quindi eseguirne l'override ValidateAntiForgeryToken con IgnoreAntiforgeryToken gli attributi, è 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
  • OPTIONS
  • TRACE

È consigliabile usare su AutoValidateAntiforgeryToken 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, a meno che non 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());
});

Eseguire l'override degli attributi antiforgery globali o 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 e ValidateAntiForgeryToken i AutoValidateAntiforgeryToken filtri 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 usando campi modulo nascosti. Nelle app moderne basate su JavaScript e nei contratti 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 si inserisce il IAntiforgery servizio non è possibile, usare JavaScript per accedere ai token nei cookie:

  • I token di accesso in una richiesta aggiuntiva al server, in same-origingenere .
  • Usare il cookiecontenuto di 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 antiforgery viene fornito sia nell'intestazione della richiesta che nel payload del modulo, viene convalidato solo il token nell'intestazione.

Antiforgery con API minime

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 di Windows e cookie antiforgery

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 agli endpoint deve essere protetto dagli attacchi CSRF.

Estendere l'antiforgeria

Il IAntiforgeryAdditionalDataProvider tipo consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF eseguendo il round trip dei 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 richiesta intersito falsa (nota anche come XSRF o CSRF) è 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 considera attendibile tale 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 usando www.good-banking-site.example.com 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://good-banking-site.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 i post del action modulo nel sito vulnerabile, non nel 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 https://www.good-banking-site.com/ richiesta altrettanto facilmente come 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 ASP.NET middleware della sessione principale.

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 questa operazione cookie viene eseguita dal Cookie middleware di autenticazione. Il middleware serializza un'entità utente in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida , cookiericrea l'entità e assegna l'entità alla HttpContext.User proprietà .

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 un token di riferimento che punta l'app allo stato utente gestito 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 aggiuntiva sotto forma di token di connessione. 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 cookieoggetto . 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 contenitore di inserimento 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 Razor file genera automaticamente token antiforgery:

<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 è 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'oggetto FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente alla Razor vista:

    @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'utente identity 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'utente identityautenticato, 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 <form> elemento senza usare helper tag con l'helper @Html.AntiForgeryTokenHTML :

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

Prendere in considerazione modelli di protezione CSRF alternativi se si tratta di un problema.

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 CookieBuilder classe , come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie antiforgery.
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 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à antiforgery. IAntiforgery può essere richiesto in Program.cs tramite WebApplication.Services. L'esempio seguente usa il middleware dalla pagina dell'app home per generare un token antiforgery 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 oggetto 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 predefinita che legge un cookie oggetto denominato XSRF-TOKEN per impostazione predefinita.

Richiedi convalida antiforgeria

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 tra i controller dell'app, può essere sottoposto a override con l'attributo IgnoreAntiforgeryToken .

Convalidare automaticamente i token antiforgery solo per metodi HTTP non sicuri

Anziché applicare l'attributo a livello generale e quindi eseguirne l'override ValidateAntiForgeryToken con IgnoreAntiforgeryToken gli attributi, è 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
  • OPTIONS
  • TRACE

È consigliabile usare su AutoValidateAntiforgeryToken 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, a meno che non 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());
});

Eseguire l'override degli attributi antiforgery globali o 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 e ValidateAntiForgeryToken i AutoValidateAntiforgeryToken filtri 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 usando campi modulo nascosti. Nelle app moderne basate su JavaScript e nei contratti 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 si inserisce il IAntiforgery servizio non è possibile, JavaScript può anche accedere al token nei cookie, ottenuto da una richiesta aggiuntiva al server (in genere same-origin) e usare il cookiecontenuto di 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 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}`
}

autenticazione di Windows e cookie antiforgery

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

Il IAntiforgeryAdditionalDataProvider tipo consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF eseguendo il round trip dei 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 richiesta intersito falsa (nota anche come XSRF o CSRF) è 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 considera attendibile tale 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 usando www.good-banking-site.example.com 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://good-banking-site.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 i post del action modulo nel sito vulnerabile, non nel 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 https://www.good-banking-site.com/ richiesta altrettanto facilmente come 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 ASP.NET middleware della sessione principale.

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 questa operazione cookie viene eseguita dal Cookie middleware di autenticazione. Il middleware serializza un'entità utente in un oggetto crittografato cookie. Nelle richieste successive, il middleware convalida , cookiericrea l'entità e assegna l'entità alla HttpContext.User proprietà .

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 un token di riferimento che punta l'app allo stato utente gestito 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 aggiuntiva sotto forma di token di connessione. 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 cookieoggetto . 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 inserimento delle dipendenze quando viene chiamata una delle API seguenti in Startup.ConfigureServices:

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

<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 è 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'oggetto FormTagHelper può essere rimosso da una visualizzazione aggiungendo la direttiva seguente alla Razor vista:

    @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'utente identity 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'utente identityautenticato, 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 in modo esplicito un token antiforgery a un <form> elemento senza usare helper tag con l'helper @Html.AntiForgeryTokenHTML :

<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

Personalizzare 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 CookieBuilder classe , come illustrato nella tabella seguente.

Opzione Descrizione
Cookie Determina le impostazioni utilizzate per creare i cookie antiforgery.
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 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 funzionalità antiforgery con IAntiforgery

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

Nell'esempio seguente :

  • Il middleware dalla pagina dell'app home 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 convalida 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 tra 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

ASP.NET app Core non generano token antiforgery per metodi HTTP sicuri (GET, HEAD, OPTIONS e TRACE). Anziché applicare l'attributo a livello generale e quindi eseguirne l'override ValidateAntiForgeryToken con IgnoreAntiforgeryToken gli attributi, è 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
  • OPTIONS
  • TRACE

È consigliabile usare su AutoValidateAntiforgeryToken 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, a meno che non 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:

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

Esempio globale:

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

Eseguire l'override degli attributi antiforgery globali o 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 e ValidateAntiForgeryToken i AutoValidateAntiforgeryToken filtri 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 nei contratti 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 cookiecontenuto di 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 oggetto con il nome XSRF-TOKEN, il servizio AngularJS $http aggiunge il cookie valore 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'intestazione è 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 di Windows e cookie antiforgery

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

Il IAntiforgeryAdditionalDataProvider tipo consente agli sviluppatori di estendere il comportamento del sistema anti-CSRF eseguendo il round trip dei 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