Evitar ataques XSRF/CSRF (solicitações intersite forjadas) no ASP.NET Core

Por Fiyaz Hasan e Rick Anderson

A falsificação de solicitação entre sites é um ataque contra aplicativos hospedados na Web em que um aplicativo Web mal-intencionado pode influenciar a interação entre um navegador cliente e um aplicativo Web que confia nesse navegador. Os ataques de solicitação entre sites forjada são possíveis porque os navegadores da Web enviam alguns tipos de tokens de autenticação automaticamente com cada solicitação a um site. Essa forma de exploração também é conhecida como ataque de um clique ou montagem de sessão, pois o ataque aproveita a sessão autenticada anteriormente pelo usuário. A falsificação de solicitação entre sites também é conhecida como XSRF ou CSRF.

Um exemplo de ataque CSRF:

  1. Um usuário entra no www.good-banking-site.example.com usando a autenticação de formulários. O servidor autentica o usuário e emite uma resposta que inclui uma autenticação cookie. O site é vulnerável a ataques porque confia em qualquer solicitação recebida com uma autenticação cookieválida.

  2. O usuário visita um site mal-intencionado, www.bad-crook-site.example.com.

    O site mal-intencionado, www.bad-crook-site.example.com, contém um formulário HTML semelhante ao seguinte exemplo:

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

    Observe que o formulário publica action no site vulnerável, não no site mal-intencionado. Essa é a parte "entre sites" do CSRF.

  3. O usuário seleciona o botão Enviar. O navegador faz a solicitação e inclui automaticamente a autenticação cookie para o domínio solicitado, www.good-banking-site.example.com.

  4. A solicitação é executada no servidor www.good-banking-site.example.com com o contexto de autenticação do usuário e pode executar qualquer ação que um usuário autenticado tenha permissão para executar.

Além do cenário em que o usuário seleciona o botão para enviar o formulário, o site mal-intencionado pode:

  • Executar um script que envia automaticamente o formulário.
  • Enviar o envio do formulário como uma solicitação AJAX.
  • Ocultar o formulário usando CSS.

Esses cenários alternativos não exigem nenhuma ação ou entrada do usuário além de visitar inicialmente o site mal-intencionado.

O uso de HTTPS não impede um ataque CSRF. O site mal-intencionado pode enviar uma solicitação https://www.good-banking-site.com/ tão facilmente quanto pode enviar uma solicitação insegura.

Alguns ataques visam pontos de extremidade que respondem a solicitações GET, nesse caso, uma marca de imagem pode ser usada para executar a ação. Essa forma de ataque é comum em sites de fórum que permitem imagens, mas bloqueiam JavaScript. Aplicativos que alteram o estado em solicitações GET, em que variáveis ou recursos são alterados, são vulneráveis a ataques mal-intencionados. Solicitações GET que alteram o estado são inseguras. Uma prática recomendada é nunca alterar o estado em uma solicitação GET.

Ataques CSRF são possíveis contra aplicativos Web que usam cookies para autenticação porque:

  • Os navegadores armazenam cookies emitidos por um aplicativo Web.
  • Os cookies armazenados incluem cookies de sessões para usuários autenticados.
  • Os navegadores enviam todos os cookies associados a um domínio para o aplicativo Web a cada solicitação, independentemente de como a solicitação para o aplicativo foi gerada no navegador.

No entanto, os ataques CSRF não se limitam à exploração de cookies. Por exemplo, a autenticação Básica e Digest também são vulneráveis. Depois que um usuário entra com a autenticação Básica ou Digest, o navegador envia automaticamente as credenciais até que a sessão termine.

Nesse contexto, a sessão refere-se à sessão do lado do cliente durante a qual o usuário é autenticado. Ela não está relacionado a sessões do lado do servidor ou ao Middleware de Sessão do ASP.NET Core.

Os usuários podem se proteger contra vulnerabilidades CSRF tomando precauções:

  • Saia dos aplicativos Web quando terminar de usá-los.
  • Limpe os cookies do navegador periodicamente.

No entanto, as vulnerabilidades CSRF são fundamentalmente um problema com o aplicativo Web, não com o usuário final.

Conceitos básicos sobre autenticação

A autenticação baseada em Cookie é uma forma popular de autenticação. Os sistemas de autenticação baseados em token estão crescendo em popularidade, especialmente para Aplicativos de Página Única (SPAs).

Quando um usuário se autentica usando seu nome de usuário e senha, é emitido um token contendo um tíquete de autenticação. O token pode ser usado para autenticação e autorização. O token é armazenado como um cookie que é enviado com cada solicitação que o cliente faz. Gerar e validar isso cookie é executado com o Cookie Middleware de Autenticação. O middleware serializa uma entidade de segurança de usuário em um criptografado cookie. Em solicitações subsequentes, o middleware valida o cookie, recria a entidade de segurança e atribui a entidade de segurança à propriedade HttpContext.User.

Autenticação baseada em token

Quando um usuário é autenticado, um token é emitido para ele (não um token antifalsificação). O token contém informações do usuário na forma de declarações ou um token de referência que aponta o aplicativo para o estado do usuário mantido no aplicativo. Quando um usuário tenta acessar um recurso que requer autenticação, o token é enviado para o aplicativo com um cabeçalho de autorização extra na forma de um token de portador. Essa abordagem torna o aplicativo sem estado. Em cada solicitação subsequente, o token é passado na solicitação de validação do lado do servidor. Esse token não é criptografado, ele é codificado. No servidor, o token é decodificado para acessar suas informações. Para enviar o token em solicitações subsequentes, armazene o token no armazenamento local do navegador. Colocar um token no armazenamento local do navegador e recuperá-lo e usá-lo como um token de portador fornece proteção contra ataques CSRF. No entanto, se o aplicativo estiver vulnerável à injeção de script por meio do XSS ou de um arquivo JavaScript externo comprometido, um invasor poderá recuperar qualquer valor do armazenamento local e enviá-lo para si mesmo. O ASP.NET Core codifica todas as saídas do lado do servidor de variáveis por padrão, reduzindo o risco de XSS. Se você substituir esse comportamento usando Html.Raw ou código personalizado com entrada não confiável, poderá aumentar o risco de XSS.

Não se preocupe com a vulnerabilidade CSRF se o token estiver armazenado no armazenamento local do navegador. O CSRF é uma preocupação quando o token é armazenado em um cookie. Para obter mais informações, consulte o exemplo de código SPA do problema do GitHub adiciona dois cookies.

Vários aplicativos hospedados em um domínio

Ambientes de hospedagem compartilhados são vulneráveis ao sequestro de sessão, CSRF de entrada e outros ataques.

Embora example1.contoso.net e example2.contoso.net sejam hosts diferentes, há uma relação de confiança implícita entre hosts no domínio *.contoso.net. Essa relação de confiança implícita permite que hosts potencialmente não confiáveis afetem os cookies uns dos outros (as políticas de mesma origem que regem as solicitações AJAX não se aplicam necessariamente a cookies de HTTP).

Ataques que exploram as cookies confiáveis entre aplicativos hospedados no mesmo domínio podem ser evitados por não compartilhar domínios. Quando cada aplicativo é hospedado em seu próprio domínio, não há nenhuma relação de confiança implícita cookie a ser explorada.

Antifalsificação no ASP.NET Core

Aviso

ASP.NET Core implementa antifalsificação por meio da Proteção de Dados do ASP.NET Core. A pilha de proteção de dados deve ser configurada para funcionar em um farm de servidores. Para obter mais informações, consulte Como configurar a proteção de dados.

O middleware antifalsificação é adicionado ao contêiner de Injeção de dependência quando uma das seguintes APIs é chamada em Program.cs:

Para obter mais informações, consulte Antifalsificação com APIs mínimas.

O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML. A marcação a seguir em um arquivo Razor gera tokens antifalsificação automaticamente:

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

Da mesma forma, IHtmlHelper.BeginForm gera tokens antifalsificação por padrão se o método do formulário não for GET.

A geração automática de tokens antifalsificação para elementos de formulário HTML ocorre quando a marca <form> contém o atributo method="post" e qualquer um das seguintes alternativas são verdadeiras:

  • O atributo de ação está vazio (action="").
  • O atributo de ação não foi fornecido (<form method="post">).

A geração automática de tokens antifalsificação para elementos de formulário HTML pode ser desabilitada:

  • Desabilite explicitamente tokens antifalsificação por meio do atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • O elemento formulário é recusado nos Auxiliares de Marca usando o símbolo ! recusar do Auxiliar de Marca:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Remova o FormTagHelper do modo de exibição. O FormTagHelper pode ser removido de um modo de exibição adicionando a seguinte diretiva à exibição Razor :

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

Observação

As Razor Páginas são protegidas automaticamente contra XSRF/CSRF. Para obter mais informações, consulte XSRF/CSRF e Razor Páginas.

A abordagem mais comum para se defender contra ataques CSRF é usar o Padrão de Token do Sincronizador (STP). O STP é usado quando o usuário solicita uma página com dados de formulário:

  1. O servidor envia um token associado à identidade do usuário atual para o cliente.
  2. O cliente envia de volta o token para o servidor para verificação.
  3. Se o servidor receber um token que não corresponda à identidade do usuário autenticado, a solicitação será rejeitada.

