Prevención de ataques de falsificación de solicitud entre sitios (XSRF/CSRF) en ASP.NET Core

Por Fiyaz Hasan y Rick Anderson

La falsificación de solicitud entre sitios es un ataque contra aplicaciones hospedadas en web, en el que una aplicación web malintencionada puede influir en la interacción entre un explorador cliente y una aplicación web que confía en ese explorador. Estos ataques son posibles porque los exploradores web envían automáticamente algunos tipos de token de autenticación con cada solicitud en un sitio web. Esta forma de vulnerabilidad de seguridad también se conoce como ataque con un clic o montaje en la sesión porque el ataque aprovecha la sesión del usuario autenticada anteriormente. La falsificación de solicitudes entre sitios también se conoce como XSRF o CSRF.

Ejemplo de un ataque de CSRF:

  1. Un usuario inicia sesión en www.good-banking-site.example.com con la autenticación de formularios. El servidor autentica al usuario y emite una respuesta que incluye una autenticación cookie. El sitio es vulnerable a ataques porque confía en cualquier solicitud que reciba con una cookie de autenticación válida.

  2. El usuario visita un sitio malintencionado, www.bad-crook-site.example.com.

    El sitio malintencionado, www.bad-crook-site.example.com, contiene un formulario HTML similar al ejemplo siguiente:

    <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 el action del formulario publica en el sitio vulnerable, no en el sitio malintencionado. Esta es la parte "entre sitios" de CSRF.

  3. El usuario selecciona el botón Enviar. El explorador realiza la solicitud e incluye automáticamente la cookie de autenticación para el dominio solicitado, www.good-banking-site.example.com.

  4. La solicitud se ejecuta en el servidor de www.good-banking-site.example.com con el contexto de autenticación del usuario y puede realizar cualquier acción que un usuario autenticado pueda realizar.

Además del escenario en el que el usuario selecciona el botón para enviar el formulario, el sitio malintencionado podría:

  • Ejecutar un script que envíe automáticamente el formulario.
  • Enviar el envío del formulario como una solicitud AJAX.
  • Ocultar el formulario mediante CSS.

Estos escenarios alternativos no requieren ninguna acción ni entrada por parte del usuario aparte de visitar inicialmente el sitio malintencionado.

El uso de HTTPS no impide un ataque de CSRF. El sitio malintencionado puede enviar una solicitud https://www.good-banking-site.com/ con la misma facilidad con la que puede enviar una solicitud no segura.

Algunos ataques se dirigen a puntos de conexión que responden a solicitudes GET, en cuyo caso se puede usar una etiqueta de imagen para realizar la acción. Esta forma de ataque es común en sitios de foros que permiten imágenes pero bloquean JavaScript. Las aplicaciones que cambian el estado de las solicitudes GET, en las que se modifican variables o recursos, son vulnerables a ataques malintencionados. Las solicitudes GET que cambian de estado no son seguras. Un procedimiento recomendado es no cambiar nunca el estado en una solicitud GET.

Los ataques de CSRF se pueden producir en las aplicaciones web que usan cookies para la autenticación porque:

  • Los exploradores almacenan cookies emitidas por una aplicación web.
  • Las cookies almacenadas incluyen cookies de sesión para los usuarios autenticados.
  • Los exploradores envían todas las cookies asociadas con un dominio a la aplicación web cada solicitud independientemente de cómo se generó la solicitud a la aplicación en el explorador.

Sin embargo, los ataques de CSRF no se limitan a aprovechar las vulnerabilidades de las cookies. Por ejemplo, la autenticación básica e implícita también son vulnerables. Una vez que un usuario inicia sesión con la autenticación básica o implícita, el explorador envía automáticamente las credenciales hasta que finaliza la sesión.

En este contexto, la sesión hace referencia a la sesión del lado cliente durante la cual se autentica el usuario. No está relacionado con las sesiones del lado servidor ni el middleware de sesión de ASP.NET Core.

Para protegerse contra las vulnerabilidades de CSRF, los usuarios pueden tomar precauciones:

  • Cierre la sesión de las aplicaciones web cuando termine de usarlas.
  • Borre periódicamente las cookies del explorador.

Sin embargo, las vulnerabilidades de CSRF son fundamentalmente un problema con la aplicación web, no con el usuario final.

Aspectos básicos de la autenticación

La autenticación basada en Cookies es una forma popular de autenticación. Los sistemas de autenticación basados en tokens están creciendo en popularidad, especialmente para aplicaciones de página única (SPA).

Cuando un usuario se autentica con su nombre de usuario y contraseña, se emite un token que contiene un vale de autenticación. El token se puede usar para la autenticación y autorización. El token se almacena como una cookie que se envía con cada solicitud que realiza el cliente. El middleware de autenticación de Cookie realiza la generación y validación de esta cookie. El middleware serializa una entidad de seguridad de usuario en una cookie cifrada. En las solicitudes posteriores, el middleware valida la cookie, vuelve a crear la entidad de seguridad y asigna la entidad de seguridad a la propiedad HttpContext.User.

Autenticación basada en tokens

Cuando se autentica un usuario, se emite un token (no un token de antifalsificación). El token contiene información de usuario en forma de notificaciones o un token de referencia que apunta la aplicación al estado de usuario mantenido en la aplicación. Cuando un usuario intenta acceder a un recurso que requiere autenticación, el token se envía a la aplicación con un encabezado de autorización adicional en forma de token de portador. Este enfoque hace que la aplicación no tenga estado. En cada solicitud posterior, el token se pasa en la solicitud de validación del lado servidor. Este token no está cifrado; está codificado. En el servidor, el token se descodifica para acceder a su información. Para enviar el token en solicitudes posteriores, almacene el token en el almacenamiento local del explorador. Colocar un token en el almacenamiento local del explorador, recuperarlo y usarlo como token de portador proporciona protección contra ataques de CSRF. Sin embargo, si la aplicación es vulnerable a la inserción de scripts a través de XSS o un archivo JavaScript externo en peligro, un atacante podría recuperar cualquier valor del almacenamiento local y enviárselo a sí mismo. ASP.NET Core codifica todas las salidas del lado servidor de las variables de forma predeterminada, lo que reduce el riesgo de XSS. Si invalida este comportamiento mediante Html.Raw o código personalizado con entradas que no son de confianza, puede aumentar el riesgo de XSS.

No se preocupe por la vulnerabilidad de CSRF si el token se almacena en el almacenamiento local del explorador. La CSRF es un problema cuando el token se almacena en una cookie. Para obtener más información, consulte el problema de GitHub El ejemplo de código SPA agrega dos cookies.

Varias aplicaciones hospedadas en un dominio

Los entornos de hospedaje compartidos son vulnerables al secuestro de sesión, a la CSRF de inicio de sesión y a otros ataques.

Aunque example1.contoso.net y example2.contoso.net son hosts diferentes, hay una relación de confianza implícita entre los hosts bajo el dominio *.contoso.net. Esta relación de confianza implícita permite a los hosts que podrían no ser de confianza afectar a las cookies de otros hosts (las directivas de mismo origen que rigen las solicitudes de AJAX no se aplican necesariamente a las cookies HTTP).

Los ataques que aprovechan las cookies de confianza entre aplicaciones hospedadas en el mismo dominio pueden evitarse si no se comparten dominios. Cuando cada aplicación se hospeda en su propio dominio, no hay ninguna relación de confianza implícita de cookies que se pueda aprovechar.

Antifalsificación en ASP.NET Core

Advertencia

ASP.NET Core implementa la antifalsificación mediante la Protección de datos de ASP.NET Core. La pila de protección de datos debe configurarse para que funcione en una granja de servidores. Para obtener más información, consulte Configuración de la protección de datos.

El middleware de antifalsificación se agrega al contenedor de inserción de dependencias cuando se llama a una de las siguientes API en Program.cs:

Para más información, consulte Antifalsificación con API mínima.

El elemento FormTagHelper inserta tokens antifalsificación en los elementos de formulario HTML. El siguiente marcado en un archivo de Razor genera automáticamente tokens de antifalsificación:

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

De forma similar, IHtmlHelper.BeginForm genera tokens antifalsificación de forma predeterminada si el método del formulario no es GET.

La generación automática de tokens de antifalsificación para elementos de formulario HTML se produce cuando la etiqueta <form> contiene el atributo method="post" y se cumple alguna de las siguientes condiciones:

  • El atributo action está vacío (action="").
  • El atributo action no se proporciona (<form method="post">).

La generación automática de tokens de antifalsificación para elementos de formulario HTML se puede deshabilitar:

  • Deshabilite explícitamente tokens de antifalsificación con el atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • El elemento de formulario se ha excluido de los asistentes de etiquetas mediante el símbolo de rechazo ! del asistente de etiquetas:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Quite FormTagHelper de la vista. FormTagHelper se puede quitar de una vista agregando la siguiente directiva a la vista de Razor:

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

Nota:

Razor Pages se protegen automáticamente de XSRF/CSRF. Para obtener más información, vea XSRF/CSRF y Razor Pages.

El enfoque más común para defenderse contra ataques de CSRF es usar el patrón de token del sincronizador (STP). El STP se usa cuando el usuario solicita una página con datos de formulario:

  1. El servidor envía un token asociado a la identidad del usuario actual al cliente.
  2. El cliente devuelve el token al servidor para su comprobación.
  3. Si el servidor recibe un token que no coincide con la identidad del usuario autenticado, se rechaza la solicitud.