O token é exclusivo e imprevisível. O token também pode ser usado para garantir o sequenciamento adequado de uma série de solicitações (por exemplo, garantindo a sequência de solicitações de: página 1 > página 2 > página 3). Todos os formulários em ASP.NET Core modelos MVC e Páginas Razor geram tokens antifalsificação. O seguinte par de exemplos de modo de exibição gera tokens antifalsificação:

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

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

Adicione explicitamente um token antifalsificação a um <form> elemento sem usar Auxiliares de Marca com o auxiliar HTML @Html.AntiForgeryToken :

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

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

Em cada um dos casos anteriores, o ASP.NET Core adiciona um campo de formulário oculto semelhante ao seguinte exemplo:

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

O ASP.NET Core inclui três filtros para trabalhar com tokens antifalsificação:

Antifalsificação com AddControllers

Chamar AddControllersnão habilita tokens antifalsificação. AddControllersWithViews deve ser chamado para ter suporte interno a tokens antifalsificação.

Várias guias do navegador e o padrão de token do sincronizador

Com o Padrão de Token do Sincronizador, apenas a página carregada mais recentemente contém um token antifalsificação válido. O uso de várias guias pode ser problemático. Por exemplo, se um usuário abrir várias guias:

  • Somente a guia carregada mais recentemente contém um token antifalsificação válido.
  • As solicitações feitas de guias carregadas anteriormente falham com um erro: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Considere padrões de proteção CSRF alternativos se isso representar um problema.

Configurar antifalsificação com AntiforgeryOptions

Personalize AntiforgeryOptions no Program.cs:

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

Defina as propriedades Cookie antifalsificação usando as propriedades da classe CookieBuilder, conforme mostrado na tabela a seguir.

Opção Descrição
Cookie Determina as configurações usadas para criar o cookies antifalsificação.
FormFieldName O nome do campo de formulário oculto usado pelo sistema antifalsificação para renderizar tokens antifalsificação em modos de exibição.
HeaderName O nome do cabeçalho usado pelo sistema antifalsificação. Se null, o sistema considerará apenas os dados do formulário.
SuppressXFrameOptionsHeader Especifica se a geração do X-Frame-Options cabeçalho deve ser suprimida. Por padrão, o cabeçalho é gerado com um valor de "SAMEORIGIN". Assume o padrão de false.

Para obter mais informações, consulte CookieAuthenticationOptions.

Gerar tokens antifalsificação com IAntiforgery

IAntiforgery fornece a API para configurar recursos antifalsificação. IAntiforgery pode ser solicitado no Program.cs usando WebApplication.Services. O exemplo a seguir usa middleware da home page do aplicativo para gerar um token antifalsificação e enviá-lo na resposta como um 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);
});

O exemplo anterior define um cookie nomeado XSRF-TOKEN. O cliente pode ler esse cookie e fornecer seu valor como um cabeçalho anexado a solicitações AJAX. Por exemplo, o Angular inclui proteção XSRF interna que lê um cookie nomeado XSRF-TOKEN por padrão.

Exigir validação antifalsificação

O filtro de ação ValidateAntiForgeryToken pode ser aplicado a uma ação individual, a um controlador ou globalmente. As solicitações feitas às ações que têm esse filtro aplicado são bloqueadas, a menos que a solicitação inclua um token antifalsificação válido:

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

    return RedirectToAction();
}

O atributo ValidateAntiForgeryToken exige um token para solicitações para os métodos de ação que marca, incluindo solicitações HTTP GET. Se o atributo ValidateAntiForgeryToken for aplicado entre os controladores do aplicativo, ele poderá ser substituído pelo atributo IgnoreAntiforgeryToken.

Validar automaticamente tokens antifalsificação somente para métodos HTTP não seguros

Em vez de aplicar amplamente o atributo ValidateAntiForgeryToken e substituí-lo por atributos IgnoreAntiforgeryToken, o atributo AutoValidateAntiforgeryToken pode ser usado. Esse atributo funciona de forma idêntica ao atributo ValidateAntiForgeryToken, mas ele não requer tokens para solicitações feitas usando os seguintes métodos HTTP:

  • GET
  • HEAD
  • OPÇÕES
  • RASTREAMENTO

É recomendável usar amplamente AutoValidateAntiforgeryToken para cenários que não são de API. Esse atributo garante que as ações POST sejam protegidas por padrão. A alternativa é ignorar tokens antifalsificação por padrão, a menos que ValidateAntiForgeryToken seja aplicado a métodos de ação individuais. Nesse cenário, é mais provável que um método de ação POST seja deixado desprotegido por engano, deixando o aplicativo vulnerável a ataques CSRF. Todos os POSTs devem enviar o token antifalsificação.

As APIs não têm um mecanismo automático para enviar a partecookie que não é do token. A implementação provavelmente depende da implementação do código do cliente. Mostramos alguns exemplos abaixo:

Exemplo de nível de classe:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Exemplo global:

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

Substituir atributos antifalsificação globais ou de controlador

O filtro IgnoreAntiforgeryToken é usado para eliminar a necessidade de um token antifalsificação para uma determinada ação (ou controlador). Quando aplicado, esse filtro substitui ValidateAntiForgeryToken e AutoValidateAntiforgeryToken filtra especificados em um nível mais alto (globalmente ou em um controlador).

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

    return RedirectToAction();
}

Atualizar tokens após a autenticação

Os tokens devem ser atualizados depois que o usuário é autenticado redirecionando o usuário para um modo de exibição ou para a página de Páginas Razor.

JavaScript, AJAX e SPAs

Em aplicativos tradicionais baseados em HTML, tokens antifalsificação são passados para o servidor usando campos de formulário ocultos. Em SPAs e aplicativos modernos baseados em JavaScript, muitas solicitações são feitas programaticamente. Essas solicitações AJAX podem usar outras técnicas, como cabeçalhos de solicitação ou cookies, para enviar o token.

Se cookies forem usados para armazenar tokens de autenticação e autenticar solicitações de API no servidor, o CSRF é um problema em potencial. Se o armazenamento local for usado para armazenar o token, a vulnerabilidade CSRF poderá ser atenuada porque os valores do armazenamento local não são enviados automaticamente para o servidor a cada solicitação. Usar o armazenamento local para armazenar o token antifalsificação no cliente e enviar o token como um cabeçalho de solicitação é uma abordagem recomendada.

Blazor

Para obter mais informações, confira autenticação e autorização do AsP.NET Core Blazor.

JavaScript

Usando JavaScript com modos de exibição, o token pode ser criado usando um serviço de dentro da exibição. Injete o serviço IAntiforgery na exibição e chame 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>
}

O exemplo anterior usa JavaScript para ler o valor do campo oculto para o cabeçalho POST do AJAX.

Essa abordagem elimina a necessidade de lidar diretamente com o cookies das configurações do servidor ou de lê-las do cliente. No entanto, quando não for possível injetar o serviço IAntiforgery, use JavaScript para acessar tokens em cookies:

  • Tokens de acesso em uma solicitação adicional para o servidor, normalmente same-origin.
  • Use o conteúdo cookie do para criar um cabeçalho com o valor do token.

Supondo que o script envie o token em um cabeçalho de solicitação chamado X-XSRF-TOKEN, configure o serviço antifalsificação para procurar o cabeçalho X-XSRF-TOKEN:

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

O exemplo a seguir adiciona um ponto de extremidade protegido que grava o token de solicitação em um JavaScript legível 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();

O exemplo a seguir usa JavaScript para fazer uma solicitação AJAX para obter o token e fazer outra solicitação com o cabeçalho apropriado:

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

Observação

Quando o token antifalsificação é fornecido no cabeçalho da solicitação e no conteúdo do formulário, somente o token no cabeçalho é validado.

antifalsificação com APIs mínimas

Chame AddAntiforgery e UseAntiforgery(IApplicationBuilder) registre serviços antiforgery no DI. Tokens antifalsificação são usados para atenuar ataques de falsificação de solicitação entre sites.

var builder = WebApplication.CreateBuilder();

builder.Services.AddAntiforgery();

var app = builder.Build();

app.UseAntiforgery();

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

app.Run();

O middleware antifalsificação:

O token antifalsificação só será validado se:

  • O ponto de extremidade contém metadados que implementam IAntiforgeryMetadata em que RequiresValidation=true.
  • O método HTTP associado ao ponto de extremidade é um método HTTP relevante. Os métodos relevantes são todos métodos HTTP exceto TRACE, OPTIONS, HEAD e GET.
  • A solicitação está associada a um ponto de extremidade válido.

Observação: Quando habilitado manualmente, o middleware antifalsificação deve ser executado após o middleware de autenticação e autorização para impedir a leitura de dados do formulário quando o usuário não for autenticado.

Por padrão, APIs mínimas que aceitam dados de formulário exigem validação de token antifalsificação.

Considere o seguinte método 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>
";
}