El token es único e imprevisible. El token también se puede usar para garantizar la secuenciación correcta de una serie de solicitudes (por ejemplo, garantizar la secuencia de solicitudes de: página 1 > página 2 > página 3). Todos los formularios de las plantillas de Razor Pages y de ASP.NET Core MVC generan tokens de antifalsificación. El siguiente par de ejemplos de vista genera tokens de antifalsificación:

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

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

Agregue explícitamente un token de antifalsificación a un elemento <form> sin usar asistentes de etiquetas con el asistente de HTML @Html.AntiForgeryToken:

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

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

En cada uno de los casos anteriores, ASP.NET Core agrega un campo de formulario oculto similar al ejemplo siguiente:

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

ASP.NET Core incluye tres filtros para trabajar con tokens de antifalsificación:

Antifraude con AddControllers

La llamada a AddControllersno habilita tokens de antifalsificación. Se debe llamar a AddControllersWithViews para tener compatibilidad integrada con tokens de antifalsificación.

Varias pestañas del explorador y el patrón de token del sincronizador

Con el patrón de token del sincronizador, solo la página cargada más recientemente contiene un token de antifalsificación válido. El uso de varias pestañas puede ser problemático. Por ejemplo, si un usuario abre varias pestañas:

  • Solo la pestaña cargada más recientemente contiene un token de antifalsificación válido.
  • Las solicitudes realizadas desde pestañas cargadas anteriormente producen un error: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Considere la posibilidad de usar patrones alternativos de protección contra CSRF si esto plantea un problema.

Configuración de la antifalsificación con AntiforgeryOptions

Personalización de AntiforgeryOptions en Program.cs:

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

Establezca las propiedades de la Cookie de antifalsificación mediante las propiedades de la clase CookieBuilder, como se muestra en la tabla siguiente.

Opción Descripción
Cookie Determina la configuración usada para crear la cookie de antifalsificación.
FormFieldName Nombre del campo de formulario oculto utilizado por el sistema de antifalsificación para representar tokens de antifalsificación en vistas.
HeaderName Nombre del encabezado utilizado por el sistema de antifalsificación. Si es null, el sistema solo tiene en cuenta los datos del formulario.
SuppressXFrameOptionsHeader Especifica si se va a suprimir la generación del encabezado X-Frame-Options. De forma predeterminada, el encabezado se genera con un valor de "SAMEORIGIN". Tiene como valor predeterminado false.

Para obtener más información, vea CookieAuthenticationOptions.

Generación de tokens de antifalsificación con IAntiforgery

IAntiforgeryproporciona la API para configurar características de antifalsificación. IAntiforgery se puede solicitar en Program.cs mediante WebApplication.Services. En el ejemplo siguiente se usa middleware de la página principal de la aplicación para generar un token de antifalsificación y enviarlo en la respuesta como 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);
});

En el ejemplo anterior se establece una cookie denominada XSRF-TOKEN. El cliente puede leer esta cookie y proporcionar su valor como un encabezado adjunto a las solicitudes de AJAX. Por ejemplo, Angular incluye protección integrada contra XSRF que lee una cookie denominada XSRF-TOKEN de forma predeterminada.

Requerir validación de antifalsificación

El filtro de acción ValidateAntiForgeryToken se puede aplicar a una acción individual, un controlador o globalmente. Las solicitudes realizadas a las acciones que tienen aplicado este filtro se bloquean a menos que la solicitud incluya un token de antifalsificación válido:

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

    return RedirectToAction();
}

El atributo ValidateAntiForgeryToken requiere un token para las solicitudes a los métodos de acción que marca, incluidas las solicitudes HTTP GET. Si el atributo ValidateAntiForgeryToken se aplica en los controladores de la aplicación, se puede invalidar con el atributo IgnoreAntiforgeryToken.

Validar automáticamente tokens de antifalsificación solo para métodos HTTP no seguros

En lugar de aplicar ampliamente el atributo ValidateAntiForgeryToken y, a continuación, reemplazarlo con atributos IgnoreAntiforgeryToken, se puede usar el atributo AutoValidateAntiforgeryToken. Este atributo funciona de forma idéntica al atributo ValidateAntiForgeryToken, salvo que no requiere tokens para las solicitudes realizadas mediante los siguientes métodos HTTP:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

Se recomienda usar ampliamente AutoValidateAntiforgeryToken para escenarios que no son de API. Este atributo garantiza que las acciones POST estén protegidas de forma predeterminada. La alternativa es omitir los tokens de antifalsificación de forma predeterminada, a menos que ValidateAntiForgeryToken se aplique a métodos de acción individuales. Es más probable que en este escenario un método de acción POST se deje desprotegido por error, lo que hace que la aplicación sea vulnerable a los ataques de CSRF. Todos los POST deben enviar el token de antifalsificación.

Las API no tienen un mecanismo automático para enviar la parte del token que no es una cookie. La implementación probablemente depende de la implementación del código de cliente. A continuación se muestran algunos ejemplos:

Ejemplo de nivel de clase:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Ejemplo global:

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

Invalidación de atributos de antifalsificación globales o de controlador

El filtro IgnoreAntiforgeryToken se usa para evitar tener que usar un token de antifalsificación para una acción determinada (o controlador). Cuando se aplica, este filtro invalida los filtros ValidateAntiForgeryToken y AutoValidateAntiforgeryToken especificados en un nivel superior (global o en un controlador).

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

    return RedirectToAction();
}

Actualización de tokens después de la autenticación

Los tokens se deben actualizar después de que el usuario se autentique al redirigir al usuario a una vista o página de Razor Pages.

JavaScript, AJAX y SPA

En las aplicaciones tradicionales basadas en HTML, los tokens de antifalsificación se pasan al servidor mediante campos de formulario ocultos. En las aplicaciones y SPA modernas basadas en JavaScript, muchas solicitudes se realizan mediante programación. Estas solicitudes de AJAX pueden usar otras técnicas (como encabezados de solicitud o cookies) para enviar el token.

Si se usan cookies para almacenar tokens de autenticación y para autenticar solicitudes de API en el servidor, la CSRF es un posible problema. Si el almacenamiento local se usa para almacenar el token, se podría mitigar la vulnerabilidad de CSRF porque los valores del almacenamiento local no se envían automáticamente al servidor con cada solicitud. El uso del almacenamiento local para almacenar el token de antifalsificación en el cliente y enviar el token como encabezado de solicitud es un enfoque recomendado.

Blazor

Para obtener más información, consulte AutenticaciónBlazor y autorización de ASP.NET Core.

JavaScript

Al usar JavaScript con vistas, el token se puede crear mediante un servicio desde dentro de la vista. Inserte el servicio IAntiforgery en la vista y llame a 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>
}

En el ejemplo anterior se usa JavaScript para leer el valor de campo oculto del encabezado POST de AJAX.

Este enfoque evita tener que tratar directamente con la configuración de cookies del servidor o leerlas del cliente. Sin embargo, si no es posible insertar el servicio IAntiforgery, use JavaScript para acceder a tokens en cookies:

  • Acceda a tokens en una solicitud adicional al servidor, normalmente same-origin.
  • Use el contenido de la cookie para crear un encabezado con el valor del token.

Suponiendo que el script envía el token en un encabezado de solicitud denominado X-XSRF-TOKEN, configure el servicio de antifalsificación para buscar el encabezado X-XSRF-TOKEN:

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

En el ejemplo siguiente se agrega un punto de conexión protegido que escribe el token de solicitud en una cookie legible en 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();

En el ejemplo siguiente se usa JavaScript para realizar una solicitud de AJAX para obtener el token y realizar otra solicitud con el encabezado adecuado:

var response = await fetch("/antiforgery/token", {
    method: "GET",
    headers: { "Authorization": authorizationToken }
});

if (response.ok) {
    // https://developer.mozilla.org/docs/web/api/document/cookie
    const xsrfToken = document.cookie
        .split("; ")
        .find(row => row.startsWith("XSRF-TOKEN="))
        .split("=")[1];

    response = await fetch("/JavaScript/FetchEndpoint", {
        method: "POST",
        headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
    });

    if (response.ok) {
        resultElement.innerText = await response.text();
    } else {
        resultElement.innerText = `Request Failed: ${response.status}`
    }
} else {    
    resultElement.innerText = `Request Failed: ${response.status}`
}

Nota:

Cuando el token antifalsificación se proporciona tanto en la cabecera de la solicitud como en la carga útil del formulario, sólo se valida el token de la cabecera.

Antifalsificación con API mínimas

Llame a AddAntiforgery y UseAntiforgery(IApplicationBuilder) para registrar servicios de antifalsificación en la inserción de dependencias. Los tokens de antifalsificación se usan para mitigar losataques de falsificación de solicitud entre sitios.

var builder = WebApplication.CreateBuilder();

builder.Services.AddAntiforgery();

var app = builder.Build();

app.UseAntiforgery();

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

app.Run();

Middleware de antifalsificación:

El token de antifalsificación solo se valida si:

  • El punto de conexión contiene metadatos que implementan IAntiforgeryMetadata donde RequiresValidation=true.
  • El método HTTP asociado al punto de conexión es un método HTTP pertinente. Los métodos pertinentes son todos los métodos HTTP, excepto TRACE, OPTIONS, HEAD y GET.
  • La solicitud está asociada a un punto de conexión válido.

Nota: cuando se habilita manualmente, el middleware de antifalsificación debe ejecutarse después del middleware de autenticación y autorización para evitar la lectura de datos del formulario cuando el usuario no está autenticado.

De forma predeterminada, las API mínimas que aceptan datos de formulario requieren validación de tokens de antifalsificación.

Observe el método GenerateForm siguiente:

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

El código anterior tiene tres argumentos, la acción, el token de antifalsificación y un bool que indica si se debe usar el token.

Observe el ejemplo siguiente:

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

En el código anterior se envía a:

  • /todo requerir un token de antifalsificación válido.
  • /todo2no requieren un token de antifalsificación válido porque se llama a DisableAntiforgery.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

Una MENSAJE para:

  • /todo del formulario generado por el punto de conexión / se realiza correctamente porque el token de antifalsificación es válido.
  • /todo del formulario generado por el error /SkipToken porque no se incluye la antifalsificación.
  • /todo2 del formulario generado por el punto de conexión /DisableAntiforgery se realiza correctamente porque no se requiere la antifalsificación.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

Cuando se envía un formulario sin un token de antifalsificación válido:

  • En el entorno de desarrollo, se produce una excepción.
  • En el entorno de producción, se registra un mensaje.

cookies de autenticación de Windows y antifalsificación

Al usar la autenticación de Windows, los puntos de conexión de la aplicación deben protegerse frente a ataques de CSRF de la misma manera que se hace para las cookies. El explorador envía implícitamente el contexto de autenticación al servidor y los puntos de conexión deben protegerse frente a ataques de CSRF.

Extensión de la antifalsificación

El tipo IAntiforgeryAdditionalDataProvider permite a los desarrolladores ampliar el comportamiento del sistema anti-CSRF mediante el recorrido de ida y vuelta de datos adicionales en cada token. Se llama al método GetAdditionalData cada vez que se genera un token de campo; el valor devuelto se inserta dentro del token generado. Un implementador podría devolver una marca de tiempo, un valor nonce o cualquier otro valor y, a continuación, llamar a ValidateAdditionalData para validar estos datos cuando se valida el token. El nombre de usuario del cliente ya está insertado en los tokens generados, por lo que no es necesario incluir esta información. Si un token incluye datos complementarios pero no se configura un IAntiForgeryAdditionalDataProvider, los datos complementarios no se validan.

Recursos adicionales

La falsificación de solicitud entre sitios (también conocida como XSRF o CSRF) es un ataque contra aplicaciones hospedadas en web, en el que una aplicación web malintencionada puede influir en la interacción entre un explorador cliente y una aplicación web que confía en ese explorador. Estos ataques son posibles porque los exploradores web envían automáticamente algunos tipos de token de autenticación con cada solicitud en un sitio web. Esta forma de vulnerabilidad de seguridad también se conoce como ataque con un clic o montaje en la sesión porque el ataque aprovecha la sesión del usuario autenticada anteriormente.

Ejemplo de un ataque de CSRF:

  1. Un usuario inicia sesión en www.good-banking-site.example.com con la autenticación de formularios. El servidor autentica al usuario y emite una respuesta que incluye una autenticación cookie. El sitio es vulnerable a ataques porque confía en cualquier solicitud que reciba con una cookie de autenticación válida.

  2. El usuario visita un sitio malintencionado, www.bad-crook-site.example.com.

    El sitio malintencionado, www.bad-crook-site.example.com, contiene un formulario HTML similar al ejemplo siguiente:

    <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 el action del formulario publica en el sitio vulnerable, no en el sitio malintencionado. Esta es la parte "entre sitios" de CSRF.

  3. El usuario selecciona el botón Enviar. El explorador realiza la solicitud e incluye automáticamente la cookie de autenticación para el dominio solicitado, www.good-banking-site.example.com.

  4. La solicitud se ejecuta en el servidor de www.good-banking-site.example.com con el contexto de autenticación del usuario y puede realizar cualquier acción que un usuario autenticado pueda realizar.

Además del escenario en el que el usuario selecciona el botón para enviar el formulario, el sitio malintencionado podría:

  • Ejecutar un script que envíe automáticamente el formulario.
  • Enviar el envío del formulario como una solicitud AJAX.
  • Ocultar el formulario mediante CSS.

Estos escenarios alternativos no requieren ninguna acción ni entrada por parte del usuario aparte de visitar inicialmente el sitio malintencionado.

El uso de HTTPS no impide un ataque de CSRF. El sitio malintencionado puede enviar una solicitud https://www.good-banking-site.com/ con la misma facilidad con la que puede enviar una solicitud no segura.

Algunos ataques se dirigen a puntos de conexión que responden a solicitudes GET, en cuyo caso se puede usar una etiqueta de imagen para realizar la acción. Esta forma de ataque es común en sitios de foros que permiten imágenes pero bloquean JavaScript. Las aplicaciones que cambian el estado de las solicitudes GET, en las que se modifican variables o recursos, son vulnerables a ataques malintencionados. Las solicitudes GET que cambian de estado no son seguras. Un procedimiento recomendado es no cambiar nunca el estado en una solicitud GET.

Los ataques de CSRF se pueden producir en las aplicaciones web que usan cookies para la autenticación porque:

  • Los exploradores almacenan cookies emitidas por una aplicación web.
  • Las cookies almacenadas incluyen cookies de sesión para los usuarios autenticados.
  • Los exploradores envían todas las cookies asociadas con un dominio a la aplicación web cada solicitud independientemente de cómo se generó la solicitud a la aplicación en el explorador.

Sin embargo, los ataques de CSRF no se limitan a aprovechar las vulnerabilidades de las cookies. Por ejemplo, la autenticación básica e implícita también son vulnerables. Una vez que un usuario inicia sesión con la autenticación básica o implícita, el explorador envía automáticamente las credenciales hasta que finaliza la sesión.

En este contexto, la sesión hace referencia a la sesión del lado cliente durante la cual se autentica el usuario. No está relacionado con las sesiones del lado servidor ni el middleware de sesión de ASP.NET Core.

Para protegerse contra las vulnerabilidades de CSRF, los usuarios pueden tomar precauciones:

  • Cierre la sesión de las aplicaciones web cuando termine de usarlas.
  • Borre periódicamente las cookies del explorador.

Sin embargo, las vulnerabilidades de CSRF son fundamentalmente un problema con la aplicación web, no con el usuario final.

Aspectos básicos de la autenticación

La autenticación basada en Cookies es una forma popular de autenticación. Los sistemas de autenticación basados en tokens están creciendo en popularidad, especialmente para aplicaciones de página única (SPA).

Cuando un usuario se autentica con su nombre de usuario y contraseña, se emite un token que contiene un vale de autenticación que se puede usar para la autenticación y la autorización. El token se almacena como una cookie que se envía con cada solicitud que realiza el cliente. El middleware de autenticación de Cookies realiza la generación y validación de esta cookie. El middleware serializa una entidad de seguridad de usuario en una cookie cifrada. En las solicitudes posteriores, el middleware valida la cookie, vuelve a crear la entidad de seguridad y asigna la entidad de seguridad a la propiedad HttpContext.User.

Autenticación basada en tokens

Cuando se autentica un usuario, se emite un token (no un token de antifalsificación). El token contiene información de usuario en forma de notificaciones o un token de referencia que apunta la aplicación al estado de usuario mantenido en la aplicación. Cuando un usuario intenta acceder a un recurso que requiere autenticación, el token se envía a la aplicación con un encabezado de autorización adicional en forma de token de portador. Este enfoque hace que la aplicación no tenga estado. En cada solicitud posterior, el token se pasa en la solicitud de validación del lado servidor. Este token no está cifrado; está codificado. En el servidor, el token se descodifica para acceder a su información. Para enviar el token en solicitudes posteriores, almacene el token en el almacenamiento local del explorador. Colocar un token en el almacenamiento local del explorador, recuperarlo y usarlo como token de portador proporciona protección contra ataques de CSRF. Sin embargo, si la aplicación es vulnerable a la inserción de scripts a través de XSS o un archivo javascript externo en peligro, un atacante podría recuperar cualquier valor del almacenamiento local y enviárselo a sí mismo. ASP.NET Core codifica todas las salidas del lado servidor de las variables de forma predeterminada, lo que reduce el riesgo de XSS. Si invalida este comportamiento mediante Html.Raw o código personalizado con entradas que no son de confianza, puede aumentar el riesgo de XSS.

No se preocupe por la vulnerabilidad de CSRF si el token se almacena en el almacenamiento local del explorador. La CSRF es un problema cuando el token se almacena en una cookie. Para obtener más información, consulte el problema de GitHub El ejemplo de código SPA agrega dos cookies.

Varias aplicaciones hospedadas en un dominio

Los entornos de hospedaje compartidos son vulnerables al secuestro de sesión, a la CSRF de inicio de sesión y a otros ataques.

Aunque example1.contoso.net y example2.contoso.net son hosts diferentes, hay una relación de confianza implícita entre los hosts bajo el dominio *.contoso.net. Esta relación de confianza implícita permite a los hosts que podrían no ser de confianza afectar a las cookies de otros hosts (las directivas de mismo origen que rigen las solicitudes de AJAX no se aplican necesariamente a las cookies HTTP).

Los ataques que aprovechan las cookies de confianza entre aplicaciones hospedadas en el mismo dominio pueden evitarse si no se comparten dominios. Cuando cada aplicación se hospeda en su propio dominio, no hay ninguna relación de confianza implícita de cookies que se pueda aprovechar.