O código anterior tem três argumentos, a ação, o token anti-falsificação e um bool indicando se o token deve ser usado.

Considere o seguinte exemplo:

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

No código anterior, posta para:

  • /todo exige um token antifalsificação válido.
  • /todo2 não requer um token antifalsificação válido porque DisableAntiforgery é chamado.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

Um POST para:

  • /todo do formulário gerado pelo ponto de extremidade / é bem-sucedido porque o token antifalsificação é válido.
  • /todo do formulário gerado pelo /SkipToken falha porque a antifalsificação não está incluída.
  • /todo2 do formulário gerado pelo ponto de extremidade /DisableAntiforgery é bem-sucedido porque o token antifalsificação é não é necessário.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

Quando um formulário é enviado sem um token antifalsificação válido:

  • No ambiente de desenvolvimento, uma exceção é gerada.
  • No ambiente de produção, uma mensagem é registrada em log.

cookies de autenticação e antifalsificação do Windows

Ao usar a Autenticação do Windows, os pontos de extremidade do aplicativo devem ser protegidos contra ataques CSRF da mesma maneira que o feito para cookies. O navegador envia implicitamente o contexto de autenticação para o servidor e os pontos de extremidade precisam ser protegidos contra ataques CSRF.

Estender a antifalsificação

O tipo IAntiforgeryAdditionalDataProvider permite que os desenvolvedores estendam o comportamento do sistema anti-CSRF ao arredondar dados adicionais em cada token. O método GetAdditionalData é chamado sempre que um token de campo é gerado e o valor retornado é inserido no token gerado. Um implementador pode retornar um carimbo de data/hora, um nonce ou qualquer outro valor e, em seguida, chamar ValidateAdditionalData para validar esses dados quando o token for validado. O nome de usuário do cliente já está inserido nos tokens gerados, portanto, não é necessário incluir essas informações. Se um token incluir dados complementares, mas nenhum IAntiForgeryAdditionalDataProvider estiver configurado, os dados complementares não serão validados.

Recursos adicionais

A solicitação entre sites forjada (também conhecida como XSRF ou CSRF) é um ataque contra aplicativos Web hospedados, no qual um site mal-intencionado pode influenciar a interação entre um navegador cliente e um aplicativo Web que confia nesse navegador. Os ataques de solicitação entre sites forjada são possíveis porque os navegadores da Web enviam alguns tipos de tokens de autenticação automaticamente com cada solicitação a um site. Essa forma de exploração também é conhecida como ataque de um clique ou montagem de sessão, pois o ataque aproveita a sessão autenticada anteriormente pelo usuário.

Um exemplo de ataque CSRF:

  1. Um usuário entra no www.good-banking-site.example.com usando a autenticação de formulários. O servidor autentica o usuário e emite uma resposta que inclui uma autenticação cookie. O site é vulnerável a ataques porque confia em qualquer solicitação recebida com uma autenticação cookieválida.

  2. O usuário visita um site mal-intencionado, www.bad-crook-site.example.com.

    O site mal-intencionado, www.bad-crook-site.example.com, contém um formulário HTML semelhante ao seguinte exemplo:

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

    Observe que o formulário publica action no site vulnerável, não no site mal-intencionado. Essa é a parte "entre sites" do CSRF.

  3. O usuário seleciona o botão Enviar. O navegador faz a solicitação e inclui automaticamente a autenticação cookie para o domínio solicitado, www.good-banking-site.example.com.

  4. A solicitação é executada no servidor www.good-banking-site.example.com com o contexto de autenticação do usuário e pode executar qualquer ação que um usuário autenticado tenha permissão para executar.

Além do cenário em que o usuário seleciona o botão para enviar o formulário, o site mal-intencionado pode:

  • Executar um script que envia automaticamente o formulário.
  • Enviar o envio do formulário como uma solicitação AJAX.
  • Ocultar o formulário usando CSS.

Esses cenários alternativos não exigem nenhuma ação ou entrada do usuário além de visitar inicialmente o site mal-intencionado.

O uso de HTTPS não impede um ataque CSRF. O site mal-intencionado pode enviar uma solicitação https://www.good-banking-site.com/ tão facilmente quanto pode enviar uma solicitação insegura.

Alguns ataques visam pontos de extremidade que respondem a solicitações GET, nesse caso, uma marca de imagem pode ser usada para executar a ação. Essa forma de ataque é comum em sites de fórum que permitem imagens, mas bloqueiam JavaScript. Aplicativos que alteram o estado em solicitações GET, em que variáveis ou recursos são alterados, são vulneráveis a ataques mal-intencionados. Solicitações GET que alteram o estado são inseguras. Uma prática recomendada é nunca alterar o estado em uma solicitação GET.

Ataques CSRF são possíveis contra aplicativos Web que usam cookies para autenticação porque:

  • Os navegadores armazenam cookies emitidos por um aplicativo Web.
  • Os cookies armazenados incluem cookies de sessões para usuários autenticados.
  • Os navegadores enviam todos os cookies associados a um domínio para o aplicativo Web a cada solicitação, independentemente de como a solicitação para o aplicativo foi gerada no navegador.

No entanto, os ataques CSRF não se limitam à exploração de cookies. Por exemplo, a autenticação Básica e Digest também são vulneráveis. Depois que um usuário entra com a autenticação Básica ou Digest, o navegador envia automaticamente as credenciais até que a sessão termine.

Nesse contexto, a sessão refere-se à sessão do lado do cliente durante a qual o usuário é autenticado. Ela não está relacionado a sessões do lado do servidor ou ao Middleware de Sessão do ASP.NET Core.

Os usuários podem se proteger contra vulnerabilidades CSRF tomando precauções:

  • Saia dos aplicativos Web quando terminar de usá-los.
  • Limpe os cookies do navegador periodicamente.

No entanto, as vulnerabilidades CSRF são fundamentalmente um problema com o aplicativo Web, não com o usuário final.

Conceitos básicos sobre autenticação

A autenticação baseada em Cookie é uma forma popular de autenticação. Os sistemas de autenticação baseados em token estão crescendo em popularidade, especialmente para Aplicativos de Página Única (SPAs).

Quando um usuário se autentica usando seu nome de usuário e senha, ele recebe um token, contendo um tíquete de autenticação que pode ser usado para autenticação e autorização. O token é armazenado como um cookie que é enviado com cada solicitação que o cliente faz. A geração e a validação desse cookie é feira pelo Middleware de Autenticação do Cookie. O middleware serializa uma entidade de segurança de usuário em um criptografado cookie. Em solicitações subsequentes, o middleware valida o cookie, recria a entidade de segurança e atribui a entidade de segurança à propriedade HttpContext.User.

Autenticação baseada em token

Quando um usuário é autenticado, um token é emitido para ele (não um token antifalsificação). O token contém informações do usuário na forma de declarações ou um token de referência que aponta o aplicativo para o estado do usuário mantido no aplicativo. Quando um usuário tenta acessar um recurso que requer autenticação, o token é enviado para o aplicativo com um cabeçalho de autorização extra na forma de um token de portador. Essa abordagem torna o aplicativo sem estado. Em cada solicitação subsequente, o token é passado na solicitação de validação do lado do servidor. Esse token não é criptografado, ele é codificado. No servidor, o token é decodificado para acessar suas informações. Para enviar o token em solicitações subsequentes, armazene o token no armazenamento local do navegador. Colocar um token no armazenamento local do navegador e recuperá-lo e usá-lo como um token de portador fornece proteção contra ataques CSRF. No entanto, se o aplicativo estiver vulnerável à injeção de script por meio do XSS ou de um arquivo javascript externo comprometido, um invasor poderá recuperar qualquer valor do armazenamento local e enviá-lo para si mesmo. O ASP.NET Core codifica todas as saídas do lado do servidor de variáveis por padrão, reduzindo o risco de XSS. Se você substituir esse comportamento usando Html.Raw ou código personalizado com entrada não confiável, poderá aumentar o risco de XSS.

Não se preocupe com a vulnerabilidade CSRF se o token estiver armazenado no armazenamento local do navegador. O CSRF é uma preocupação quando o token é armazenado em um cookie. Para obter mais informações, consulte o exemplo de código SPA do problema do GitHub adiciona dois cookies.

Vários aplicativos hospedados em um domínio

Os ambientes de hospedagem compartilhada são vulneráveis ao sequestro de sessão, ao CSRF de logon e a outros ataques.

Embora example1.contoso.net e example2.contoso.net sejam hosts diferentes, há uma relação de confiança implícita entre hosts no domínio *.contoso.net. Essa relação de confiança implícita permite que hosts potencialmente não confiáveis afetem os cookies uns dos outros (as políticas de mesma origem que regem as solicitações AJAX não se aplicam necessariamente a cookies de HTTP).

Ataques que exploram as cookies confiáveis entre aplicativos hospedados no mesmo domínio podem ser evitados por não compartilhar domínios. Quando cada aplicativo é hospedado em seu próprio domínio, não há nenhuma relação de confiança implícita cookie a ser explorada.

Antifalsificação no ASP.NET Core