Antifalsificación en ASP.NET Core

Advertencia

ASP.NET Core implementa la antifalsificación mediante la Protección de datos de ASP.NET Core. La pila de protección de datos debe configurarse para que funcione en una granja de servidores. Para obtener más información, consulte Configuración de la protección de datos.

El middleware de antifalsificación se agrega al contenedor de inserción de dependencias cuando se llama a una de las siguientes API en Program.cs:

El elemento FormTagHelper inserta tokens antifalsificación en los elementos de formulario HTML. El siguiente marcado en un archivo de Razor genera automáticamente tokens de antifalsificación:

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

De forma similar, IHtmlHelper.BeginForm genera tokens antifalsificación de forma predeterminada si el método del formulario no es GET.

La generación automática de tokens de antifalsificación para elementos de formulario HTML se produce cuando la etiqueta <form> contiene el atributo method="post" y se cumple alguna de las siguientes condiciones:

  • El atributo action está vacío (action="").
  • El atributo action no se proporciona (<form method="post">).

La generación automática de tokens de antifalsificación para elementos de formulario HTML se puede deshabilitar:

  • Deshabilite explícitamente tokens de antifalsificación con el atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • El elemento de formulario se ha excluido de los asistentes de etiquetas mediante el símbolo de rechazo ! del asistente de etiquetas:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Quite FormTagHelper de la vista. FormTagHelper se puede quitar de una vista agregando la siguiente directiva a la vista de Razor:

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

Nota:

Razor Pages se protegen automáticamente de XSRF/CSRF. Para obtener más información, vea XSRF/CSRF y Razor Pages.

El enfoque más común para defenderse contra ataques de CSRF es usar el patrón de token del sincronizador (STP). El STP se usa cuando el usuario solicita una página con datos de formulario:

  1. El servidor envía un token asociado a la identidad del usuario actual al cliente.
  2. El cliente devuelve el token al servidor para su comprobación.
  3. Si el servidor recibe un token que no coincide con la identidad del usuario autenticado, se rechaza la solicitud.

El token es único e imprevisible. El token también se puede usar para garantizar la secuenciación correcta de una serie de solicitudes (por ejemplo, garantizar la secuencia de solicitudes de: página 1 > página 2 > página 3). Todos los formularios de las plantillas de Razor Pages y de ASP.NET Core MVC generan tokens de antifalsificación. El siguiente par de ejemplos de vista genera tokens de antifalsificación:

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

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

Agregue explícitamente un token de antifalsificación a un elemento <form> sin usar asistentes de etiquetas con el asistente de HTML @Html.AntiForgeryToken:

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

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

En cada uno de los casos anteriores, ASP.NET Core agrega un campo de formulario oculto similar al ejemplo siguiente:

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

ASP.NET Core incluye tres filtros para trabajar con tokens de antifalsificación:

Antifraude con AddControllers

La llamada a AddControllersno habilita tokens de antifalsificación. Se debe llamar a AddControllersWithViews para tener compatibilidad integrada con tokens de antifalsificación.

Varias pestañas del explorador y el patrón de token del sincronizador

Con el patrón de token del sincronizador, solo la página cargada más recientemente contiene un token de antifalsificación válido. El uso de varias pestañas puede ser problemático. Por ejemplo, si un usuario abre varias pestañas:

  • Solo la pestaña cargada más recientemente contiene un token de antifalsificación válido.
  • Las solicitudes realizadas desde pestañas cargadas anteriormente producen un error: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Considere la posibilidad de usar patrones alternativos de protección contra CSRF si esto plantea un problema.

Configuración de la antifalsificación con AntiforgeryOptions

Personalización de AntiforgeryOptions en Program.cs:

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

Establezca las propiedades de la Cookie de antifalsificación mediante las propiedades de la clase CookieBuilder, como se muestra en la tabla siguiente.

Opción Descripción
Cookie Determina la configuración usada para crear la cookie de antifalsificación.
FormFieldName Nombre del campo de formulario oculto utilizado por el sistema de antifalsificación para representar tokens de antifalsificación en vistas.
HeaderName Nombre del encabezado utilizado por el sistema de antifalsificación. Si es null, el sistema solo tiene en cuenta los datos del formulario.
SuppressXFrameOptionsHeader Especifica si se va a suprimir la generación del encabezado X-Frame-Options. De forma predeterminada, el encabezado se genera con un valor de "SAMEORIGIN". Tiene como valor predeterminado false.

Para obtener más información, vea CookieAuthenticationOptions.

Generación de tokens de antifalsificación con IAntiforgery

IAntiforgeryproporciona la API para configurar características de antifalsificación. IAntiforgery se puede solicitar en Program.cs mediante WebApplication.Services. En el ejemplo siguiente se usa middleware de la página principal de la aplicación para generar un token de antifalsificación y enviarlo en la respuesta como 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);
});

En el ejemplo anterior se establece una cookie denominada XSRF-TOKEN. El cliente puede leer esta cookie y proporcionar su valor como un encabezado adjunto a las solicitudes de AJAX. Por ejemplo, Angular incluye protección integrada contra XSRF que lee una cookie denominada XSRF-TOKEN de forma predeterminada.

Requerir validación de antifalsificación

El filtro de acción ValidateAntiForgeryToken se puede aplicar a una acción individual, un controlador o globalmente. Las solicitudes realizadas a las acciones que tienen aplicado este filtro se bloquean a menos que la solicitud incluya un token de antifalsificación válido:

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

    return RedirectToAction();
}

El atributo ValidateAntiForgeryToken requiere un token para las solicitudes a los métodos de acción que marca, incluidas las solicitudes HTTP GET. Si el atributo ValidateAntiForgeryToken se aplica en los controladores de la aplicación, se puede invalidar con el atributo IgnoreAntiforgeryToken.

Validar automáticamente tokens de antifalsificación solo para métodos HTTP no seguros

En lugar de aplicar ampliamente el atributo ValidateAntiForgeryToken y, a continuación, reemplazarlo con atributos IgnoreAntiforgeryToken, se puede usar el atributo AutoValidateAntiforgeryToken. Este atributo funciona de forma idéntica al atributo ValidateAntiForgeryToken, salvo que no requiere tokens para las solicitudes realizadas mediante los siguientes métodos HTTP:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

Se recomienda usar ampliamente AutoValidateAntiforgeryToken para escenarios que no son de API. Este atributo garantiza que las acciones POST estén protegidas de forma predeterminada. La alternativa es omitir los tokens de antifalsificación de forma predeterminada, a menos que ValidateAntiForgeryToken se aplique a métodos de acción individuales. Es más probable que en este escenario un método de acción POST se deje desprotegido por error, lo que hace que la aplicación sea vulnerable a los ataques de CSRF. Todos los POST deben enviar el token de antifalsificación.

Las API no tienen un mecanismo automático para enviar la parte del token que no es una cookie. La implementación probablemente depende de la implementación del código de cliente. A continuación se muestran algunos ejemplos:

Ejemplo de nivel de clase:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Ejemplo global:

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

Invalidación de atributos de antifalsificación globales o de controlador

El filtro IgnoreAntiforgeryToken se usa para evitar tener que usar un token de antifalsificación para una acción determinada (o controlador). Cuando se aplica, este filtro invalida los filtros ValidateAntiForgeryToken y AutoValidateAntiforgeryToken especificados en un nivel superior (global o en un controlador).

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

    return RedirectToAction();
}

Actualización de tokens después de la autenticación

Los tokens se deben actualizar después de que el usuario se autentique al redirigir al usuario a una vista o página de Razor Pages.

JavaScript, AJAX y SPA

En las aplicaciones tradicionales basadas en HTML, los tokens de antifalsificación se pasan al servidor mediante campos de formulario ocultos. En las aplicaciones y SPA modernas basadas en JavaScript, muchas solicitudes se realizan mediante programación. Estas solicitudes de AJAX pueden usar otras técnicas (como encabezados de solicitud o cookies) para enviar el token.

Si se usan cookies para almacenar tokens de autenticación y para autenticar solicitudes de API en el servidor, la CSRF es un posible problema. Si el almacenamiento local se usa para almacenar el token, se podría mitigar la vulnerabilidad de CSRF porque los valores del almacenamiento local no se envían automáticamente al servidor con cada solicitud. El uso del almacenamiento local para almacenar el token de antifalsificación en el cliente y enviar el token como encabezado de solicitud es un enfoque recomendado.

JavaScript

Al usar JavaScript con vistas, el token se puede crear mediante un servicio desde dentro de la vista. Inserte el servicio IAntiforgery en la vista y llame a 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>
}

En el ejemplo anterior se usa JavaScript para leer el valor de campo oculto del encabezado POST de AJAX.

Este enfoque evita tener que tratar directamente con la configuración de cookies del servidor o leerlas del cliente. Sin embargo, si no es posible insertar el servicio IAntiforgery, use JavaScript para acceder a tokens en cookies:

  • Acceda a tokens en una solicitud adicional al servidor, normalmente same-origin.
  • Use el contenido de la cookie para crear un encabezado con el valor del token.

Suponiendo que el script envía el token en un encabezado de solicitud denominado X-XSRF-TOKEN, configure el servicio de antifalsificación para buscar el encabezado X-XSRF-TOKEN:

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

En el ejemplo siguiente se agrega un punto de conexión protegido que escribe el token de solicitud en una cookie legible en 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();