Aviso

ASP.NET Core implementa antifalsificação por meio da Proteção de Dados do ASP.NET Core. A pilha de proteção de dados deve ser configurada para funcionar em um farm de servidores. Para obter mais informações, consulte Como configurar a proteção de dados.

O middleware antifalsificação é adicionado ao contêiner de Injeção de dependência quando uma das seguintes APIs é chamada em Program.cs:

O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML. A marcação a seguir em um arquivo Razor gera tokens antifalsificação automaticamente:

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

Da mesma forma, IHtmlHelper.BeginForm gera tokens antifalsificação por padrão se o método do formulário não for GET.

A geração automática de tokens antifalsificação para elementos de formulário HTML ocorre quando a marca <form> contém o atributo method="post" e qualquer um das seguintes alternativas são verdadeiras:

  • O atributo de ação está vazio (action="").
  • O atributo de ação não foi fornecido (<form method="post">).

A geração automática de tokens antifalsificação para elementos de formulário HTML pode ser desabilitada:

  • Desabilite explicitamente tokens antifalsificação por meio do atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • O elemento formulário é recusado nos Auxiliares de Marca usando o símbolo ! recusar do Auxiliar de Marca:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Remova o FormTagHelper do modo de exibição. O FormTagHelper pode ser removido de um modo de exibição adicionando a seguinte diretiva à exibição Razor :

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

Observação

As Razor Páginas são protegidas automaticamente contra XSRF/CSRF. Para obter mais informações, consulte XSRF/CSRF e Razor Páginas.

A abordagem mais comum para se defender contra ataques CSRF é usar o Padrão de Token do Sincronizador (STP). O STP é usado quando o usuário solicita uma página com dados de formulário:

  1. O servidor envia um token associado à identidade do usuário atual para o cliente.
  2. O cliente envia de volta o token para o servidor para verificação.
  3. Se o servidor receber um token que não corresponda à identidade do usuário autenticado, a solicitação será rejeitada.

O token é exclusivo e imprevisível. O token também pode ser usado para garantir o sequenciamento adequado de uma série de solicitações (por exemplo, garantindo a sequência de solicitações de: página 1 > página 2 > página 3). Todos os formulários em ASP.NET Core modelos MVC e Páginas Razor geram tokens antifalsificação. O seguinte par de exemplos de modo de exibição gera tokens antifalsificação:

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

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

Adicione explicitamente um token antifalsificação a um <form> elemento sem usar Auxiliares de Marca com o auxiliar HTML @Html.AntiForgeryToken :

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

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

Em cada um dos casos anteriores, o ASP.NET Core adiciona um campo de formulário oculto semelhante ao seguinte exemplo:

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

O ASP.NET Core inclui três filtros para trabalhar com tokens antifalsificação:

Antifalsificação com AddControllers

Chamar AddControllersnão habilita tokens antifalsificação. AddControllersWithViews deve ser chamado para ter suporte interno a tokens antifalsificação.

Várias guias do navegador e o padrão de token do sincronizador

Com o Padrão de Token do Sincronizador, apenas a página carregada mais recentemente contém um token antifalsificação válido. O uso de várias guias pode ser problemático. Por exemplo, se um usuário abrir várias guias:

  • Somente a guia carregada mais recentemente contém um token antifalsificação válido.
  • As solicitações feitas de guias carregadas anteriormente falham com um erro: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Considere padrões de proteção CSRF alternativos se isso representar um problema.

Configurar antifalsificação com AntiforgeryOptions

Personalize AntiforgeryOptions no Program.cs:

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

Defina as propriedades Cookie antifalsificação usando as propriedades da classe CookieBuilder, conforme mostrado na tabela a seguir.

Opção Descrição
Cookie Determina as configurações usadas para criar o cookies antifalsificação.
FormFieldName O nome do campo de formulário oculto usado pelo sistema antifalsificação para renderizar tokens antifalsificação em modos de exibição.
HeaderName O nome do cabeçalho usado pelo sistema antifalsificação. Se null, o sistema considerará apenas os dados do formulário.
SuppressXFrameOptionsHeader Especifica se a geração do X-Frame-Options cabeçalho deve ser suprimida. Por padrão, o cabeçalho é gerado com um valor de "SAMEORIGIN". Assume o padrão de false.

Para obter mais informações, consulte CookieAuthenticationOptions.

Gerar tokens antifalsificação com IAntiforgery

IAntiforgery fornece a API para configurar recursos antifalsificação. IAntiforgery pode ser solicitado no Program.cs usando WebApplication.Services. O exemplo a seguir usa middleware da home page do aplicativo para gerar um token antifalsificação e enviá-lo na resposta como um 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);
});

O exemplo anterior define um cookie nomeado XSRF-TOKEN. O cliente pode ler esse cookie e fornecer seu valor como um cabeçalho anexado a solicitações AJAX. Por exemplo, o Angular inclui proteção XSRF interna que lê um cookie nomeado XSRF-TOKEN por padrão.

Exigir validação antifalsificação

O filtro de ação ValidateAntiForgeryToken pode ser aplicado a uma ação individual, a um controlador ou globalmente. As solicitações feitas às ações que têm esse filtro aplicado são bloqueadas, a menos que a solicitação inclua um token antifalsificação válido:

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

    return RedirectToAction();
}

O atributo ValidateAntiForgeryToken exige um token para solicitações para os métodos de ação que marca, incluindo solicitações HTTP GET. Se o atributo ValidateAntiForgeryToken for aplicado entre os controladores do aplicativo, ele poderá ser substituído pelo atributo IgnoreAntiforgeryToken.

Validar automaticamente tokens antifalsificação somente para métodos HTTP não seguros

Em vez de aplicar amplamente o atributo ValidateAntiForgeryToken e substituí-lo por atributos IgnoreAntiforgeryToken, o atributo AutoValidateAntiforgeryToken pode ser usado. Esse atributo funciona de forma idêntica ao atributo ValidateAntiForgeryToken, mas ele não requer tokens para solicitações feitas usando os seguintes métodos HTTP:

  • GET
  • HEAD
  • OPÇÕES
  • RASTREAMENTO

É recomendável usar amplamente AutoValidateAntiforgeryToken para cenários que não são de API. Esse atributo garante que as ações POST sejam protegidas por padrão. A alternativa é ignorar tokens antifalsificação por padrão, a menos que ValidateAntiForgeryToken seja aplicado a métodos de ação individuais. Nesse cenário, é mais provável que um método de ação POST seja deixado desprotegido por engano, deixando o aplicativo vulnerável a ataques CSRF. Todos os POSTs devem enviar o token antifalsificação.

As APIs não têm um mecanismo automático para enviar a partecookie que não é do token. A implementação provavelmente depende da implementação do código do cliente. Mostramos alguns exemplos abaixo:

Exemplo de nível de classe:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Exemplo global:

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

Substituir atributos antifalsificação globais ou de controlador

O filtro IgnoreAntiforgeryToken é usado para eliminar a necessidade de um token antifalsificação para uma determinada ação (ou controlador). Quando aplicado, esse filtro substitui ValidateAntiForgeryToken e AutoValidateAntiforgeryToken filtra especificados em um nível mais alto (globalmente ou em um controlador).

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

    return RedirectToAction();
}

Atualizar tokens após a autenticação

Os tokens devem ser atualizados depois que o usuário é autenticado redirecionando o usuário para um modo de exibição ou para a página de Páginas Razor.

JavaScript, AJAX e SPAs

Em aplicativos tradicionais baseados em HTML, tokens antifalsificação são passados para o servidor usando campos de formulário ocultos. Em SPAs e aplicativos modernos baseados em JavaScript, muitas solicitações são feitas programaticamente. Essas solicitações AJAX podem usar outras técnicas (como cabeçalhos de solicitação ou cookies) para enviar o token.

Se cookies forem usados para armazenar tokens de autenticação e autenticar solicitações de API no servidor, o CSRF é um problema em potencial. Se o armazenamento local for usado para armazenar o token, a vulnerabilidade CSRF poderá ser atenuada porque os valores do armazenamento local não são enviados automaticamente para o servidor a cada solicitação. Usar o armazenamento local para armazenar o token antifalsificação no cliente e enviar o token como um cabeçalho de solicitação é uma abordagem recomendada.

JavaScript

Usando JavaScript com modos de exibição, o token pode ser criado usando um serviço de dentro da exibição. Injete o serviço IAntiforgery na exibição e chame 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>
}

O exemplo anterior usa JavaScript para ler o valor do campo oculto para o cabeçalho POST do AJAX.

Essa abordagem elimina a necessidade de lidar diretamente com o cookies das configurações do servidor ou de lê-las do cliente. No entanto, quando não for possível injetar o serviço IAntiforgery, use JavaScript para acessar tokens em cookies:

  • Tokens de acesso em uma solicitação adicional para o servidor, normalmente same-origin.
  • Use o conteúdo cookie do para criar um cabeçalho com o valor do token.

Supondo que o script envie o token em um cabeçalho de solicitação chamado X-XSRF-TOKEN, configure o serviço antifalsificação para procurar o cabeçalho X-XSRF-TOKEN:

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

O exemplo a seguir adiciona um ponto de extremidade protegido que grava o token de solicitação em um JavaScript legível 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();

O exemplo a seguir usa JavaScript para fazer uma solicitação AJAX para obter o token e fazer outra solicitação com o cabeçalho apropriado:

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

Observação

Quando o token antifalsificação é fornecido no cabeçalho da solicitação e no conteúdo do formulário, somente o token no cabeçalho é validado.

antifalsificação com APIs mínimas

Minimal APIs não dão suporte ao uso dos filtros incluídos (ValidateAntiForgeryToken, AutoValidateAntiforgeryToken, IgnoreAntiforgeryToken), no entanto, IAntiforgery fornece as APIs necessárias para validar uma solicitação.

O exemplo a seguir cria um filtro que valida o token antifalsificação:

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

        });
    }
}

Em seguida, o filtro pode ser aplicado a um ponto de extremidade:

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

cookies de autenticação e antifalsificação do Windows

Ao usar a Autenticação do Windows, os pontos de extremidade do aplicativo devem ser protegidos contra ataques CSRF da mesma maneira que o feito para cookies. O navegador envia implicitamente o contexto de autenticação para o servidor e os pontos de extremidade precisam ser protegidos contra ataques CSRF.

Estender a antifalsificação

O tipo IAntiforgeryAdditionalDataProvider permite que os desenvolvedores estendam o comportamento do sistema anti-CSRF ao arredondar dados adicionais em cada token. O método GetAdditionalData é chamado sempre que um token de campo é gerado e o valor retornado é inserido no token gerado. Um implementador pode retornar um carimbo de data/hora, um nonce ou qualquer outro valor e, em seguida, chamar ValidateAdditionalData para validar esses dados quando o token for validado. O nome de usuário do cliente já está inserido nos tokens gerados, portanto, não é necessário incluir essas informações. Se um token incluir dados complementares, mas nenhum IAntiForgeryAdditionalDataProvider estiver configurado, os dados complementares não serão validados.

Recursos adicionais

A solicitação entre sites forjada (também conhecida como XSRF ou CSRF) é um ataque contra aplicativos Web hospedados, no qual um site mal-intencionado pode influenciar a interação entre um navegador cliente e um aplicativo Web que confia nesse navegador. Os ataques de solicitação entre sites forjada são possíveis porque os navegadores da Web enviam alguns tipos de tokens de autenticação automaticamente com cada solicitação a um site. Essa forma de exploração também é conhecida como ataque de um clique ou montagem de sessão, pois o ataque aproveita a sessão autenticada anteriormente pelo usuário.

Um exemplo de ataque CSRF:

  1. Um usuário entra no www.good-banking-site.example.com usando a autenticação de formulários. O servidor autentica o usuário e emite uma resposta que inclui uma autenticação cookie. O site é vulnerável a ataques porque confia em qualquer solicitação recebida com uma autenticação cookieválida.

  2. O usuário visita um site mal-intencionado, www.bad-crook-site.example.com.

    O site mal-intencionado, www.bad-crook-site.example.com, contém um formulário HTML semelhante ao seguinte exemplo:

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

    Observe que o formulário publica action no site vulnerável, não no site mal-intencionado. Essa é a parte "entre sites" do CSRF.

  3. O usuário seleciona o botão Enviar. O navegador faz a solicitação e inclui automaticamente a autenticação cookie para o domínio solicitado, www.good-banking-site.example.com.

  4. A solicitação é executada no servidor www.good-banking-site.example.com com o contexto de autenticação do usuário e pode executar qualquer ação que um usuário autenticado tenha permissão para executar.

Além do cenário em que o usuário seleciona o botão para enviar o formulário, o site mal-intencionado pode:

  • Executar um script que envia automaticamente o formulário.
  • Enviar o envio do formulário como uma solicitação AJAX.
  • Ocultar o formulário usando CSS.

Esses cenários alternativos não exigem nenhuma ação ou entrada do usuário além de visitar inicialmente o site mal-intencionado.

O uso de HTTPS não impede um ataque CSRF. O site mal-intencionado pode enviar uma solicitação https://www.good-banking-site.com/ tão facilmente quanto pode enviar uma solicitação insegura.

Alguns ataques visam pontos de extremidade que respondem a solicitações GET, nesse caso, uma marca de imagem pode ser usada para executar a ação. Essa forma de ataque é comum em sites de fórum que permitem imagens, mas bloqueiam JavaScript. Aplicativos que alteram o estado em solicitações GET, em que variáveis ou recursos são alterados, são vulneráveis a ataques mal-intencionados. Solicitações GET que alteram o estado são inseguras. Uma prática recomendada é nunca alterar o estado em uma solicitação GET.

Ataques CSRF são possíveis contra aplicativos Web que usam cookies para autenticação porque:

  • Os navegadores armazenam cookies emitidos por um aplicativo Web.
  • Os cookies armazenados incluem cookies de sessões para usuários autenticados.
  • Os navegadores enviam todos os cookies associados a um domínio para o aplicativo Web a cada solicitação, independentemente de como a solicitação para o aplicativo foi gerada no navegador.

No entanto, os ataques CSRF não se limitam à exploração de cookies. Por exemplo, a autenticação Básica e Digest também são vulneráveis. Depois que um usuário entra com a autenticação Básica ou Digest, o navegador envia automaticamente as credenciais até que a sessão termine.

Nesse contexto, a sessão refere-se à sessão do lado do cliente durante a qual o usuário é autenticado. Ela não está relacionado a sessões do lado do servidor ou ao Middleware de Sessão do ASP.NET Core.

Os usuários podem se proteger contra vulnerabilidades CSRF tomando precauções:

  • Saia dos aplicativos Web quando terminar de usá-los.
  • Limpe os cookies do navegador periodicamente.

No entanto, as vulnerabilidades CSRF são fundamentalmente um problema com o aplicativo Web, não com o usuário final.

Conceitos básicos sobre autenticação

A autenticação baseada em Cookie é uma forma popular de autenticação. Os sistemas de autenticação baseados em token estão crescendo em popularidade, especialmente para Aplicativos de Página Única (SPAs).

Quando um usuário se autentica usando seu nome de usuário e senha, ele recebe um token, contendo um tíquete de autenticação que pode ser usado para autenticação e autorização. O token é armazenado como um cookie que é enviado com cada solicitação que o cliente faz. A geração e a validação desse cookie é feira pelo Middleware de Autenticação do Cookie. O middleware serializa uma entidade de segurança de usuário em um criptografado cookie. Em solicitações subsequentes, o middleware valida o cookie, recria a entidade de segurança e atribui a entidade de segurança à propriedade HttpContext.User.

Autenticação baseada em token

Quando um usuário é autenticado, um token é emitido para ele (não um token antifalsificação). O token contém informações do usuário na forma de declarações ou um token de referência que aponta o aplicativo para o estado do usuário mantido no aplicativo. Quando um usuário tenta acessar um recurso que requer autenticação, o token é enviado para o aplicativo com um cabeçalho de autorização extra na forma de um token de portador. Essa abordagem torna o aplicativo sem estado. Em cada solicitação subsequente, o token é passado na solicitação de validação do lado do servidor. Esse token não é criptografado, ele é codificado. No servidor, o token é decodificado para acessar suas informações. Para enviar o token em solicitações subsequentes, armazene o token no armazenamento local do navegador. Não se preocupe com a vulnerabilidade CSRF se o token estiver armazenado no armazenamento local do navegador. O CSRF é uma preocupação quando o token é armazenado em um cookie. Para obter mais informações, consulte o exemplo de código SPA do problema do GitHub adiciona dois cookies.

Vários aplicativos hospedados em um domínio

Os ambientes de hospedagem compartilhada são vulneráveis ao sequestro de sessão, ao CSRF de logon e a outros ataques.

Embora example1.contoso.net e example2.contoso.net sejam hosts diferentes, há uma relação de confiança implícita entre hosts no domínio *.contoso.net. Essa relação de confiança implícita permite que hosts potencialmente não confiáveis afetem os cookies uns dos outros (as políticas de mesma origem que regem as solicitações AJAX não se aplicam necessariamente a cookies de HTTP).

Ataques que exploram as cookies confiáveis entre aplicativos hospedados no mesmo domínio podem ser evitados por não compartilhar domínios. Quando cada aplicativo é hospedado em seu próprio domínio, não há nenhuma relação de confiança implícita cookie a ser explorada.

Antifalsificação no ASP.NET Core

Aviso

ASP.NET Core implementa antifalsificação por meio da Proteção de Dados do ASP.NET Core. A pilha de proteção de dados deve ser configurada para funcionar em um farm de servidores. Para obter mais informações, consulte Como configurar a proteção de dados.

O middleware antifalsificação é adicionado ao contêiner de Injeção de dependência quando uma das seguintes APIs é chamada em Program.cs:

O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML. A marcação a seguir em um arquivo Razor gera tokens antifalsificação automaticamente:

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