En el ejemplo siguiente se usa JavaScript para realizar una solicitud de AJAX para obtener el token y realizar otra solicitud con el encabezado adecuado:

var response = await fetch("/antiforgery/token", {
    method: "GET",
    headers: { "Authorization": authorizationToken }
});

if (response.ok) {
    // https://developer.mozilla.org/docs/web/api/document/cookie
    const xsrfToken = document.cookie
        .split("; ")
        .find(row => row.startsWith("XSRF-TOKEN="))
        .split("=")[1];

    response = await fetch("/JavaScript/FetchEndpoint", {
        method: "POST",
        headers: { "X-XSRF-TOKEN": xsrfToken, "Authorization": authorizationToken }
    });

    if (response.ok) {
        resultElement.innerText = await response.text();
    } else {
        resultElement.innerText = `Request Failed: ${response.status}`
    }
} else {    
    resultElement.innerText = `Request Failed: ${response.status}`
}

Nota:

Cuando el token antifalsificación se proporciona tanto en la cabecera de la solicitud como en la carga útil del formulario, sólo se valida el token de la cabecera.

Antifalsificación con API mínimas

Las Minimal APIs no admiten el uso de los filtros incluidos (ValidateAntiForgeryToken, AutoValidateAntiforgeryToken, IgnoreAntiforgeryToken); sin embargo, IAntiforgery proporciona las API necesarias para validar una solicitud.

En el ejemplo siguiente se crea un filtro que valida el token de antifalsificación:

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

        });
    }
}

A continuación, el filtro se puede aplicar a un punto de conexión:

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

cookies de autenticación de Windows y antifalsificación

Al usar la autenticación de Windows, los puntos de conexión de la aplicación deben protegerse frente a ataques de CSRF de la misma manera que se hace para las cookies. El explorador envía implícitamente el contexto de autenticación al servidor y los puntos de conexión deben protegerse frente a ataques de CSRF.

Extensión de la antifalsificación

El tipo IAntiforgeryAdditionalDataProvider permite a los desarrolladores ampliar el comportamiento del sistema anti-CSRF mediante el recorrido de ida y vuelta de datos adicionales en cada token. Se llama al método GetAdditionalData cada vez que se genera un token de campo; el valor devuelto se inserta dentro del token generado. Un implementador podría devolver una marca de tiempo, un valor nonce o cualquier otro valor y, a continuación, llamar a ValidateAdditionalData para validar estos datos cuando se valida el token. El nombre de usuario del cliente ya está insertado en los tokens generados, por lo que no es necesario incluir esta información. Si un token incluye datos complementarios pero no se configura un IAntiForgeryAdditionalDataProvider, los datos complementarios no se validan.

Recursos adicionales

La falsificación de solicitud entre sitios (también conocida como XSRF o CSRF) es un ataque contra aplicaciones hospedadas en web, en el que una aplicación web malintencionada puede influir en la interacción entre un explorador cliente y una aplicación web que confía en ese explorador. Estos ataques son posibles porque los exploradores web envían automáticamente algunos tipos de token de autenticación con cada solicitud en un sitio web. Esta forma de vulnerabilidad de seguridad también se conoce como ataque con un clic o montaje en la sesión porque el ataque aprovecha la sesión del usuario autenticada anteriormente.

Ejemplo de un ataque de CSRF:

  1. Un usuario inicia sesión en www.good-banking-site.example.com con la autenticación de formularios. El servidor autentica al usuario y emite una respuesta que incluye una autenticación cookie. El sitio es vulnerable a ataques porque confía en cualquier solicitud que reciba con una cookie de autenticación válida.

  2. El usuario visita un sitio malintencionado, www.bad-crook-site.example.com.

    El sitio malintencionado, www.bad-crook-site.example.com, contiene un formulario HTML similar al ejemplo siguiente:

    <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 el action del formulario publica en el sitio vulnerable, no en el sitio malintencionado. Esta es la parte "entre sitios" de CSRF.

  3. El usuario selecciona el botón Enviar. El explorador realiza la solicitud e incluye automáticamente la cookie de autenticación para el dominio solicitado, www.good-banking-site.example.com.

  4. La solicitud se ejecuta en el servidor de www.good-banking-site.example.com con el contexto de autenticación del usuario y puede realizar cualquier acción que un usuario autenticado pueda realizar.

Además del escenario en el que el usuario selecciona el botón para enviar el formulario, el sitio malintencionado podría:

  • Ejecutar un script que envíe automáticamente el formulario.
  • Enviar el envío del formulario como una solicitud AJAX.
  • Ocultar el formulario mediante CSS.

Estos escenarios alternativos no requieren ninguna acción ni entrada por parte del usuario aparte de visitar inicialmente el sitio malintencionado.

El uso de HTTPS no impide un ataque de CSRF. El sitio malintencionado puede enviar una solicitud https://www.good-banking-site.com/ con la misma facilidad con la que puede enviar una solicitud no segura.

Algunos ataques se dirigen a puntos de conexión que responden a solicitudes GET, en cuyo caso se puede usar una etiqueta de imagen para realizar la acción. Esta forma de ataque es común en sitios de foros que permiten imágenes pero bloquean JavaScript. Las aplicaciones que cambian el estado de las solicitudes GET, en las que se modifican variables o recursos, son vulnerables a ataques malintencionados. Las solicitudes GET que cambian de estado no son seguras. Un procedimiento recomendado es no cambiar nunca el estado en una solicitud GET.

Los ataques de CSRF se pueden producir en las aplicaciones web que usan cookies para la autenticación porque:

  • Los exploradores almacenan cookies emitidas por una aplicación web.
  • Las cookies almacenadas incluyen cookies de sesión para los usuarios autenticados.
  • Los exploradores envían todas las cookies asociadas con un dominio a la aplicación web cada solicitud independientemente de cómo se generó la solicitud a la aplicación en el explorador.

Sin embargo, los ataques de CSRF no se limitan a aprovechar las vulnerabilidades de las cookies. Por ejemplo, la autenticación básica e implícita también son vulnerables. Una vez que un usuario inicia sesión con la autenticación básica o implícita, el explorador envía automáticamente las credenciales hasta que finaliza la sesión.

En este contexto, la sesión hace referencia a la sesión del lado cliente durante la cual se autentica el usuario. No está relacionado con las sesiones del lado servidor ni el middleware de sesión de ASP.NET Core.

Para protegerse contra las vulnerabilidades de CSRF, los usuarios pueden tomar precauciones:

  • Cierre la sesión de las aplicaciones web cuando termine de usarlas.
  • Borre periódicamente las cookies del explorador.

Sin embargo, las vulnerabilidades de CSRF son fundamentalmente un problema con la aplicación web, no con el usuario final.

Aspectos básicos de la autenticación

La autenticación basada en Cookies es una forma popular de autenticación. Los sistemas de autenticación basados en tokens están creciendo en popularidad, especialmente para aplicaciones de página única (SPA).

Cuando un usuario se autentica con su nombre de usuario y contraseña, se emite un token que contiene un vale de autenticación que se puede usar para la autenticación y la autorización. El token se almacena como una cookie que se envía con cada solicitud que realiza el cliente. El middleware de autenticación de Cookies realiza la generación y validación de esta cookie. El middleware serializa una entidad de seguridad de usuario en una cookie cifrada. En las solicitudes posteriores, el middleware valida la cookie, vuelve a crear la entidad de seguridad y asigna la entidad de seguridad a la propiedad HttpContext.User.

Autenticación basada en tokens

Cuando se autentica un usuario, se emite un token (no un token de antifalsificación). El token contiene información de usuario en forma de notificaciones o un token de referencia que apunta la aplicación al estado de usuario mantenido en la aplicación. Cuando un usuario intenta acceder a un recurso que requiere autenticación, el token se envía a la aplicación con un encabezado de autorización adicional en forma de token de portador. Este enfoque hace que la aplicación no tenga estado. En cada solicitud posterior, el token se pasa en la solicitud de validación del lado servidor. Este token no está cifrado; está codificado. En el servidor, el token se descodifica para acceder a su información. Para enviar el token en solicitudes posteriores, almacene el token en el almacenamiento local del explorador. No se preocupe por la vulnerabilidad de CSRF si el token se almacena en el almacenamiento local del explorador. La CSRF es un problema cuando el token se almacena en una cookie. Para obtener más información, consulte el problema de GitHub El ejemplo de código SPA agrega dos cookies.

Varias aplicaciones hospedadas en un dominio

Los entornos de hospedaje compartidos son vulnerables al secuestro de sesión, a la CSRF de inicio de sesión y a otros ataques.

Aunque example1.contoso.net y example2.contoso.net son hosts diferentes, hay una relación de confianza implícita entre los hosts bajo el dominio *.contoso.net. Esta relación de confianza implícita permite a los hosts que podrían no ser de confianza afectar a las cookies de otros hosts (las directivas de mismo origen que rigen las solicitudes de AJAX no se aplican necesariamente a las cookies HTTP).

Los ataques que aprovechan las cookies de confianza entre aplicaciones hospedadas en el mismo dominio pueden evitarse si no se comparten dominios. Cuando cada aplicación se hospeda en su propio dominio, no hay ninguna relación de confianza implícita de cookies que se pueda aprovechar.

Antifalsificación en ASP.NET Core

Advertencia

ASP.NET Core implementa la antifalsificación mediante la Protección de datos de ASP.NET Core. La pila de protección de datos debe configurarse para que funcione en una granja de servidores. Para obtener más información, consulte Configuración de la protección de datos.

El middleware de antifalsificación se agrega al contenedor de inserción de dependencias cuando se llama a una de las siguientes API en Program.cs:

El elemento FormTagHelper inserta tokens antifalsificación en los elementos de formulario HTML. El siguiente marcado en un archivo de Razor genera automáticamente tokens de antifalsificación:

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

De forma similar, IHtmlHelper.BeginForm genera tokens antifalsificación de forma predeterminada si el método del formulario no es GET.

La generación automática de tokens de antifalsificación para elementos de formulario HTML se produce cuando la etiqueta <form> contiene el atributo method="post" y se cumple alguna de las siguientes condiciones:

  • El atributo action está vacío (action="").
  • El atributo action no se proporciona (<form method="post">).

La generación automática de tokens de antifalsificación para elementos de formulario HTML se puede deshabilitar:

  • Deshabilite explícitamente tokens de antifalsificación con el atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • El elemento de formulario se ha excluido de los asistentes de etiquetas mediante el símbolo de rechazo ! del asistente de etiquetas:

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Quite FormTagHelper de la vista. FormTagHelper se puede quitar de una vista agregando la siguiente directiva a la vista de Razor:

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

Nota:

Razor Pages se protegen automáticamente de XSRF/CSRF. Para obtener más información, vea XSRF/CSRF y Razor Pages.

El enfoque más común para defenderse contra ataques de CSRF es usar el patrón de token del sincronizador (STP). El STP se usa cuando el usuario solicita una página con datos de formulario:

  1. El servidor envía un token asociado a la identidad del usuario actual al cliente.
  2. El cliente devuelve el token al servidor para su comprobación.
  3. Si el servidor recibe un token que no coincide con la identidad del usuario autenticado, se rechaza la solicitud.

El token es único e imprevisible. El token también se puede usar para garantizar la secuenciación correcta de una serie de solicitudes (por ejemplo, garantizar la secuencia de solicitudes de: página 1 > página 2 > página 3). Todos los formularios de las plantillas de Razor Pages y de ASP.NET Core MVC generan tokens de antifalsificación. El siguiente par de ejemplos de vista genera tokens de antifalsificación:

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

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

Agregue explícitamente un token de antifalsificación a un elemento <form> sin usar asistentes de etiquetas con el asistente de HTML @Html.AntiForgeryToken:

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

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

En cada uno de los casos anteriores, ASP.NET Core agrega un campo de formulario oculto similar al ejemplo siguiente:

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

ASP.NET Core incluye tres filtros para trabajar con tokens de antifalsificación:

Antifalsificación con AddControllers

La llamada a AddControllersno habilita tokens de antifalsificación. Se debe llamar a AddControllersWithViews para tener compatibilidad integrada con tokens de antifalsificación.

Varias pestañas del explorador y el patrón de token del sincronizador

Con el patrón de token del sincronizador, solo la página cargada más recientemente contiene un token de antifalsificación válido. El uso de varias pestañas puede ser problemático. Por ejemplo, si un usuario abre varias pestañas:

  • Solo la pestaña cargada más recientemente contiene un token de antifalsificación válido.
  • Las solicitudes realizadas desde pestañas cargadas anteriormente producen un error: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Considere la posibilidad de usar patrones alternativos de protección contra CSRF si esto plantea un problema.

Configuración de la antifalsificación con AntiforgeryOptions

Personalización de AntiforgeryOptions en Program.cs:

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

Establezca las propiedades de la Cookie de antifalsificación mediante las propiedades de la clase CookieBuilder, como se muestra en la tabla siguiente.

Opción Descripción
Cookie Determina la configuración usada para crear la cookie de antifalsificación.
FormFieldName Nombre del campo de formulario oculto utilizado por el sistema de antifalsificación para representar tokens de antifalsificación en vistas.
HeaderName Nombre del encabezado utilizado por el sistema de antifalsificación. Si es null, el sistema solo tiene en cuenta los datos del formulario.
SuppressXFrameOptionsHeader Especifica si se va a suprimir la generación del encabezado X-Frame-Options. De forma predeterminada, el encabezado se genera con un valor de "SAMEORIGIN". Tiene como valor predeterminado false.

Para obtener más información, vea CookieAuthenticationOptions.

Generación de tokens de antifalsificación con IAntiforgery

IAntiforgeryproporciona la API para configurar características de antifalsificación. IAntiforgery se puede solicitar en Program.cs mediante WebApplication.Services. En el ejemplo siguiente se usa middleware de la página principal de la aplicación para generar un token de antifalsificación y enviarlo en la respuesta como 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);
});

En el ejemplo anterior se establece una cookie denominada XSRF-TOKEN. El cliente puede leer esta cookie y proporcionar su valor como un encabezado adjunto a las solicitudes de AJAX. Por ejemplo, Angular incluye protección integrada contra XSRF que lee una cookie denominada XSRF-TOKEN de forma predeterminada.

Requerir validación de antifalsificación

El filtro de acción ValidateAntiForgeryToken se puede aplicar a una acción individual, un controlador o globalmente. Las solicitudes realizadas a las acciones que tienen aplicado este filtro se bloquean a menos que la solicitud incluya un token de antifalsificación válido:

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

    return RedirectToAction();
}

El atributo ValidateAntiForgeryToken requiere un token para las solicitudes a los métodos de acción que marca, incluidas las solicitudes HTTP GET. Si el atributo ValidateAntiForgeryToken se aplica en los controladores de la aplicación, se puede invalidar con el atributo IgnoreAntiforgeryToken.

Validar automáticamente tokens de antifalsificación solo para métodos HTTP no seguros

En lugar de aplicar ampliamente el atributo ValidateAntiForgeryToken y, a continuación, reemplazarlo con atributos IgnoreAntiforgeryToken, se puede usar el atributo AutoValidateAntiforgeryToken. Este atributo funciona de forma idéntica al atributo ValidateAntiForgeryToken, salvo que no requiere tokens para las solicitudes realizadas mediante los siguientes métodos HTTP:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

Se recomienda usar ampliamente AutoValidateAntiforgeryToken para escenarios que no son de API. Este atributo garantiza que las acciones POST estén protegidas de forma predeterminada. La alternativa es omitir los tokens de antifalsificación de forma predeterminada, a menos que ValidateAntiForgeryToken se aplique a métodos de acción individuales. Es más probable que en este escenario un método de acción POST se deje desprotegido por error, lo que hace que la aplicación sea vulnerable a los ataques de CSRF. Todos los POST deben enviar el token de antifalsificación.

Las API no tienen un mecanismo automático para enviar la parte del token que no es una cookie. La implementación probablemente depende de la implementación del código de cliente. A continuación se muestran algunos ejemplos:

Ejemplo de nivel de clase:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Ejemplo global:

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

Invalidación de atributos de antifalsificación globales o de controlador

El filtro IgnoreAntiforgeryToken se usa para evitar tener que usar un token de antifalsificación para una acción determinada (o controlador). Cuando se aplica, este filtro invalida los filtros ValidateAntiForgeryToken y AutoValidateAntiforgeryToken especificados en un nivel superior (global o en un controlador).

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

    return RedirectToAction();
}

Actualización de tokens después de la autenticación

Los tokens se deben actualizar después de que el usuario se autentique al redirigir al usuario a una vista o página de Razor Pages.

JavaScript, AJAX y SPA

En las aplicaciones tradicionales basadas en HTML, los tokens de antifalsificación se pasan al servidor mediante campos de formulario ocultos. En las aplicaciones y SPA modernas basadas en JavaScript, muchas solicitudes se realizan mediante programación. Estas solicitudes de AJAX pueden usar otras técnicas (como encabezados de solicitud o cookies) para enviar el token.

Si se usan cookies para almacenar tokens de autenticación y para autenticar solicitudes de API en el servidor, la CSRF es un posible problema. Si el almacenamiento local se usa para almacenar el token, se podría mitigar la vulnerabilidad de CSRF porque los valores del almacenamiento local no se envían automáticamente al servidor con cada solicitud. El uso del almacenamiento local para almacenar el token de antifalsificación en el cliente y enviar el token como encabezado de solicitud es un enfoque recomendado.

JavaScript

Al usar JavaScript con vistas, el token se puede crear mediante un servicio desde dentro de la vista. Inserte el servicio IAntiforgery en la vista y llame a 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>
}

En el ejemplo anterior se usa JavaScript para leer el valor de campo oculto del encabezado POST de AJAX.

Este enfoque evita tener que tratar directamente con la configuración de cookies del servidor o leerlas del cliente. Sin embargo, si no es posible insertar el servicio IAntiforgery, JavaScript también puede acceder al token en cookies, obtenido de una solicitud adicional al servidor (normalmente same-origin), y usar el contenido de la cookie para crear un encabezado con el valor del token.

Suponiendo que el script envía el token en un encabezado de solicitud denominado X-XSRF-TOKEN, configure el servicio de antifalsificación para buscar el encabezado X-XSRF-TOKEN:

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

En el ejemplo siguiente se agrega un punto de conexión protegido que escribirá el token de solicitud en una cookie legible en 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();

En el ejemplo siguiente se usa JavaScript para realizar una solicitud de AJAX para obtener el token y realizar otra solicitud con el encabezado adecuado:

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 autenticación de Windows y antifalsificación

Al usar la autenticación de Windows, los puntos de conexión de la aplicación deben protegerse frente a ataques de CSRF de la misma manera que se hace para las cookies. El explorador envía implícitamente el contexto de autenticación al servidor y, por tanto, los puntos de conexión deben protegerse frente a ataques de CSRF.