Da mesma forma, IHtmlHelper.BeginForm gera tokens antifalsificação por padrão se o método do formulário não for GET.

A geração automática de tokens antifalsificação para elementos de formulário HTML ocorre quando a marca <form> contém o atributo method="post" e qualquer um das seguintes alternativas são verdadeiras:

  • O atributo de ação está vazio (action="").
  • O atributo de ação não foi fornecido (<form method="post">).

A geração automática de tokens antifalsificação para elementos de formulário HTML pode ser desabilitada:

  • Desabilite explicitamente tokens antifalsificação por meio do atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • O elemento formulário é recusado nos Auxiliares de Marca usando o símbolo ! recusar do Auxiliar de Marca:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Remova o FormTagHelper do modo de exibição. O FormTagHelper pode ser removido de um modo de exibição adicionando a seguinte diretiva à exibição Razor :

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

Observação

As Razor Páginas são protegidas automaticamente contra XSRF/CSRF. Para obter mais informações, consulte XSRF/CSRF e Razor Páginas.

A abordagem mais comum para se defender contra ataques CSRF é usar o Padrão de Token do Sincronizador (STP). O STP é usado quando o usuário solicita uma página com dados de formulário:

  1. O servidor envia um token associado à identidade do usuário atual para o cliente.
  2. O cliente envia de volta o token para o servidor para verificação.
  3. Se o servidor receber um token que não corresponda à identidade do usuário autenticado, a solicitação será rejeitada.

O token é exclusivo e imprevisível. O token também pode ser usado para garantir o sequenciamento adequado de uma série de solicitações (por exemplo, garantindo a sequência de solicitações de: página 1 > página 2 > página 3). Todos os formulários em ASP.NET Core modelos MVC e Páginas Razor geram tokens antifalsificação. O seguinte par de exemplos de modo de exibição gera tokens antifalsificação:

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

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

Adicione explicitamente um token antifalsificação a um <form> elemento sem usar Auxiliares de Marca com o auxiliar HTML @Html.AntiForgeryToken :

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

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

Em cada um dos casos anteriores, o ASP.NET Core adiciona um campo de formulário oculto semelhante ao seguinte exemplo:

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

O ASP.NET Core inclui três filtros para trabalhar com tokens antifalsificação:

Antifalsificação com AddControllers

Chamar AddControllersnão habilita tokens antifalsificação. AddControllersWithViews deve ser chamado para ter suporte interno a tokens antifalsificação.

Várias guias do navegador e o padrão de token do sincronizador

Com o Padrão de Token do Sincronizador, apenas a página carregada mais recentemente contém um token antifalsificação válido. O uso de várias guias pode ser problemático. Por exemplo, se um usuário abrir várias guias:

  • Somente a guia carregada mais recentemente contém um token antifalsificação válido.
  • As solicitações feitas de guias carregadas anteriormente falham com um erro: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Considere padrões de proteção CSRF alternativos se isso representar um problema.

Configurar antifalsificação com AntiforgeryOptions

Personalize AntiforgeryOptions no Program.cs:

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

Defina as propriedades Cookie antifalsificação usando as propriedades da classe CookieBuilder, conforme mostrado na tabela a seguir.

Opção Descrição
Cookie Determina as configurações usadas para criar o cookies antifalsificação.
FormFieldName O nome do campo de formulário oculto usado pelo sistema antifalsificação para renderizar tokens antifalsificação em modos de exibição.
HeaderName O nome do cabeçalho usado pelo sistema antifalsificação. Se null, o sistema considerará apenas os dados do formulário.
SuppressXFrameOptionsHeader Especifica se a geração do X-Frame-Options cabeçalho deve ser suprimida. Por padrão, o cabeçalho é gerado com um valor de "SAMEORIGIN". Assume o padrão de false.

Para obter mais informações, consulte CookieAuthenticationOptions.

Gerar tokens antifalsificação com IAntiforgery

IAntiforgery fornece a API para configurar recursos antifalsificação. IAntiforgery pode ser solicitado no Program.cs usando WebApplication.Services. O exemplo a seguir usa middleware da home page do aplicativo para gerar um token antifalsificação e enviá-lo na resposta como um 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);
});

O exemplo anterior define um cookie nomeado XSRF-TOKEN. O cliente pode ler esse cookie e fornecer seu valor como um cabeçalho anexado a solicitações AJAX. Por exemplo, o Angular inclui proteção XSRF interna que lê um cookie nomeado XSRF-TOKEN por padrão.

Exigir validação antifalsificação

O filtro de ação ValidateAntiForgeryToken pode ser aplicado a uma ação individual, a um controlador ou globalmente. As solicitações feitas às ações que têm esse filtro aplicado são bloqueadas, a menos que a solicitação inclua um token antifalsificação válido:

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

    return RedirectToAction();
}

O atributo ValidateAntiForgeryToken exige um token para solicitações para os métodos de ação que marca, incluindo solicitações HTTP GET. Se o atributo ValidateAntiForgeryToken for aplicado entre os controladores do aplicativo, ele poderá ser substituído pelo atributo IgnoreAntiforgeryToken.

Validar automaticamente tokens antifalsificação somente para métodos HTTP não seguros

Em vez de aplicar amplamente o atributo ValidateAntiForgeryToken e substituí-lo por atributos IgnoreAntiforgeryToken, o atributo AutoValidateAntiforgeryToken pode ser usado. Esse atributo funciona de forma idêntica ao atributo ValidateAntiForgeryToken, mas ele não requer tokens para solicitações feitas usando os seguintes métodos HTTP:

  • GET
  • HEAD
  • OPÇÕES
  • RASTREAMENTO

É recomendável usar amplamente AutoValidateAntiforgeryToken para cenários que não são de API. Esse atributo garante que as ações POST sejam protegidas por padrão. A alternativa é ignorar tokens antifalsificação por padrão, a menos que ValidateAntiForgeryToken seja aplicado a métodos de ação individuais. Nesse cenário, é mais provável que um método de ação POST seja deixado desprotegido por engano, deixando o aplicativo vulnerável a ataques CSRF. Todos os POSTs devem enviar o token antifalsificação.

As APIs não têm um mecanismo automático para enviar a partecookie que não é do token. A implementação provavelmente depende da implementação do código do cliente. Mostramos alguns exemplos abaixo:

Exemplo de nível de classe:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Exemplo global:

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

Substituir atributos antifalsificação globais ou de controlador

O filtro IgnoreAntiforgeryToken é usado para eliminar a necessidade de um token antifalsificação para uma determinada ação (ou controlador). Quando aplicado, esse filtro substitui ValidateAntiForgeryToken e AutoValidateAntiforgeryToken filtra especificados em um nível mais alto (globalmente ou em um controlador).

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

    return RedirectToAction();
}

Atualizar tokens após a autenticação

Os tokens devem ser atualizados depois que o usuário é autenticado redirecionando o usuário para um modo de exibição ou para a página de Páginas Razor.

JavaScript, AJAX e SPAs

Em aplicativos tradicionais baseados em HTML, tokens antifalsificação são passados para o servidor usando campos de formulário ocultos. Em SPAs e aplicativos modernos baseados em JavaScript, muitas solicitações são feitas programaticamente. Essas solicitações AJAX podem usar outras técnicas (como cabeçalhos de solicitação ou cookies) para enviar o token.

Se cookies forem usados para armazenar tokens de autenticação e autenticar solicitações de API no servidor, o CSRF é um problema em potencial. Se o armazenamento local for usado para armazenar o token, a vulnerabilidade CSRF poderá ser atenuada porque os valores do armazenamento local não são enviados automaticamente para o servidor a cada solicitação. Usar o armazenamento local para armazenar o token antifalsificação no cliente e enviar o token como um cabeçalho de solicitação é uma abordagem recomendada.

JavaScript

Usando JavaScript com modos de exibição, o token pode ser criado usando um serviço de dentro da exibição. Injete o serviço IAntiforgery na exibição e chame 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>
}

O exemplo anterior usa JavaScript para ler o valor do campo oculto para o cabeçalho POST do AJAX.

Essa abordagem elimina a necessidade de lidar diretamente com o cookies das configurações do servidor ou de lê-las do cliente. No entanto, quando não for possível injetar o serviçoIAntiforgery, o JavaScript também pode acessar o token em cookies, obtido de uma solicitação adicional para o servidor (geralmente same-origin), e usar o conteúdo do cookie para criar um cabeçalho com o valor do token.

Supondo que o script envie o token em um cabeçalho de solicitação chamado X-XSRF-TOKEN, configure o serviço antifalsificação para procurar o cabeçalho X-XSRF-TOKEN:

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

O exemplo a seguir adiciona um ponto de extremidade protegido que grava o token de solicitação em um cookie legível pelo JavaScript:

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

O exemplo a seguir usa JavaScript para fazer uma solicitação AJAX para obter o token e fazer outra solicitação com o cabeçalho apropriado:

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

cookies de autenticação e antifalsificação do Windows