Extensión de la antifalsificación

El tipo IAntiforgeryAdditionalDataProvider permite a los desarrolladores ampliar el comportamiento del sistema anti-CSRF mediante el recorrido de ida y vuelta de datos adicionales en cada token. Se llama al método GetAdditionalData cada vez que se genera un token de campo; el valor devuelto se inserta dentro del token generado. Un implementador podría devolver una marca de tiempo, un valor nonce o cualquier otro valor y, a continuación, llamar a ValidateAdditionalData para validar estos datos cuando se valida el token. El nombre de usuario del cliente ya está insertado en los tokens generados, por lo que no es necesario incluir esta información. Si un token incluye datos complementarios pero no se configura un IAntiForgeryAdditionalDataProvider, los datos complementarios no se validan.

Recursos adicionales

La falsificación de solicitud entre sitios (también conocida como XSRF o CSRF) es un ataque contra aplicaciones hospedadas en web, en el que una aplicación web malintencionada puede influir en la interacción entre un explorador cliente y una aplicación web que confía en ese explorador. Estos ataques son posibles porque los exploradores web envían automáticamente algunos tipos de token de autenticación con cada solicitud en un sitio web. Esta forma de vulnerabilidad de seguridad también se conoce como ataque con un clic o montaje en la sesión porque el ataque aprovecha la sesión del usuario autenticada anteriormente.

Ejemplo de un ataque de CSRF:

  1. Un usuario inicia sesión en www.good-banking-site.example.com con la autenticación de formularios. El servidor autentica al usuario y emite una respuesta que incluye una autenticación cookie. El sitio es vulnerable a ataques porque confía en cualquier solicitud que reciba con una cookie de autenticación válida.

  2. El usuario visita un sitio malintencionado, www.bad-crook-site.example.com.

    El sitio malintencionado, www.bad-crook-site.example.com, contiene un formulario HTML similar al ejemplo siguiente:

    <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 el action del formulario publica en el sitio vulnerable, no en el sitio malintencionado. Esta es la parte "entre sitios" de CSRF.

  3. El usuario selecciona el botón Enviar. El explorador realiza la solicitud e incluye automáticamente la cookie de autenticación para el dominio solicitado, www.good-banking-site.example.com.

  4. La solicitud se ejecuta en el servidor de www.good-banking-site.example.com con el contexto de autenticación del usuario y puede realizar cualquier acción que un usuario autenticado pueda realizar.

Además del escenario en el que el usuario selecciona el botón para enviar el formulario, el sitio malintencionado podría:

  • Ejecutar un script que envíe automáticamente el formulario.
  • Enviar el envío del formulario como una solicitud AJAX.
  • Ocultar el formulario mediante CSS.

Estos escenarios alternativos no requieren ninguna acción ni entrada por parte del usuario aparte de visitar inicialmente el sitio malintencionado.

El uso de HTTPS no impide un ataque de CSRF. El sitio malintencionado puede enviar una solicitud https://www.good-banking-site.com/ con la misma facilidad con la que puede enviar una solicitud no segura.

Algunos ataques se dirigen a puntos de conexión que responden a solicitudes GET, en cuyo caso se puede usar una etiqueta de imagen para realizar la acción. Esta forma de ataque es común en sitios de foros que permiten imágenes pero bloquean JavaScript. Las aplicaciones que cambian el estado de las solicitudes GET, en las que se modifican variables o recursos, son vulnerables a ataques malintencionados. Las solicitudes GET que cambian de estado no son seguras. Un procedimiento recomendado es no cambiar nunca el estado en una solicitud GET.

Los ataques de CSRF se pueden producir en las aplicaciones web que usan cookies para la autenticación porque:

  • Los exploradores almacenan cookies emitidas por una aplicación web.
  • Las cookies almacenadas incluyen cookies de sesión para los usuarios autenticados.
  • Los exploradores envían todas las cookies asociadas con un dominio a la aplicación web cada solicitud independientemente de cómo se generó la solicitud a la aplicación en el explorador.

Sin embargo, los ataques de CSRF no se limitan a aprovechar las vulnerabilidades de las cookies. Por ejemplo, la autenticación básica e implícita también son vulnerables. Una vez que un usuario inicia sesión con la autenticación básica o implícita, el explorador envía automáticamente las credenciales hasta que finaliza la sesión.

En este contexto, la sesión hace referencia a la sesión del lado cliente durante la cual se autentica el usuario. No está relacionado con las sesiones del lado servidor ni el middleware de sesión de ASP.NET Core.

Para protegerse contra las vulnerabilidades de CSRF, los usuarios pueden tomar precauciones:

  • Cierre la sesión de las aplicaciones web cuando termine de usarlas.
  • Borre periódicamente las cookies del explorador.

Sin embargo, las vulnerabilidades de CSRF son fundamentalmente un problema con la aplicación web, no con el usuario final.

Aspectos básicos de la autenticación

La autenticación basada en Cookies es una forma popular de autenticación. Los sistemas de autenticación basados en tokens están creciendo en popularidad, especialmente para aplicaciones de página única (SPA).

Cuando un usuario se autentica con su nombre de usuario y contraseña, se emite un token que contiene un vale de autenticación que se puede usar para la autenticación y la autorización. El token se almacena como una cookie que se envía con cada solicitud que realiza el cliente. El middleware de autenticación de Cookies realiza la generación y validación de esta cookie. El middleware serializa una entidad de seguridad de usuario en una cookie cifrada. En las solicitudes posteriores, el middleware valida la cookie, vuelve a crear la entidad de seguridad y asigna la entidad de seguridad a la propiedad HttpContext.User.

Autenticación basada en tokens

Cuando se autentica un usuario, se emite un token (no un token de antifalsificación). El token contiene información de usuario en forma de notificaciones o un token de referencia que apunta la aplicación al estado de usuario mantenido en la aplicación. Cuando un usuario intenta acceder a un recurso que requiere autenticación, el token se envía a la aplicación con un encabezado de autorización adicional en forma de token de portador. Este enfoque hace que la aplicación no tenga estado. En cada solicitud posterior, el token se pasa en la solicitud de validación del lado servidor. Este token no está cifrado; está codificado. En el servidor, el token se descodifica para acceder a su información. Para enviar el token en solicitudes posteriores, almacene el token en el almacenamiento local del explorador. No se preocupe por la vulnerabilidad de CSRF si el token se almacena en el almacenamiento local del explorador. La CSRF es un problema cuando el token se almacena en una cookie. Para obtener más información, consulte el problema de GitHub El ejemplo de código SPA agrega dos cookies.

Varias aplicaciones hospedadas en un dominio

Los entornos de hospedaje compartidos son vulnerables al secuestro de sesión, a la CSRF de inicio de sesión y a otros ataques.

Aunque example1.contoso.net y example2.contoso.net son hosts diferentes, hay una relación de confianza implícita entre los hosts bajo el dominio *.contoso.net. Esta relación de confianza implícita permite a los hosts que podrían no ser de confianza afectar a las cookies de otros hosts (las directivas de mismo origen que rigen las solicitudes de AJAX no se aplican necesariamente a las cookies HTTP).

Los ataques que aprovechan las cookies de confianza entre aplicaciones hospedadas en el mismo dominio pueden evitarse si no se comparten dominios. Cuando cada aplicación se hospeda en su propio dominio, no hay ninguna relación de confianza implícita de cookies que se pueda aprovechar.

Configuración de la antifalsificación de ASP.NET Core

Advertencia

ASP.NET Core implementa la antifalsificación mediante la Protección de datos de ASP.NET Core. La pila de protección de datos debe configurarse para que funcione en una granja de servidores. Para obtener más información, consulte Configuración de la protección de datos.

El middleware de antifalsificación se agrega al contenedor de inserción de dependencias cuando se llama a una de las siguientes API en Startup.ConfigureServices:

En ASP.NET Core 2.0 o posterior, el elemento FormTagHelper inserta tokens de antifalsificación en los elementos de formulario HTML. El siguiente marcado en un archivo de Razor genera automáticamente tokens de antifalsificación:

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

De forma similar, IHtmlHelper.BeginForm genera tokens antifalsificación de forma predeterminada si el método del formulario no es GET.

La generación automática de tokens de antifalsificación para elementos de formulario HTML se produce cuando la etiqueta <form> contiene el atributo method="post" y se cumple alguna de las siguientes condiciones:

  • El atributo action está vacío (action="").
  • El atributo action no se proporciona (<form method="post">).

La generación automática de tokens de antifalsificación para elementos de formulario HTML se puede deshabilitar:

  • Deshabilite explícitamente tokens de antifalsificación con el atributo asp-antiforgery:

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • El elemento de formulario se ha excluido de los asistentes de etiquetas mediante el símbolo de rechazo ! del asistente de etiquetas:

    <!form method="post">
        ...
    </!form>
    
  • Quite FormTagHelper de la vista. FormTagHelper se puede quitar de una vista agregando la siguiente directiva a la vista de Razor:

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

Nota:

Razor Pages se protegen automáticamente de XSRF/CSRF. Para obtener más información, vea XSRF/CSRF y Razor Pages.

El enfoque más común para defenderse contra ataques de CSRF es usar el patrón de token del sincronizador (STP). El STP se usa cuando el usuario solicita una página con datos de formulario:

  1. El servidor envía un token asociado a la identidad del usuario actual al cliente.
  2. El cliente devuelve el token al servidor para su comprobación.
  3. Si el servidor recibe un token que no coincide con la identidad del usuario autenticado, se rechaza la solicitud.

El token es único e imprevisible. El token también se puede usar para garantizar la secuenciación correcta de una serie de solicitudes (por ejemplo, garantizar la secuencia de solicitudes de: página 1 > página 2 > página 3). Todos los formularios de las plantillas de Razor Pages y de ASP.NET Core MVC generan tokens de antifalsificación. El siguiente par de ejemplos de vista genera tokens de antifalsificación:

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

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

Agregue explícitamente un token de antifalsificación a un elemento <form> sin usar asistentes de etiquetas con el asistente de HTML @Html.AntiForgeryToken:

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

En cada uno de los casos anteriores, ASP.NET Core agrega un campo de formulario oculto similar al ejemplo siguiente:

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

ASP.NET Core incluye tres filtros para trabajar con tokens de antifalsificación:

Opciones de antifalsificación

Personalización de AntiforgeryOptions en Startup.ConfigureServices:

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

Establezca las propiedades de la Cookie de antifalsificación mediante las propiedades de la clase CookieBuilder, como se muestra en la tabla siguiente.

Opción Descripción
Cookie Determina la configuración usada para crear la cookie de antifalsificación.
FormFieldName Nombre del campo de formulario oculto utilizado por el sistema de antifalsificación para representar tokens de antifalsificación en vistas.
HeaderName Nombre del encabezado utilizado por el sistema de antifalsificación. Si es null, el sistema solo tiene en cuenta los datos del formulario.
SuppressXFrameOptionsHeader Especifica si se va a suprimir la generación del encabezado X-Frame-Options. De forma predeterminada, el encabezado se genera con un valor de "SAMEORIGIN". Tiene como valor predeterminado false.

Para obtener más información, vea CookieAuthenticationOptions.

Configuración de características de antifalsificación con IAntiforgery

IAntiforgeryproporciona la API para configurar características de antifalsificación. IAntiforgery se puede solicitar en el método Configure de la clase Startup.

En el ejemplo siguiente:

  • Se usa middleware de la página principal de la aplicación para generar un token de antifalsificación y enviarlo en la respuesta como cookie.
  • El token de solicitud se envía como una cookie legible en JavaScript con la convención de nomenclatura de Angular predeterminada que se describe en la sección 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);
    });
}

Requerir validación de antifalsificación

ValidateAntiForgeryToken es un filtro de acción que se puede aplicar a una acción individual, un controlador o globalmente. Las solicitudes realizadas a las acciones que tienen aplicado este filtro se bloquean a menos que la solicitud incluya un token de antifalsificación 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 });
}

El atributo ValidateAntiForgeryToken requiere un token para las solicitudes a los métodos de acción que marca, incluidas las solicitudes HTTP GET. Si el atributo ValidateAntiForgeryToken se aplica en los controladores de la aplicación, se puede invalidar con el atributo IgnoreAntiforgeryToken.

Nota:

ASP.NET Core no admite la adición automática de tokens de antifalsificación a las solicitudes GET.

Validar automáticamente tokens de antifalsificación solo para métodos HTTP no seguros

Las aplicaciones de ASP.NET Core no generan tokens de antifalsificación para métodos HTTP seguros (GET, HEAD, OPTIONS y TRACE). En lugar de aplicar ampliamente el atributo ValidateAntiForgeryToken y, a continuación, reemplazarlo con atributos IgnoreAntiforgeryToken, se puede usar el atributo AutoValidateAntiforgeryToken. Este atributo funciona de forma idéntica al atributo ValidateAntiForgeryToken, salvo que no requiere tokens para las solicitudes realizadas mediante los siguientes métodos HTTP:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

Se recomienda usar ampliamente AutoValidateAntiforgeryToken para escenarios que no son de API. Este atributo garantiza que las acciones POST estén protegidas de forma predeterminada. La alternativa es omitir los tokens de antifalsificación de forma predeterminada, a menos que ValidateAntiForgeryToken se aplique a métodos de acción individuales. Es más probable que en este escenario un método de acción POST se deje desprotegido por error, lo que hace que la aplicación sea vulnerable a los ataques de CSRF. Todos los POST deben enviar el token de antifalsificación.

Las API no tienen un mecanismo automático para enviar la parte del token que no es una cookie. La implementación probablemente depende de la implementación del código de cliente. A continuación se muestran algunos ejemplos:

Ejemplo de nivel de clase:

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

Ejemplo global:

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

Invalidación de atributos de antifalsificación globales o de controlador

El filtro IgnoreAntiforgeryToken se usa para evitar tener que usar un token de antifalsificación para una acción determinada (o controlador). Cuando se aplica, este filtro invalida los filtros ValidateAntiForgeryToken y AutoValidateAntiforgeryToken especificados en un nivel superior (global o en un controlador).

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

Actualización de tokens después de la autenticación

Los tokens se deben actualizar después de que el usuario se autentique al redirigir al usuario a una vista o página de Razor Pages.

JavaScript, AJAX y SPA

En las aplicaciones tradicionales basadas en HTML, los tokens de antifalsificación se pasan al servidor mediante campos de formulario ocultos. En las aplicaciones y SPA modernas basadas en JavaScript, muchas solicitudes se realizan mediante programación. Estas solicitudes de AJAX pueden usar otras técnicas (como encabezados de solicitud o cookies) para enviar el token.

Si se usan cookies para almacenar tokens de autenticación y para autenticar solicitudes de API en el servidor, la CSRF es un posible problema. Si el almacenamiento local se usa para almacenar el token, se podría mitigar la vulnerabilidad de CSRF porque los valores del almacenamiento local no se envían automáticamente al servidor con cada solicitud. El uso del almacenamiento local para almacenar el token de antifalsificación en el cliente y enviar el token como encabezado de solicitud es un enfoque recomendado.

JavaScript

Al usar JavaScript con vistas, el token se puede crear mediante un servicio desde dentro de la vista. Inserte el servicio IAntiforgery en la vista y llame a 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>

Este enfoque evita tener que tratar directamente con la configuración de cookies del servidor o leerlas del cliente.

En el ejemplo anterior se usa JavaScript para leer el valor de campo oculto del encabezado POST de AJAX.

JavaScript también puede acceder a tokens de cookies y usar el contenido de la cookie para crear un encabezado con el valor del token.

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

Suponiendo que el script solicita enviar el token en un encabezado denominado X-CSRF-TOKEN, configure el servicio de antifalsificación para buscar el encabezado X-CSRF-TOKEN:

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

En el ejemplo siguiente se usa JavaScript para realizar una solicitud de AJAX con el encabezado adecuado:

function getCookie(cname) {
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) === ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) === 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

var csrfToken = getCookie("CSRF-TOKEN");

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
    if (xhttp.readyState === XMLHttpRequest.DONE) {
        if (xhttp.status === 204) {
            alert('Todo item is created successfully.');
        } else {
            alert('There was an error processing the AJAX request.');
        }
    }
};
xhttp.open('POST', '/api/items', true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({ "name": "Learn C#" }));

AngularJS

AngularJS usa una convención para abordar la CSRF. Si el servidor envía una cookie denominada XSRF-TOKEN, el servicio AngularJS$http agrega el valor de cookie a un encabezado cuando envía una solicitud al servidor. Este es un proceso automático. El cliente no necesita establecer el encabezado explícitamente. El nombre del encabezado es X-XSRF-TOKEN. El servidor debe detectar este encabezado y validar su contenido.

Para que la API de ASP.NET Core funcione con esta convención en el inicio de la aplicación:

  • Configure la aplicación para proporcionar un token en una cookie denominada XSRF-TOKEN.
  • Configure el servicio de antifalsificación para buscar un encabezado denominado X-XSRF-TOKEN, que es el nombre de encabezado predeterminado de Angular para enviar el token XSRF.
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
    app.Use(next => context =>
    {
        string path = context.Request.Path.Value;

        if (
            string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
            string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
        {
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, 
                new CookieOptions() { HttpOnly = false });
        }

        return next(context);
    });
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
}

Nota:

Cuando el token antifalsificación se proporciona tanto en la cabecera de la solicitud como en la carga útil del formulario, sólo se valida el token de la cabecera.

cookies de autenticación de Windows y antifalsificación

Al usar la autenticación de Windows, los puntos de conexión de la aplicación deben protegerse frente a ataques de CSRF de la misma manera que se hace para las cookies. El explorador envía implícitamente el contexto de autenticación al servidor y, por tanto, los puntos de conexión deben protegerse frente a ataques de CSRF.

Extensión de la antifalsificación

El tipo IAntiforgeryAdditionalDataProvider permite a los desarrolladores ampliar el comportamiento del sistema anti-CSRF mediante el recorrido de ida y vuelta de datos adicionales en cada token. Se llama al método GetAdditionalData cada vez que se genera un token de campo; el valor devuelto se inserta dentro del token generado. Un implementador podría devolver una marca de tiempo, un valor nonce o cualquier otro valor y, a continuación, llamar a ValidateAdditionalData para validar estos datos cuando se valida el token. El nombre de usuario del cliente ya está insertado en los tokens generados, por lo que no es necesario incluir esta información. Si un token incluye datos complementarios pero no se configura un IAntiForgeryAdditionalDataProvider, los datos complementarios no se validan.

Recursos adicionales