Ao usar a Autenticação do Windows, os pontos de extremidade do aplicativo devem ser protegidos contra ataques CSRF da mesma maneira que o feito para cookies. O navegador envia implicitamente o contexto de autenticação para o servidor e, portanto, os pontos de extremidade precisam ser protegidos contra ataques CSRF.

Estender a antifalsificação

O tipo IAntiforgeryAdditionalDataProvider permite que os desenvolvedores estendam o comportamento do sistema anti-CSRF ao arredondar dados adicionais em cada token. O método GetAdditionalData é chamado sempre que um token de campo é gerado e o valor retornado é inserido no token gerado. Um implementador pode retornar um carimbo de data/hora, um nonce ou qualquer outro valor e, em seguida, chamar ValidateAdditionalData para validar esses dados quando o token for validado. O nome de usuário do cliente já está inserido nos tokens gerados, portanto, não é necessário incluir essas informações. Se um token incluir dados complementares, mas nenhum IAntiForgeryAdditionalDataProvider estiver configurado, os dados complementares não serão validados.

Recursos adicionais

A solicitação entre sites forjada (também conhecida como XSRF ou CSRF) é um ataque contra aplicativos Web hospedados, no qual um site mal-intencionado pode influenciar a interação entre um navegador cliente e um aplicativo Web que confia nesse navegador. Os ataques de solicitação entre sites forjada são possíveis porque os navegadores da Web enviam alguns tipos de tokens de autenticação automaticamente com cada solicitação a um site. Essa forma de exploração também é conhecida como ataque de um clique ou montagem de sessão, pois o ataque aproveita a sessão autenticada anteriormente pelo usuário.

Um exemplo de ataque CSRF:

  1. Um usuário entra no www.good-banking-site.example.com usando a autenticação de formulários. O servidor autentica o usuário e emite uma resposta que inclui uma autenticação cookie. O site é vulnerável a ataques porque confia em qualquer solicitação recebida com uma autenticação cookieválida.

  2. O usuário visita um site mal-intencionado, www.bad-crook-site.example.com.

    O site mal-intencionado, www.bad-crook-site.example.com, contém um formulário HTML semelhante ao seguinte exemplo:

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

    Observe que o formulário publica action no site vulnerável, não no site mal-intencionado. Essa é a parte "entre sites" do CSRF.

  3. O usuário seleciona o botão Enviar. O navegador faz a solicitação e inclui automaticamente a autenticação cookie para o domínio solicitado, www.good-banking-site.example.com.

  4. A solicitação é executada no servidor www.good-banking-site.example.com com o contexto de autenticação do usuário e pode executar qualquer ação que um usuário autenticado tenha permissão para executar.

Além do cenário em que o usuário seleciona o botão para enviar o formulário, o site mal-intencionado pode:

  • Executar um script que envia automaticamente o formulário.
  • Enviar o envio do formulário como uma solicitação AJAX.
  • Ocultar o formulário usando CSS.

Esses cenários alternativos não exigem nenhuma ação ou entrada do usuário além de visitar inicialmente o site mal-intencionado.

O uso de HTTPS não impede um ataque CSRF. O site mal-intencionado pode enviar uma solicitação https://www.good-banking-site.com/ tão facilmente quanto pode enviar uma solicitação insegura.

Alguns ataques visam pontos de extremidade que respondem a solicitações GET, nesse caso, uma marca de imagem pode ser usada para executar a ação. Essa forma de ataque é comum em sites de fórum que permitem imagens, mas bloqueiam JavaScript. Aplicativos que alteram o estado em solicitações GET, em que variáveis ou recursos são alterados, são vulneráveis a ataques mal-intencionados. Solicitações GET que alteram o estado são inseguras. Uma prática recomendada é nunca alterar o estado em uma solicitação GET.

Ataques CSRF são possíveis contra aplicativos Web que usam cookies para autenticação porque:

  • Os navegadores armazenam cookies emitidos por um aplicativo Web.
  • Os cookies armazenados incluem cookies de sessões para usuários autenticados.
  • Os navegadores enviam todos os cookies associados a um domínio para o aplicativo Web a cada solicitação, independentemente de como a solicitação para o aplicativo foi gerada no navegador.

No entanto, os ataques CSRF não se limitam à exploração de cookies. Por exemplo, a autenticação Básica e Digest também são vulneráveis. Depois que um usuário entra com a autenticação Básica ou Digest, o navegador envia automaticamente as credenciais até que a sessão termine.

Nesse contexto, a sessão refere-se à sessão do lado do cliente durante a qual o usuário é autenticado. Ela não está relacionado a sessões do lado do servidor ou ao Middleware de Sessão do ASP.NET Core.

Os usuários podem se proteger contra vulnerabilidades CSRF tomando precauções:

  • Saia dos aplicativos Web quando terminar de usá-los.
  • Limpe os cookies do navegador periodicamente.

No entanto, as vulnerabilidades CSRF são fundamentalmente um problema com o aplicativo Web, não com o usuário final.

Conceitos básicos sobre autenticação

A autenticação baseada em Cookie é uma forma popular de autenticação. Os sistemas de autenticação baseados em token estão crescendo em popularidade, especialmente para Aplicativos de Página Única (SPAs).

Quando um usuário se autentica usando seu nome de usuário e senha, ele recebe um token, contendo um tíquete de autenticação que pode ser usado para autenticação e autorização. O token é armazenado como um cookie que é enviado com cada solicitação que o cliente faz. A geração e a validação desse cookie é feira pelo Middleware de Autenticação do Cookie. O middleware serializa uma entidade de segurança de usuário em um criptografado cookie. Em solicitações subsequentes, o middleware valida o cookie, recria a entidade de segurança e atribui a entidade de segurança à propriedade HttpContext.User.

Autenticação baseada em token

Quando um usuário é autenticado, um token é emitido para ele (não um token antifalsificação). O token contém informações do usuário na forma de declarações ou um token de referência que aponta o aplicativo para o estado do usuário mantido no aplicativo. Quando um usuário tenta acessar um recurso que requer autenticação, o token é enviado para o aplicativo com um cabeçalho de autorização extra na forma de um token de portador. Essa abordagem torna o aplicativo sem estado. Em cada solicitação subsequente, o token é passado na solicitação de validação do lado do servidor. Esse token não é criptografado, ele é codificado. No servidor, o token é decodificado para acessar suas informações. Para enviar o token em solicitações subsequentes, armazene o token no armazenamento local do navegador. Não se preocupe com a vulnerabilidade CSRF se o token estiver armazenado no armazenamento local do navegador. O CSRF é uma preocupação quando o token é armazenado em um cookie. Para obter mais informações, consulte o exemplo de código SPA do problema do GitHub adiciona dois cookies.

Vários aplicativos hospedados em um domínio

Os ambientes de hospedagem compartilhada são vulneráveis ao sequestro de sessão, ao CSRF de logon e a outros ataques.

Embora example1.contoso.net e example2.contoso.net sejam hosts diferentes, há uma relação de confiança implícita entre hosts no domínio *.contoso.net. Essa relação de confiança implícita permite que hosts potencialmente não confiáveis afetem os cookies uns dos outros (as políticas de mesma origem que regem as solicitações AJAX não se aplicam necessariamente a cookies de HTTP).

Ataques que exploram as cookies confiáveis entre aplicativos hospedados no mesmo domínio podem ser evitados por não compartilhar domínios. Quando cada aplicativo é hospedado em seu próprio domínio, não há nenhuma relação de confiança implícita cookie a ser explorada.

Configuração antifalsificação do ASP.NET Core

Aviso

ASP.NET Core implementa antifalsificação por meio da Proteção de Dados do ASP.NET Core. A pilha de proteção de dados deve ser configurada para funcionar em um farm de servidores. Para obter mais informações, consulte Como configurar a proteção de dados.

O middleware antifalsificação é adicionado ao contêiner de Injeção de dependência quando uma das seguintes APIs é chamada em Startup.ConfigureServices:

No ASP.NET Core 2.0 ou posterior, o FormTagHelperinjeta tokens antifalsificação em elementos de formulário HTML. A marcação a seguir em um arquivo Razor gera tokens antifalsificação automaticamente:

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

Da mesma forma, IHtmlHelper.BeginForm gera tokens antifalsificação por padrão se o método do formulário não for GET.

A geração automática de tokens antifalsificação para elementos de formulário HTML ocorre quando a marca <form> contém o atributo method="post" e qualquer um das seguintes alternativas são verdadeiras:

  • O atributo de ação está vazio (action="").
  • O atributo de ação não foi fornecido (<form method="post">).

A geração automática de tokens antifalsificação para elementos de formulário HTML pode ser desabilitada:

  • Desabilite explicitamente tokens antifalsificação por meio do atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • O elemento formulário é recusado nos Auxiliares de Marca usando o símbolo ! recusar do Auxiliar de Marca:

    <!form method="post">
        ...
    </!form>
    
  • Remova o FormTagHelper do modo de exibição. O FormTagHelper pode ser removido de um modo de exibição adicionando a seguinte diretiva à exibição Razor :

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

Observação

As Razor Páginas são protegidas automaticamente contra XSRF/CSRF. Para obter mais informações, consulte XSRF/CSRF e Razor Páginas.

A abordagem mais comum para se defender contra ataques CSRF é usar o Padrão de Token do Sincronizador (STP). O STP é usado quando o usuário solicita uma página com dados de formulário:

  1. O servidor envia um token associado à identidade do usuário atual para o cliente.
  2. O cliente envia de volta o token para o servidor para verificação.
  3. Se o servidor receber um token que não corresponda à identidade do usuário autenticado, a solicitação será rejeitada.

O token é exclusivo e imprevisível. O token também pode ser usado para garantir o sequenciamento adequado de uma série de solicitações (por exemplo, garantindo a sequência de solicitações de: página 1 > página 2 > página 3). Todos os formulários em ASP.NET Core modelos MVC e Páginas Razor geram tokens antifalsificação. O seguinte par de exemplos de modo de exibição gera tokens antifalsificação:

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

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

Adicione explicitamente um token antifalsificação a um <form> elemento sem usar Auxiliares de Marca com o auxiliar HTML @Html.AntiForgeryToken :

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

Em cada um dos casos anteriores, o ASP.NET Core adiciona um campo de formulário oculto semelhante ao seguinte exemplo:

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

O ASP.NET Core inclui três filtros para trabalhar com tokens antifalsificação:

Opções de antifalsificação

Personalize AntiforgeryOptions no Startup.ConfigureServices:

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

Defina as propriedades Cookie antifalsificação usando as propriedades da classe CookieBuilder, conforme mostrado na tabela a seguir.

Opção Descrição
Cookie Determina as configurações usadas para criar o cookies antifalsificação.
FormFieldName O nome do campo de formulário oculto usado pelo sistema antifalsificação para renderizar tokens antifalsificação em modos de exibição.
HeaderName O nome do cabeçalho usado pelo sistema antifalsificação. Se null, o sistema considerará apenas os dados do formulário.
SuppressXFrameOptionsHeader Especifica se a geração do X-Frame-Options cabeçalho deve ser suprimida. Por padrão, o cabeçalho é gerado com um valor de "SAMEORIGIN". Assume o padrão de false.

Para obter mais informações, consulte CookieAuthenticationOptions.

Configurar recursos antifalsificação com o IAntiforgery

O IAntiforgery fornece a API para configurar recursos antifalsificação. O IAntiforgery pode ser solicitado no método Configure da classe Startup .

No exemplo a seguir:

  • O exemplo a seguir usa middleware da home page do aplicativo para gerar um token antifalsificação e enviá-lo na resposta como um cookie.
  • O token de solicitação é enviado como um cookie legível pelo JavaScript com a convenção de nomenclatura do Angular padrão descrita na seção 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);
    });
}

Exigir validação antifalsificação

O filtro de ação ValidateAntiForgeryToken pode ser aplicado a uma ação individual, a um controlador ou globalmente. As solicitações feitas às ações que têm esse filtro aplicado são bloqueadas, a menos que a solicitação inclua um token antifalsificação válido.

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

O atributo ValidateAntiForgeryToken exige um token para solicitações para os métodos de ação que marca, incluindo solicitações HTTP GET. Se o atributo ValidateAntiForgeryToken for aplicado entre os controladores do aplicativo, ele poderá ser substituído pelo atributo IgnoreAntiforgeryToken.

Observação

O ASP.NET Core não é compatível com a adição automática de tokens antifalsificação a solicitações GET.

Validar automaticamente tokens antifalsificação somente para métodos HTTP não seguros

Os aplicativos ASP.NET Core não geram tokens antifalsificação para métodos HTTP seguros (GET, HEAD, OPTIONS e TRACE). Em vez de aplicar amplamente o atributo ValidateAntiForgeryToken e substituí-lo por atributos IgnoreAntiforgeryToken, o atributo AutoValidateAntiforgeryToken pode ser usado. Esse atributo funciona de forma idêntica ao atributo ValidateAntiForgeryToken, mas ele não requer tokens para solicitações feitas usando os seguintes métodos HTTP:

  • GET
  • HEAD
  • OPÇÕES
  • RASTREAMENTO

É recomendável usar amplamente AutoValidateAntiforgeryToken para cenários que não são de API. Esse atributo garante que as ações POST sejam protegidas por padrão. A alternativa é ignorar tokens antifalsificação por padrão, a menos que ValidateAntiForgeryToken seja aplicado a métodos de ação individuais. Nesse cenário, é mais provável que um método de ação POST seja deixado desprotegido por engano, deixando o aplicativo vulnerável a ataques CSRF. Todos os POSTs devem enviar o token antifalsificação.

As APIs não têm um mecanismo automático para enviar a partecookie que não é do token. A implementação provavelmente depende da implementação do código do cliente. Mostramos alguns exemplos abaixo:

Exemplo de nível de classe:

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

Exemplo global:

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

Substituir atributos antifalsificação globais ou de controlador

O filtro IgnoreAntiforgeryToken é usado para eliminar a necessidade de um token antifalsificação para uma determinada ação (ou controlador). Quando aplicado, esse filtro substitui ValidateAntiForgeryToken e AutoValidateAntiforgeryToken filtra especificados em um nível mais alto (globalmente ou em um controlador).

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

Atualizar tokens após a autenticação

Os tokens devem ser atualizados depois que o usuário é autenticado redirecionando o usuário para um modo de exibição ou para a página de Páginas Razor.

JavaScript, AJAX e SPAs

Em aplicativos tradicionais baseados em HTML, tokens antifalsificação são passados para o servidor usando campos de formulário ocultos. Em SPAs e aplicativos modernos baseados em JavaScript, muitas solicitações são feitas programaticamente. Essas solicitações AJAX podem usar outras técnicas (como cabeçalhos de solicitação ou cookies) para enviar o token.

Se cookies forem usados para armazenar tokens de autenticação e autenticar solicitações de API no servidor, o CSRF é um problema em potencial. Se o armazenamento local for usado para armazenar o token, a vulnerabilidade CSRF poderá ser atenuada porque os valores do armazenamento local não são enviados automaticamente para o servidor a cada solicitação. Usar o armazenamento local para armazenar o token antifalsificação no cliente e enviar o token como um cabeçalho de solicitação é uma abordagem recomendada.

JavaScript

Usando JavaScript com modos de exibição, o token pode ser criado usando um serviço de dentro da exibição. Injete o serviço IAntiforgery na exibição e chame 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>

Essa abordagem elimina a necessidade de lidar diretamente com o cookies das configurações do servidor ou de lê-las do cliente.

O exemplo anterior usa JavaScript para ler o valor do campo oculto para o cabeçalho POST do AJAX.

O JavaScript também pode acessar tokens em cookies e usar o conteúdo do cookie para criar um cabeçalho com o valor do token.

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

Supondo que o script envie o token em um cabeçalho de solicitação chamado X-CSRF-TOKEN, configure o serviço antifalsificação para procurar o cabeçalho X-CSRF-TOKEN:

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

O exemplo a seguir usa JavaScript para fazer uma solicitação AJAX com o cabeçalho apropriado:

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

O JS Angular usa uma convenção para lidar com CSRF. Se o servidor enviar um cookie com o nome XSRF-TOKEN, o serviço AngularJS$http adicionará o valor cookie a um cabeçalho quando enviar uma solicitação ao servidor. Esse processo é automático. O cliente não precisa definir o cabeçalho explicitamente. O nome do cabeçalho é X-XSRF-TOKEN. O servidor deve detectar esse cabeçalho e validar seu conteúdo.

Para que o ASP.NET Core API funcione com essa convenção na inicialização do aplicativo:

  • Configure seu aplicativo para fornecer um token em um cookie chamado XSRF-TOKEN.
  • Configure o serviço antifalsificação para procurar um cabeçalho chamado X-XSRF-TOKEN, que é o nome de cabeçalho padrão do Angular para enviar o 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");
}

Observação

Quando o token antifalsificação é fornecido no cabeçalho da solicitação e no conteúdo do formulário, somente o token no cabeçalho é validado.

cookies de autenticação e antifalsificação do Windows

Ao usar a Autenticação do Windows, os pontos de extremidade do aplicativo devem ser protegidos contra ataques CSRF da mesma maneira que o feito para cookies. O navegador envia implicitamente o contexto de autenticação para o servidor e, portanto, os pontos de extremidade precisam ser protegidos contra ataques CSRF.

Estender a antifalsificação

O tipo IAntiforgeryAdditionalDataProvider permite que os desenvolvedores estendam o comportamento do sistema anti-CSRF ao arredondar dados adicionais em cada token. O método GetAdditionalData é chamado sempre que um token de campo é gerado e o valor retornado é inserido no token gerado. Um implementador pode retornar um carimbo de data/hora, um nonce ou qualquer outro valor e, em seguida, chamar ValidateAdditionalData para validar esses dados quando o token for validado. O nome de usuário do cliente já está inserido nos tokens gerados, portanto, não é necessário incluir essas informações. Se um token incluir dados complementares, mas nenhum IAntiForgeryAdditionalDataProvider estiver configurado, os dados complementares não serão validados.

Recursos adicionais