Предотвращение атак с межсайтовой подделкой запросов (XSRF/CSRF) в ASP.NET Core

Фияз Хасан и Рик Андерсон

Подделка межсайтовых запросов — это атака на веб-приложения, в которых вредоносное веб-приложение может повлиять на взаимодействие между клиентским браузером и веб-приложением, которое доверяет браузеру. Эти атаки возможны, так как веб-браузеры автоматически отправляют некоторые типы маркеров проверки подлинности с каждым запросом на веб-сайт. Эта форма эксплойта также называется атакой одного щелчка или ездой на сеансе, так как атака использует ранее прошедший проверку подлинности сеанс пользователя. Подделка межсайтовых запросов также называется XSRF или CSRF.

Пример атаки CSRF:

  1. Пользователь входит в www.good-banking-site.example.com систему с помощью проверки подлинности форм. Сервер выполняет проверку подлинности пользователя и выдает ответ, содержащий проверку подлинности cookie. Сайт уязвим для атаки, так как он доверяет любому запросу, который он получает с допустимой проверкой подлинности cookie.

  2. Пользователь посещает вредоносный сайт. www.bad-crook-site.example.com

    Вредоносный сайт www.bad-crook-site.example.comсодержит HTML-форму, аналогичную следующему примеру:

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

    Обратите внимание, что записи формы action на уязвимый сайт, а не на вредоносный сайт. Это "кросс-сайт" части CSRF.

  3. Пользователь выбирает кнопку отправки. Браузер выполняет запрос и автоматически включает проверку подлинности cookie для запрошенного домена www.good-banking-site.example.com.

  4. Запрос выполняется на сервере www.good-banking-site.example.com с контекстом проверки подлинности пользователя и может выполнять любое действие, которое разрешено выполнить пользователю, прошедшему проверку подлинности.

Помимо сценария, когда пользователь выбирает кнопку для отправки формы, вредоносный сайт может:

  • Запустите скрипт, который автоматически отправляет форму.
  • Отправьте отправку формы в виде запроса AJAX.
  • Скрытие формы с помощью CSS.

Эти альтернативные сценарии не требуют никаких действий или входных данных от пользователя, кроме первоначального посещения вредоносного сайта.

Использование HTTPS не предотвращает атаку CSRF. Вредоносный https://www.good-banking-site.com/ сайт может отправлять запрос так же легко, как он может отправлять небезопасный запрос.

Некоторые атаки предназначены для конечных точек, реагирующих на запросы GET, в этом случае тег изображения можно использовать для выполнения действия. Эта форма атаки распространена на сайтах форума, которые разрешают изображения, но блокируют JavaScript. Приложения, изменяющие состояние запросов GET, в которых переменные или ресурсы изменяются, уязвимы для вредоносных атак. Запросы GET, изменяющие состояние, небезопасны. Рекомендуется никогда не изменять состояние запроса GET.

Атаки CSRF возможны для веб-приложений, которые используются cookieдля проверки подлинности, так как:

  • Браузеры хранят cookieприложения, выданные веб-приложением.
  • Сохраненные cookieсеансы включают сеансы cookieдля прошедших проверку подлинности пользователей.
  • Браузеры отправляют все cookieсвязанные с доменом домены в веб-приложение каждый запрос независимо от того, как был создан запрос к приложению в браузере.

Однако атаки CSRF не ограничиваются эксплойтами cookie. Например, обычная и дайджест-проверка подлинности также уязвимы. После входа пользователя с помощью базовой или дайджест-проверки подлинности браузер автоматически отправляет учетные данные до окончания сеанса.

В этом контексте сеанс ссылается на сеанс на стороне клиента, в течение которого пользователь проходит проверку подлинности. Он не связан с сеансами на стороне сервера или по промежуточному слоям основных сеансов ASP.NET.

Пользователи могут защищать уязвимости CSRF, принимая меры предосторожности:

  • Выйдите из веб-приложений после завершения работы с ними.
  • Периодически очищать браузер cookie.

Однако уязвимости CSRF являются основной проблемой веб-приложения, а не конечным пользователем.

Основы проверки подлинности

CookieПроверка подлинности на основе — это популярная форма проверки подлинности. Системы проверки подлинности на основе маркеров растут в популярности, особенно для одностраничных приложений (SPAs).

Когда пользователь проходит проверку подлинности с помощью имени пользователя и пароля, он выдает маркер, содержащий билет проверки подлинности. Маркер можно использовать для проверки подлинности и авторизации. Маркер хранится в виде отправленного cookie с каждым запросом клиента. Создание и проверка этого cookie выполняется с помощью ПО промежуточного Cookie слоя проверки подлинности. ПО промежуточного слоя сериализует субъект-пользователя в зашифрованный cookie. В последующих запросах ПО промежуточного слоя проверяет cookie, повторно создает субъект и назначает субъекту свойству HttpContext.User .

Проверка подлинности на основе токенов

Когда пользователь проходит проверку подлинности, он выдает маркер (а не маркер антифоргерии). Маркер содержит сведения о пользователях в виде утверждений или маркера ссылки, который указывает приложению на состояние пользователя, поддерживаемое в приложении. Когда пользователь пытается получить доступ к ресурсу, требующий проверки подлинности, маркер отправляется приложению с дополнительным заголовком авторизации в виде маркера носителя. Такой подход делает приложение без отслеживания состояния. В каждом последующем запросе маркер передается в запросе на проверку на стороне сервера. Этот маркер не шифруется; он закодирован. На сервере маркер декодируется для доступа к его информации. Чтобы отправить маркер на последующие запросы, сохраните маркер в локальном хранилище браузера. Размещение маркера в локальном хранилище браузера и его извлечение и использование в качестве маркера носителя обеспечивает защиту от атак CSRF. Однако если приложение будет уязвимо для внедрения скриптов через XSS или скомпрометированный внешний файл JavaScript, злоумышленник может получить любое значение из локального хранилища и отправить его себе. ASP.NET Core кодирует все выходные данные на стороне сервера из переменных по умолчанию, что снижает риск XSS. При переопределении этого поведения с помощью Html.Raw или пользовательского кода с ненадежными входными данными можно увеличить риск XSS.

Не беспокойтесь об уязвимости CSRF, если маркер хранится в локальном хранилище браузера. CSRF является проблемой, когда маркер хранится в объекте cookie. Дополнительные сведения см. в примере cookieкода SPA проблемы GitHub.

Несколько приложений, размещенных в одном домене

Общие среды размещения уязвимы для перехвата сеансов, входа в CSRF и других атак.

Хотя example1.contoso.net и example2.contoso.net являются разными узлами, существует неявная связь доверия между узлами в домене *.contoso.net . Эта неявная связь доверия позволяет потенциально ненадежным узлам влиять друг на друга cookie(политики того же источника, которые управляют запросами AJAX, не обязательно применяются к HTTP-адресам cookie).

Атаки, которые используют доверенные cookies между приложениями, размещенными в одном домене, можно предотвратить, не предоставляя общий доступ к доменам. Если каждое приложение размещено в собственном домене, не существует неявных cookie отношений доверия для эксплойтов.

Антифоргерия в ASP.NET Core

Предупреждение

ASP.NET Core реализует антифоргерию с помощью ASP.NET Core Data Protection. Стек защиты данных должен быть настроен для работы в ферме серверов. Дополнительные сведения см. в разделе "Настройка защиты данных".

По промежуточному слоям защиты добавляется в контейнер внедрения зависимостей при вызове одного из следующих API:Program.cs

Дополнительные сведения см. в разделе "Антифоргерия с минимальными API".

FormTagHelper вставляет маркеры защиты от подделки в элементы HTML-форм. Следующая разметка в Razor файле автоматически создает маркеры антифоргерии:

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

Аналогичным образом создается маркеры антифоргерии по умолчанию, IHtmlHelper.BeginForm если метод формы не является GET.

Автоматическое создание маркеров антифоргерии для элементов формы HTML происходит, когда <form> тег содержит method="post" атрибут, и любой из следующих значений имеет значение true:

  • Атрибут действия пуст (action="").
  • Атрибут действия не предоставляется (<form method="post">).

Автоматическое создание маркеров антифоргерии для элементов формы HTML можно отключить:

  • Явным образом отключите маркеры защиты от атрибута asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • Элемент формы отказано в вспомогательных функциях тегов с помощью символа отказа от тега!

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Удалите его FormTagHelper из представления. Его FormTagHelper можно удалить из представления, добавив следующую директиву в Razor представление:

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

Примечание.

Razor Страницы автоматически защищены от XSRF/CSRF. Дополнительные сведения см. в разделе XSRF/CSRF и Razor Pages.

Наиболее распространенный подход к защите от атак CSRF — использовать шаблон токена синхронизатора (STP). STP используется, когда пользователь запрашивает страницу с данными формы:

  1. Сервер отправляет маркер, связанный с удостоверением текущего пользователя клиенту.
  2. Клиент отправляет маркер на сервер для проверки.
  3. Если сервер получает маркер, который не соответствует удостоверению пользователя, прошедшего проверку подлинности, запрос отклоняется.

Маркер является уникальным и непредсказуемым. Маркер также можно использовать для обеспечения правильной последовательности запросов (например, обеспечения последовательности запросов: страницы 1 > страницы 2 > страницы 3). Все формы в ASP.NET шаблонах Core MVC и Razor Pages создают маркеры защиты от подделки. Следующая пара примеров представления создает маркеры антифоргерии:

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

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

Явным образом добавьте маркер антифоргерии в <form> элемент без использования вспомогательных средств тегов с вспомогательным элементом @Html.AntiForgeryTokenHTML:

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

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

В каждом из предыдущих случаев ASP.NET Core добавляет скрытое поле формы, аналогичное следующему примеру:

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

ASP.NET Core включает три фильтра для работы с маркерами защиты от подделки:

Антифоргерия с AddControllers

Вызов AddControllers не включает маркеры защиты от подделки. AddControllersWithViews Необходимо вызвать встроенную поддержку маркеров защиты от подделки.

Несколько вкладок браузера и шаблон токена синхронизатора

С помощью шаблона маркера синхронизатора только последняя загруженная страница содержит допустимый маркер антифоргерии. Использование нескольких вкладок может быть проблематичным. Например, если пользователь открывает несколько вкладок:

  • Только последняя загруженная вкладка содержит допустимый маркер антифоргерии.
  • Запросы, сделанные из ранее загруженных вкладок, завершаются ошибкой: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Если это представляет проблему, рассмотрите альтернативные шаблоны защиты CSRF.

Настройка антифоргерии с помощью AntiforgeryOptions

Настройка AntiforgeryOptions в Program.cs:

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

Задайте свойства антифоргерии Cookie с помощью свойств CookieBuilder класса, как показано в следующей таблице.

Вариант Описание
Cookie Определяет параметры, используемые для создания антифоргерийных cookies.
FormFieldName Имя скрытого поля формы, используемого системой антифоргерии для отображения маркеров антифоргерии в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

Дополнительные сведения см. в разделе CookieAuthenticationOptions.

Создание маркеров защиты с помощью IAntiforgery

IAntiforgery предоставляет API для настройки функций антифоргерии. IAntiforgery его можно запросить Program.cs с помощью WebApplication.Services. В следующем примере по промежуточному слоям на домашней странице приложения создается маркер антифоргерии и отправляет его в ответ в виде 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);
});

В предыдущем примере задается именованный cookieXSRF-TOKEN. Клиент может прочитать это cookie и указать его значение в качестве заголовка, присоединенного к запросам AJAX. Например, Angular включает встроенную защиту XSRF, которая считывает именованный cookieXSRF-TOKEN по умолчанию.

Требовать проверку антифоргерии

Фильтр действий ValidateAntiForgeryToken можно применить к отдельному действию, контроллеру или глобально. Запросы, сделанные к действиям, которые применяются с этим фильтром, блокируются, если запрос не содержит допустимый маркер антифоргерии:

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

    return RedirectToAction();
}

Атрибуту ValidateAntiForgeryToken требуется маркер для запросов к методам действия, которые он помечает, включая HTTP-запросы GET. ValidateAntiForgeryToken Если атрибут применяется к контроллерам приложения, его можно переопределить атрибутомIgnoreAntiforgeryToken.

Автоматическая проверка маркеров защиты от подделки только для небезопасных методов HTTP

Вместо широкого применения атрибута ValidateAntiForgeryToken и переопределения его атрибутами IgnoreAntiforgeryTokenможно использовать атрибут AutoValidateAntiforgeryToken . Этот атрибут работает идентично атрибуту ValidateAntiForgeryToken , за исключением того, что он не требует маркеров для запросов, выполненных с помощью следующих методов HTTP:

  • GET
  • HEAD
  • ПАРАМЕТРЫ
  • ТРАССИРОВКА

Рекомендуется использовать AutoValidateAntiforgeryToken широко для сценариев, отличных от API. Этот атрибут гарантирует, что действия POST защищены по умолчанию. Альтернативой является игнорировать маркеры антифоргерии по умолчанию, если ValidateAntiForgeryToken к отдельным методам действия не применяется. Скорее всего, в этом сценарии метод действия POST остается незащищенным по ошибке, оставляя приложение уязвимым к атакам CSRF. Все POS-адреса должны отправлять маркер антифоргерии.

Api не имеют автоматического механизма отправки токена, отличногоcookie от части маркера. Реализация, вероятно, зависит от реализации клиентского кода. Ниже показаны некоторые примеры:

Пример уровня класса:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Глобальный пример:

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

Переопределение глобальных или антифоргерийных атрибутов контроллера

Фильтр IgnoreAntiforgeryToken используется для устранения необходимости в маркере защиты от определенного действия (или контроллера). При применении этот фильтр переопределяет и AutoValidateAntiforgeryToken фильтруетValidateAntiForgeryToken, указанные на более высоком уровне (глобально или на контроллере).

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

    return RedirectToAction();
}

Обновление маркеров после проверки подлинности

Маркеры должны обновляться после проверки подлинности пользователя, перенаправляя пользователя на страницу представления или Razor страницы.

JavaScript, AJAX и spAs

В традиционных приложениях на основе HTML маркеры антифоргерии передаются серверу с помощью скрытых полей формы. В современных приложениях и spAs на основе JavaScript многие запросы выполняются программными средствами. Эти запросы AJAX могут использовать другие методы, такие как заголовки запросов или cookies, для отправки маркера.

Если cookieиспользуются для хранения маркеров проверки подлинности и проверки подлинности запросов API на сервере, CSRF является потенциальной проблемой. Если локальное хранилище используется для хранения маркера, уязвимость CSRF может быть устранена, так как значения из локального хранилища не отправляются автоматически на сервер с каждым запросом. Использование локального хранилища для хранения маркера защиты от подделки на клиенте и отправка маркера в качестве заголовка запроса рекомендуется.

Blazor

Дополнительные сведения см. в разделе ASP.NET Проверка подлинности и авторизация CoreBlazor.

JavaScript

С помощью JavaScript с представлениями маркер можно создать с помощью службы в представлении. IAntiforgery Внедрение службы в представление и вызов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>
}

В предыдущем примере используется JavaScript для чтения значения скрытого поля для заголовка AJAX POST.

Этот подход устраняет необходимость непосредственного решения параметров cookieс сервера или считывания их из клиента. Однако при внедрении IAntiforgery службы используйте JavaScript для доступа к маркерам в cookieследующих случаях:

  • Маркеры доступа в дополнительном запросе к серверу обычно same-origin.
  • Используйте содержимое cookie, чтобы создать заголовок со значением маркера.

Если скрипт отправляет маркер в заголовке запроса X-XSRF-TOKEN, настройте службу защиты от подделки для поиска заголовка X-XSRF-TOKEN :

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

В следующем примере добавляется защищенная конечная точка, которая записывает маркер запроса в доступный cookieдля чтения 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();

В следующем примере JavaScript используется для выполнения запроса AJAX для получения маркера и выполнения другого запроса с соответствующим заголовком:

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

Примечание.

Если маркер антифоргерии указан как в заголовке запроса, так и в полезных данных формы, проверяется только маркер в заголовке.

Антифоргерия с минимальными API

Вызов AddAntiforgery и UseAntiforgery(IApplicationBuilder) регистрацию служб защиты от подделки в DI. Маркеры антифоргерии используются для устранения атак на подделку межсайтовых запросов.

var builder = WebApplication.CreateBuilder();

builder.Services.AddAntiforgery();

var app = builder.Build();

app.UseAntiforgery();

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

app.Run();

По промежуточному слоям защиты:

Маркер антифоргерии проверяется только в том случае, если:

  • Конечная точка содержит метаданные, реализующие IAntiforgeryMetadataRequiresValidation=true.
  • Метод HTTP, связанный с конечной точкой, является соответствующим методом HTTP. Соответствующие методы — это все методы HTTP, кроме TRACE, OPTIONS, HEAD и GET.
  • Запрос связан с допустимой конечной точкой.

Примечание. При включении вручную ПО промежуточного слоя защиты от подделки должно выполняться после проверки подлинности и ПО промежуточного слоя авторизации, чтобы предотвратить чтение данных формы, если пользователь не прошел проверку подлинности.

По умолчанию минимальные API, принимаюющие данные формы, требуют проверки маркеров защиты от подделки.

Рассмотрим следующий GenerateForm метод:

public static string GenerateForm(string action, 
    AntiforgeryTokenSet token, bool UseToken=true)
{
    string tokenInput = "";
    if (UseToken)
    {
        tokenInput = $@"<input name=""{token.FormFieldName}""
                         type=""hidden"" value=""{token.RequestToken}"" />";
    }

    return $@"
    <html><body>
        <form action=""{action}"" method=""POST"" enctype=""multipart/form-data"">
            {tokenInput}
            <input type=""text"" name=""name"" />
            <input type=""date"" name=""dueDate"" />
            <input type=""checkbox"" name=""isCompleted"" />
            <input type=""submit"" />
        </form>
    </body></html>
";
}

Предыдущий код содержит три аргумента, действие, маркер защиты от подделки и bool указание того, следует ли использовать маркер.

Рассмотрим следующий пример:

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

В приведенном выше коде записи в:

  • /todo требует допустимого маркера защиты от подделки.
  • /todo2Не требуется допустимый маркер антифоргерии, так как DisableAntiforgery вызывается.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

Post to:

  • /todo из формы, созданной конечной / точкой, успешно выполнена, так как маркер антифоргерии действителен.
  • /todo из формы, созданной /SkipToken сбоем, потому что антифоргерия не включена.
  • /todo2 из формы, созданной конечной /DisableAntiforgery точкой, завершается успешно, так как антифоргерия не требуется.
app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));

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

При отправке формы без допустимого маркера антифоргерии:

  • В среде разработки создается исключение.
  • В рабочей среде регистрируется сообщение.

проверка подлинности Windows и антифоргерии cookie

При использовании проверки подлинности Windows конечные точки приложений должны быть защищены от атак CSRF так же, как и для cookies. Браузер неявно отправляет контекст проверки подлинности серверу и конечным точкам, которые необходимо защитить от атак CSRF.

Расширение антифоргерии

Тип IAntiforgeryAdditionalDataProvider позволяет разработчикам расширить поведение системы защиты от CSRF путем обхода дополнительных данных в каждом токене. Метод GetAdditionalData вызывается каждый раз при создании маркера поля, а возвращаемое значение внедрено в созданный маркер. Реализующий может возвращать метку времени, nonce или любое другое значение, а затем вызывать ValidateAdditionalData проверку этих данных при проверке маркера. Имя пользователя клиента уже внедрено в созданные маркеры, поэтому не нужно включать эти сведения. Если маркер содержит дополнительные данные, но не IAntiForgeryAdditionalDataProvider настроен, дополнительные данные не проверяются.

Дополнительные ресурсы

Межсайтовые подделки запросов (также известные как XSRF или CSRF) — это атака на веб-размещенные веб-приложения, в которых вредоносное веб-приложение может влиять на взаимодействие между клиентским браузером и веб-приложением, которое доверяет браузеру. Эти атаки возможны, так как веб-браузеры автоматически отправляют некоторые типы маркеров проверки подлинности с каждым запросом на веб-сайт. Эта форма эксплойта также называется атакой одного щелчка или ездой на сеансе, так как атака использует ранее прошедший проверку подлинности сеанс пользователя.

Пример атаки CSRF:

  1. Пользователь входит в www.good-banking-site.example.com систему с помощью проверки подлинности форм. Сервер выполняет проверку подлинности пользователя и выдает ответ, содержащий проверку подлинности cookie. Сайт уязвим для атаки, так как он доверяет любому запросу, который он получает с допустимой проверкой подлинности cookie.

  2. Пользователь посещает вредоносный сайт. www.bad-crook-site.example.com

    Вредоносный сайт www.bad-crook-site.example.comсодержит HTML-форму, аналогичную следующему примеру:

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

    Обратите внимание, что записи формы action на уязвимый сайт, а не на вредоносный сайт. Это "кросс-сайт" части CSRF.

  3. Пользователь выбирает кнопку отправки. Браузер выполняет запрос и автоматически включает проверку подлинности cookie для запрошенного домена www.good-banking-site.example.com.

  4. Запрос выполняется на сервере www.good-banking-site.example.com с контекстом проверки подлинности пользователя и может выполнять любое действие, которое разрешено выполнить пользователю, прошедшему проверку подлинности.

Помимо сценария, когда пользователь выбирает кнопку для отправки формы, вредоносный сайт может:

  • Запустите скрипт, который автоматически отправляет форму.
  • Отправьте отправку формы в виде запроса AJAX.
  • Скрытие формы с помощью CSS.

Эти альтернативные сценарии не требуют никаких действий или входных данных от пользователя, кроме первоначального посещения вредоносного сайта.

Использование HTTPS не предотвращает атаку CSRF. Вредоносный https://www.good-banking-site.com/ сайт может отправлять запрос так же легко, как он может отправлять небезопасный запрос.

Некоторые атаки предназначены для конечных точек, реагирующих на запросы GET, в этом случае тег изображения можно использовать для выполнения действия. Эта форма атаки распространена на сайтах форума, которые разрешают изображения, но блокируют JavaScript. Приложения, изменяющие состояние запросов GET, в которых переменные или ресурсы изменяются, уязвимы для вредоносных атак. Запросы GET, изменяющие состояние, небезопасны. Рекомендуется никогда не изменять состояние запроса GET.

Атаки CSRF возможны для веб-приложений, которые используются cookieдля проверки подлинности, так как:

  • Браузеры хранят cookieприложения, выданные веб-приложением.
  • Сохраненные cookieсеансы включают сеансы cookieдля прошедших проверку подлинности пользователей.
  • Браузеры отправляют все cookieсвязанные с доменом домены в веб-приложение каждый запрос независимо от того, как был создан запрос к приложению в браузере.

Однако атаки CSRF не ограничиваются эксплойтами cookie. Например, обычная и дайджест-проверка подлинности также уязвимы. После входа пользователя с помощью базовой или дайджест-проверки подлинности браузер автоматически отправляет учетные данные до окончания сеанса.

В этом контексте сеанс ссылается на сеанс на стороне клиента, в течение которого пользователь проходит проверку подлинности. Он не связан с сеансами на стороне сервера или по промежуточному слоям основных сеансов ASP.NET.

Пользователи могут защищать уязвимости CSRF, принимая меры предосторожности:

  • Выйдите из веб-приложений после завершения работы с ними.
  • Периодически очищать браузер cookie.

Однако уязвимости CSRF являются основной проблемой веб-приложения, а не конечным пользователем.

Основы проверки подлинности

CookieПроверка подлинности на основе — это популярная форма проверки подлинности. Системы проверки подлинности на основе маркеров растут в популярности, особенно для одностраничных приложений (SPAs).

Когда пользователь проходит проверку подлинности с помощью имени пользователя и пароля, он выдает маркер, содержащий билет проверки подлинности, который можно использовать для проверки подлинности и авторизации. Маркер хранится в виде отправленного cookie с каждым запросом клиента. Создание и проверка этого cookie выполняется ПО промежуточного Cookie слоя проверки подлинности. ПО промежуточного слоя сериализует субъект-пользователя в зашифрованный cookie. В последующих запросах ПО промежуточного слоя проверяет cookie, повторно создает субъект и назначает субъекту свойству HttpContext.User .

Проверка подлинности на основе токенов

Когда пользователь проходит проверку подлинности, он выдает маркер (а не маркер антифоргерии). Маркер содержит сведения о пользователях в виде утверждений или маркера ссылки, который указывает приложению на состояние пользователя, поддерживаемое в приложении. Когда пользователь пытается получить доступ к ресурсу, требующий проверки подлинности, маркер отправляется приложению с дополнительным заголовком авторизации в виде маркера носителя. Такой подход делает приложение без отслеживания состояния. В каждом последующем запросе маркер передается в запросе на проверку на стороне сервера. Этот маркер не шифруется; он закодирован. На сервере маркер декодируется для доступа к его информации. Чтобы отправить маркер на последующие запросы, сохраните маркер в локальном хранилище браузера. Размещение маркера в локальном хранилище браузера и его извлечение и использование в качестве маркера носителя обеспечивает защиту от атак CSRF. Однако если приложение будет уязвимо для внедрения скриптов через XSS или скомпрометированный внешний файл javascript, злоумышленник может получить любое значение из локального хранилища и отправить его себе. ASP.NET Core кодирует все выходные данные на стороне сервера из переменных по умолчанию, что снижает риск XSS. При переопределении этого поведения с помощью Html.Raw или пользовательского кода с ненадежными входными данными можно увеличить риск XSS.

Не беспокойтесь об уязвимости CSRF, если маркер хранится в локальном хранилище браузера. CSRF является проблемой, когда маркер хранится в объекте cookie. Дополнительные сведения см. в примере cookieкода SPA проблемы GitHub.

Несколько приложений, размещенных в одном домене

Среды общего размещения уязвимы для перехвата сеанса, входа CSRF и других атак.

Хотя example1.contoso.net и example2.contoso.net являются разными узлами, существует неявная связь доверия между узлами в домене *.contoso.net . Эта неявная связь доверия позволяет потенциально ненадежным узлам влиять друг на друга cookie(политики того же источника, которые управляют запросами AJAX, не обязательно применяются к HTTP-адресам cookie).

Атаки, которые используют доверенные cookies между приложениями, размещенными в одном домене, можно предотвратить, не предоставляя общий доступ к доменам. Если каждое приложение размещено в собственном домене, не существует неявных cookie отношений доверия для эксплойтов.

Антифоргерия в ASP.NET Core

Предупреждение

ASP.NET Core реализует антифоргерию с помощью ASP.NET Core Data Protection. Стек защиты данных должен быть настроен для работы в ферме серверов. Дополнительные сведения см. в разделе "Настройка защиты данных".

По промежуточному слоям защиты добавляется в контейнер внедрения зависимостей при вызове одного из следующих API:Program.cs

FormTagHelper вставляет маркеры защиты от подделки в элементы HTML-форм. Следующая разметка в Razor файле автоматически создает маркеры антифоргерии:

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

Аналогичным образом создается маркеры антифоргерии по умолчанию, IHtmlHelper.BeginForm если метод формы не является GET.

Автоматическое создание маркеров антифоргерии для элементов формы HTML происходит, когда <form> тег содержит method="post" атрибут, и любой из следующих значений имеет значение true:

  • Атрибут действия пуст (action="").
  • Атрибут действия не предоставляется (<form method="post">).

Автоматическое создание маркеров антифоргерии для элементов формы HTML можно отключить:

  • Явным образом отключите маркеры защиты от атрибута asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • Элемент формы отказано в вспомогательных функциях тегов с помощью символа отказа от тега!

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Удалите его FormTagHelper из представления. Его FormTagHelper можно удалить из представления, добавив следующую директиву в Razor представление:

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

Примечание.

Razor Страницы автоматически защищены от XSRF/CSRF. Дополнительные сведения см. в разделе XSRF/CSRF и Razor Pages.

Наиболее распространенный подход к защите от атак CSRF — использовать шаблон токена синхронизатора (STP). STP используется, когда пользователь запрашивает страницу с данными формы:

  1. Сервер отправляет маркер, связанный с удостоверением текущего пользователя клиенту.
  2. Клиент отправляет маркер на сервер для проверки.
  3. Если сервер получает маркер, который не соответствует удостоверению пользователя, прошедшего проверку подлинности, запрос отклоняется.

Маркер является уникальным и непредсказуемым. Маркер также можно использовать для обеспечения правильной последовательности запросов (например, обеспечения последовательности запросов: страницы 1 > страницы 2 > страницы 3). Все формы в ASP.NET шаблонах Core MVC и Razor Pages создают маркеры защиты от подделки. Следующая пара примеров представления создает маркеры антифоргерии:

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

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

Явным образом добавьте маркер антифоргерии в <form> элемент без использования вспомогательных средств тегов с вспомогательным элементом @Html.AntiForgeryTokenHTML:

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

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

В каждом из предыдущих случаев ASP.NET Core добавляет скрытое поле формы, аналогичное следующему примеру:

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

ASP.NET Core включает три фильтра для работы с маркерами защиты от подделки:

Антифоргерия с AddControllers

Вызов AddControllers не включает маркеры защиты от подделки. AddControllersWithViews Необходимо вызвать встроенную поддержку маркеров защиты от подделки.

Несколько вкладок браузера и шаблон токена синхронизатора

С помощью шаблона маркера синхронизатора только последняя загруженная страница содержит допустимый маркер антифоргерии. Использование нескольких вкладок может быть проблематичным. Например, если пользователь открывает несколько вкладок:

  • Только последняя загруженная вкладка содержит допустимый маркер антифоргерии.
  • Запросы, сделанные из ранее загруженных вкладок, завершаются ошибкой: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Если это представляет проблему, рассмотрите альтернативные шаблоны защиты CSRF.

Настройка антифоргерии с помощью AntiforgeryOptions

Настройка AntiforgeryOptions в Program.cs:

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

Задайте свойства антифоргерии Cookie с помощью свойств CookieBuilder класса, как показано в следующей таблице.

Вариант Описание
Cookie Определяет параметры, используемые для создания антифоргерийных cookies.
FormFieldName Имя скрытого поля формы, используемого системой антифоргерии для отображения маркеров антифоргерии в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

Дополнительные сведения см. в разделе CookieAuthenticationOptions.

Создание маркеров защиты с помощью IAntiforgery

IAntiforgery предоставляет API для настройки функций антифоргерии. IAntiforgery его можно запросить Program.cs с помощью WebApplication.Services. В следующем примере по промежуточному слоям на домашней странице приложения создается маркер антифоргерии и отправляет его в ответ в виде 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);
});

В предыдущем примере задается именованный cookieXSRF-TOKEN. Клиент может прочитать это cookie и указать его значение в качестве заголовка, присоединенного к запросам AJAX. Например, Angular включает встроенную защиту XSRF, которая считывает именованный cookieXSRF-TOKEN по умолчанию.

Требовать проверку антифоргерии

Фильтр действий ValidateAntiForgeryToken можно применить к отдельному действию, контроллеру или глобально. Запросы, сделанные к действиям, которые применяются с этим фильтром, блокируются, если запрос не содержит допустимый маркер антифоргерии:

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

    return RedirectToAction();
}

Атрибуту ValidateAntiForgeryToken требуется маркер для запросов к методам действия, которые он помечает, включая HTTP-запросы GET. ValidateAntiForgeryToken Если атрибут применяется к контроллерам приложения, его можно переопределить атрибутомIgnoreAntiforgeryToken.

Автоматическая проверка маркеров защиты от подделки только для небезопасных методов HTTP

Вместо широкого применения атрибута ValidateAntiForgeryToken и переопределения его атрибутами IgnoreAntiforgeryTokenможно использовать атрибут AutoValidateAntiforgeryToken . Этот атрибут работает идентично атрибуту ValidateAntiForgeryToken , за исключением того, что он не требует маркеров для запросов, выполненных с помощью следующих методов HTTP:

  • GET
  • HEAD
  • ПАРАМЕТРЫ
  • ТРАССИРОВКА

Рекомендуется использовать AutoValidateAntiforgeryToken широко для сценариев, отличных от API. Этот атрибут гарантирует, что действия POST защищены по умолчанию. Альтернативой является игнорировать маркеры антифоргерии по умолчанию, если ValidateAntiForgeryToken к отдельным методам действия не применяется. Скорее всего, в этом сценарии метод действия POST остается незащищенным по ошибке, оставляя приложение уязвимым к атакам CSRF. Все POS-адреса должны отправлять маркер антифоргерии.

Api не имеют автоматического механизма отправки токена, отличногоcookie от части маркера. Реализация, вероятно, зависит от реализации клиентского кода. Ниже показаны некоторые примеры:

Пример уровня класса:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Глобальный пример:

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

Переопределение глобальных или антифоргерийных атрибутов контроллера

Фильтр IgnoreAntiforgeryToken используется для устранения необходимости в маркере защиты от определенного действия (или контроллера). При применении этот фильтр переопределяет и AutoValidateAntiforgeryToken фильтруетValidateAntiForgeryToken, указанные на более высоком уровне (глобально или на контроллере).

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

    return RedirectToAction();
}

Обновление маркеров после проверки подлинности

Маркеры должны обновляться после проверки подлинности пользователя, перенаправляя пользователя на страницу представления или Razor страницы.

JavaScript, AJAX и spAs

В традиционных приложениях на основе HTML маркеры антифоргерии передаются серверу с помощью скрытых полей формы. В современных приложениях и spAs на основе JavaScript многие запросы выполняются программными средствами. Эти запросы AJAX могут использовать другие методы (например, заголовки запросов или cookies) для отправки маркера.

Если cookieиспользуются для хранения маркеров проверки подлинности и проверки подлинности запросов API на сервере, CSRF является потенциальной проблемой. Если локальное хранилище используется для хранения маркера, уязвимость CSRF может быть устранена, так как значения из локального хранилища не отправляются автоматически на сервер с каждым запросом. Использование локального хранилища для хранения маркера защиты от подделки на клиенте и отправка маркера в качестве заголовка запроса рекомендуется.

JavaScript

С помощью JavaScript с представлениями маркер можно создать с помощью службы в представлении. IAntiforgery Внедрение службы в представление и вызов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>
}

В предыдущем примере используется JavaScript для чтения значения скрытого поля для заголовка AJAX POST.

Этот подход устраняет необходимость непосредственного решения параметров cookieс сервера или считывания их из клиента. Однако при внедрении IAntiforgery службы используйте JavaScript для доступа к маркерам в cookieследующих случаях:

  • Маркеры доступа в дополнительном запросе к серверу обычно same-origin.
  • Используйте содержимое cookie, чтобы создать заголовок со значением маркера.

Если скрипт отправляет маркер в заголовке запроса X-XSRF-TOKEN, настройте службу защиты от подделки для поиска заголовка X-XSRF-TOKEN :

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

В следующем примере добавляется защищенная конечная точка, которая записывает маркер запроса в доступный cookieдля чтения 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();

В следующем примере JavaScript используется для выполнения запроса AJAX для получения маркера и выполнения другого запроса с соответствующим заголовком:

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

Примечание.

Если маркер антифоргерии указан как в заголовке запроса, так и в полезных данных формы, проверяется только маркер в заголовке.

Антифоргерия с минимальными API

Minimal APIs не поддерживает использование включенных фильтров (ValidateAntiForgeryToken, AutoValidateAntiforgeryToken, IgnoreAntiforgeryTokenоднако), IAntiforgery предоставляет необходимые API для проверки запроса.

В следующем примере создается фильтр, который проверяет маркер антифоргерии:

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

        });
    }
}

Затем фильтр можно применить к конечной точке:

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

проверка подлинности Windows и антифоргерии cookie

При использовании проверки подлинности Windows конечные точки приложений должны быть защищены от атак CSRF так же, как и для cookies. Браузер неявно отправляет контекст проверки подлинности серверу и конечным точкам, которые необходимо защитить от атак CSRF.

Расширение антифоргерии

Тип IAntiforgeryAdditionalDataProvider позволяет разработчикам расширить поведение системы защиты от CSRF путем обхода дополнительных данных в каждом токене. Метод GetAdditionalData вызывается каждый раз при создании маркера поля, а возвращаемое значение внедрено в созданный маркер. Реализующий может возвращать метку времени, nonce или любое другое значение, а затем вызывать ValidateAdditionalData проверку этих данных при проверке маркера. Имя пользователя клиента уже внедрено в созданные маркеры, поэтому не нужно включать эти сведения. Если маркер содержит дополнительные данные, но не IAntiForgeryAdditionalDataProvider настроен, дополнительные данные не проверяются.

Дополнительные ресурсы

Межсайтовые подделки запросов (также известные как XSRF или CSRF) — это атака на веб-размещенные веб-приложения, в которых вредоносное веб-приложение может влиять на взаимодействие между клиентским браузером и веб-приложением, которое доверяет браузеру. Эти атаки возможны, так как веб-браузеры автоматически отправляют некоторые типы маркеров проверки подлинности с каждым запросом на веб-сайт. Эта форма эксплойта также называется атакой одного щелчка или ездой на сеансе, так как атака использует ранее прошедший проверку подлинности сеанс пользователя.

Пример атаки CSRF:

  1. Пользователь входит в www.good-banking-site.example.com систему с помощью проверки подлинности форм. Сервер выполняет проверку подлинности пользователя и выдает ответ, содержащий проверку подлинности cookie. Сайт уязвим для атаки, так как он доверяет любому запросу, который он получает с допустимой проверкой подлинности cookie.

  2. Пользователь посещает вредоносный сайт. www.bad-crook-site.example.com

    Вредоносный сайт www.bad-crook-site.example.comсодержит HTML-форму, аналогичную следующему примеру:

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

    Обратите внимание, что записи формы action на уязвимый сайт, а не на вредоносный сайт. Это "кросс-сайт" части CSRF.

  3. Пользователь выбирает кнопку отправки. Браузер выполняет запрос и автоматически включает проверку подлинности cookie для запрошенного домена www.good-banking-site.example.com.

  4. Запрос выполняется на сервере www.good-banking-site.example.com с контекстом проверки подлинности пользователя и может выполнять любое действие, которое разрешено выполнить пользователю, прошедшему проверку подлинности.

Помимо сценария, когда пользователь выбирает кнопку для отправки формы, вредоносный сайт может:

  • Запустите скрипт, который автоматически отправляет форму.
  • Отправьте отправку формы в виде запроса AJAX.
  • Скрытие формы с помощью CSS.

Эти альтернативные сценарии не требуют никаких действий или входных данных от пользователя, кроме первоначального посещения вредоносного сайта.

Использование HTTPS не предотвращает атаку CSRF. Вредоносный https://www.good-banking-site.com/ сайт может отправлять запрос так же легко, как он может отправлять небезопасный запрос.

Некоторые атаки предназначены для конечных точек, реагирующих на запросы GET, в этом случае тег изображения можно использовать для выполнения действия. Эта форма атаки распространена на сайтах форума, которые разрешают изображения, но блокируют JavaScript. Приложения, изменяющие состояние запросов GET, в которых переменные или ресурсы изменяются, уязвимы для вредоносных атак. Запросы GET, изменяющие состояние, небезопасны. Рекомендуется никогда не изменять состояние запроса GET.

Атаки CSRF возможны для веб-приложений, которые используются cookieдля проверки подлинности, так как:

  • Браузеры хранят cookieприложения, выданные веб-приложением.
  • Сохраненные cookieсеансы включают сеансы cookieдля прошедших проверку подлинности пользователей.
  • Браузеры отправляют все cookieсвязанные с доменом домены в веб-приложение каждый запрос независимо от того, как был создан запрос к приложению в браузере.

Однако атаки CSRF не ограничиваются эксплойтами cookie. Например, обычная и дайджест-проверка подлинности также уязвимы. После входа пользователя с помощью базовой или дайджест-проверки подлинности браузер автоматически отправляет учетные данные до окончания сеанса.

В этом контексте сеанс ссылается на сеанс на стороне клиента, в течение которого пользователь проходит проверку подлинности. Он не связан с сеансами на стороне сервера или по промежуточному слоям основных сеансов ASP.NET.

Пользователи могут защищать уязвимости CSRF, принимая меры предосторожности:

  • Выйдите из веб-приложений после завершения работы с ними.
  • Периодически очищать браузер cookie.

Однако уязвимости CSRF являются основной проблемой веб-приложения, а не конечным пользователем.

Основы проверки подлинности

CookieПроверка подлинности на основе — это популярная форма проверки подлинности. Системы проверки подлинности на основе маркеров растут в популярности, особенно для одностраничных приложений (SPAs).

Когда пользователь проходит проверку подлинности с помощью имени пользователя и пароля, он выдает маркер, содержащий билет проверки подлинности, который можно использовать для проверки подлинности и авторизации. Маркер хранится в виде отправленного cookie с каждым запросом клиента. Создание и проверка этого cookie выполняется ПО промежуточного Cookie слоя проверки подлинности. ПО промежуточного слоя сериализует субъект-пользователя в зашифрованный cookie. В последующих запросах ПО промежуточного слоя проверяет cookie, повторно создает субъект и назначает субъекту свойству HttpContext.User .

Проверка подлинности на основе токенов

Когда пользователь проходит проверку подлинности, он выдает маркер (а не маркер антифоргерии). Маркер содержит сведения о пользователях в виде утверждений или маркера ссылки, который указывает приложению на состояние пользователя, поддерживаемое в приложении. Когда пользователь пытается получить доступ к ресурсу, требующий проверки подлинности, маркер отправляется приложению с дополнительным заголовком авторизации в виде маркера носителя. Такой подход делает приложение без отслеживания состояния. В каждом последующем запросе маркер передается в запросе на проверку на стороне сервера. Этот маркер не шифруется; он закодирован. На сервере маркер декодируется для доступа к его информации. Чтобы отправить маркер на последующие запросы, сохраните маркер в локальном хранилище браузера. Не беспокойтесь об уязвимости CSRF, если маркер хранится в локальном хранилище браузера. CSRF является проблемой, когда маркер хранится в объекте cookie. Дополнительные сведения см. в примере cookieкода SPA проблемы GitHub.

Несколько приложений, размещенных в одном домене

Среды общего размещения уязвимы для перехвата сеанса, входа CSRF и других атак.

Хотя example1.contoso.net и example2.contoso.net являются разными узлами, существует неявная связь доверия между узлами в домене *.contoso.net . Эта неявная связь доверия позволяет потенциально ненадежным узлам влиять друг на друга cookie(политики того же источника, которые управляют запросами AJAX, не обязательно применяются к HTTP-адресам cookie).

Атаки, которые используют доверенные cookies между приложениями, размещенными в одном домене, можно предотвратить, не предоставляя общий доступ к доменам. Если каждое приложение размещено в собственном домене, не существует неявных cookie отношений доверия для эксплойтов.

Антифоргерия в ASP.NET Core

Предупреждение

ASP.NET Core реализует антифоргерию с помощью ASP.NET Core Data Protection. Стек защиты данных должен быть настроен для работы в ферме серверов. Дополнительные сведения см. в разделе "Настройка защиты данных".

По промежуточному слоям защиты добавляется в контейнер внедрения зависимостей при вызове одного из следующих API:Program.cs

FormTagHelper вставляет маркеры защиты от подделки в элементы HTML-форм. Следующая разметка в Razor файле автоматически создает маркеры антифоргерии:

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

Аналогичным образом создается маркеры антифоргерии по умолчанию, IHtmlHelper.BeginForm если метод формы не является GET.

Автоматическое создание маркеров антифоргерии для элементов формы HTML происходит, когда <form> тег содержит method="post" атрибут, и любой из следующих значений имеет значение true:

  • Атрибут действия пуст (action="").
  • Атрибут действия не предоставляется (<form method="post">).

Автоматическое создание маркеров антифоргерии для элементов формы HTML можно отключить:

  • Явным образом отключите маркеры защиты от атрибута asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • Элемент формы отказано в вспомогательных функциях тегов с помощью символа отказа от тега!

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • Удалите его FormTagHelper из представления. Его FormTagHelper можно удалить из представления, добавив следующую директиву в Razor представление:

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

Примечание.

Razor Страницы автоматически защищены от XSRF/CSRF. Дополнительные сведения см. в разделе XSRF/CSRF и Razor Pages.

Наиболее распространенный подход к защите от атак CSRF — использовать шаблон токена синхронизатора (STP). STP используется, когда пользователь запрашивает страницу с данными формы:

  1. Сервер отправляет маркер, связанный с удостоверением текущего пользователя клиенту.
  2. Клиент отправляет маркер на сервер для проверки.
  3. Если сервер получает маркер, который не соответствует удостоверению пользователя, прошедшего проверку подлинности, запрос отклоняется.

Маркер является уникальным и непредсказуемым. Маркер также можно использовать для обеспечения правильной последовательности запросов (например, обеспечения последовательности запросов: страницы 1 > страницы 2 > страницы 3). Все формы в ASP.NET шаблонах Core MVC и Razor Pages создают маркеры защиты от подделки. Следующая пара примеров представления создает маркеры антифоргерии:

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

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

Явным образом добавьте маркер антифоргерии в <form> элемент без использования вспомогательных средств тегов с вспомогательным элементом @Html.AntiForgeryTokenHTML:

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

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

В каждом из предыдущих случаев ASP.NET Core добавляет скрытое поле формы, аналогичное следующему примеру:

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

ASP.NET Core включает три фильтра для работы с маркерами защиты от подделки:

Антифоргерия с помощью AddControllers

Вызов AddControllers не включает маркеры защиты от подделки. AddControllersWithViews Необходимо вызвать встроенную поддержку маркеров защиты от подделки.

Несколько вкладок браузера и шаблон токена синхронизатора

С помощью шаблона маркера синхронизатора только последняя загруженная страница содержит допустимый маркер антифоргерии. Использование нескольких вкладок может быть проблематичным. Например, если пользователь открывает несколько вкладок:

  • Только последняя загруженная вкладка содержит допустимый маркер антифоргерии.
  • Запросы, сделанные из ранее загруженных вкладок, завершаются ошибкой: Antiforgery token validation failed. The antiforgery cookie token and request token do not match

Если это представляет проблему, рассмотрите альтернативные шаблоны защиты CSRF.

Настройка антифоргерии с помощью AntiforgeryOptions

Настройка AntiforgeryOptions в Program.cs:

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

Задайте свойства антифоргерии Cookie с помощью свойств CookieBuilder класса, как показано в следующей таблице.

Вариант Описание
Cookie Определяет параметры, используемые для создания антифоргерийных cookies.
FormFieldName Имя скрытого поля формы, используемого системой антифоргерии для отображения маркеров антифоргерии в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

Дополнительные сведения см. в разделе CookieAuthenticationOptions.

Создание маркеров защиты с помощью IAntiforgery

IAntiforgery предоставляет API для настройки функций антифоргерии. IAntiforgery его можно запросить Program.cs с помощью WebApplication.Services. В следующем примере по промежуточному слоям на домашней странице приложения создается маркер антифоргерии и отправляет его в ответ в виде 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);
});

В предыдущем примере задается именованный cookieXSRF-TOKEN. Клиент может прочитать это cookie и указать его значение в качестве заголовка, присоединенного к запросам AJAX. Например, Angular включает встроенную защиту XSRF, которая считывает именованный cookieXSRF-TOKEN по умолчанию.

Требовать проверку антифоргерии

Фильтр действий ValidateAntiForgeryToken можно применить к отдельному действию, контроллеру или глобально. Запросы, сделанные к действиям, которые применяются с этим фильтром, блокируются, если запрос не содержит допустимый маркер антифоргерии:

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

    return RedirectToAction();
}

Атрибуту ValidateAntiForgeryToken требуется маркер для запросов к методам действия, которые он помечает, включая HTTP-запросы GET. ValidateAntiForgeryToken Если атрибут применяется к контроллерам приложения, его можно переопределить атрибутомIgnoreAntiforgeryToken.

Автоматическая проверка маркеров защиты от подделки только для небезопасных методов HTTP

Вместо широкого применения атрибута ValidateAntiForgeryToken и переопределения его атрибутами IgnoreAntiforgeryTokenможно использовать атрибут AutoValidateAntiforgeryToken . Этот атрибут работает идентично атрибуту ValidateAntiForgeryToken , за исключением того, что он не требует маркеров для запросов, выполненных с помощью следующих методов HTTP:

  • GET
  • HEAD
  • ПАРАМЕТРЫ
  • ТРАССИРОВКА

Рекомендуется использовать AutoValidateAntiforgeryToken широко для сценариев, отличных от API. Этот атрибут гарантирует, что действия POST защищены по умолчанию. Альтернативой является игнорировать маркеры антифоргерии по умолчанию, если ValidateAntiForgeryToken к отдельным методам действия не применяется. Скорее всего, в этом сценарии метод действия POST остается незащищенным по ошибке, оставляя приложение уязвимым к атакам CSRF. Все POS-адреса должны отправлять маркер антифоргерии.

Api не имеют автоматического механизма отправки токена, отличногоcookie от части маркера. Реализация, вероятно, зависит от реализации клиентского кода. Ниже показаны некоторые примеры:

Пример уровня класса:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

Глобальный пример:

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

Переопределение глобальных или антифоргерийных атрибутов контроллера

Фильтр IgnoreAntiforgeryToken используется для устранения необходимости в маркере защиты от определенного действия (или контроллера). При применении этот фильтр переопределяет и AutoValidateAntiforgeryToken фильтруетValidateAntiForgeryToken, указанные на более высоком уровне (глобально или на контроллере).

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

    return RedirectToAction();
}

Обновление маркеров после проверки подлинности

Маркеры должны обновляться после проверки подлинности пользователя, перенаправляя пользователя на страницу представления или Razor страницы.

JavaScript, AJAX и spAs

В традиционных приложениях на основе HTML маркеры антифоргерии передаются серверу с помощью скрытых полей формы. В современных приложениях и spAs на основе JavaScript многие запросы выполняются программными средствами. Эти запросы AJAX могут использовать другие методы (например, заголовки запросов или cookies) для отправки маркера.

Если cookieиспользуются для хранения маркеров проверки подлинности и проверки подлинности запросов API на сервере, CSRF является потенциальной проблемой. Если локальное хранилище используется для хранения маркера, уязвимость CSRF может быть устранена, так как значения из локального хранилища не отправляются автоматически на сервер с каждым запросом. Использование локального хранилища для хранения маркера защиты от подделки на клиенте и отправка маркера в качестве заголовка запроса рекомендуется.

JavaScript

С помощью JavaScript с представлениями маркер можно создать с помощью службы в представлении. IAntiforgery Внедрение службы в представление и вызов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>
}

В предыдущем примере используется JavaScript для чтения значения скрытого поля для заголовка AJAX POST.

Этот подход устраняет необходимость непосредственного решения параметров cookieс сервера или считывания их из клиента. Однако при внедрении IAntiforgery службы JavaScript также может получить доступ к маркеру в cookieS, полученном из дополнительного запроса к серверу (обычно same-origin), и использовать cookieсодержимое файла для создания заголовка со значением маркера.

Если скрипт отправляет маркер в заголовке запроса X-XSRF-TOKEN, настройте службу защиты от подделки для поиска заголовка X-XSRF-TOKEN :

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

В следующем примере добавляется защищенная конечная точка, которая будет записывать маркер запроса в доступный cookieдля чтения 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();

В следующем примере JavaScript используется для выполнения запроса AJAX для получения маркера и выполнения другого запроса с соответствующим заголовком:

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

проверка подлинности Windows и антифоргерии cookie

При использовании проверки подлинности Windows конечные точки приложений должны быть защищены от атак CSRF так же, как и для cookies. Браузер неявно отправляет контекст проверки подлинности серверу и поэтому конечные точки должны быть защищены от атак CSRF.

Расширение антифоргерии

Тип IAntiforgeryAdditionalDataProvider позволяет разработчикам расширить поведение системы защиты от CSRF путем обхода дополнительных данных в каждом токене. Метод GetAdditionalData вызывается каждый раз при создании маркера поля, а возвращаемое значение внедрено в созданный маркер. Реализующий может возвращать метку времени, nonce или любое другое значение, а затем вызывать ValidateAdditionalData проверку этих данных при проверке маркера. Имя пользователя клиента уже внедрено в созданные маркеры, поэтому не нужно включать эти сведения. Если маркер содержит дополнительные данные, но не IAntiForgeryAdditionalDataProvider настроен, дополнительные данные не проверяются.

Дополнительные ресурсы

Межсайтовые подделки запросов (также известные как XSRF или CSRF) — это атака на веб-размещенные веб-приложения, в которых вредоносное веб-приложение может влиять на взаимодействие между клиентским браузером и веб-приложением, которое доверяет браузеру. Эти атаки возможны, так как веб-браузеры автоматически отправляют некоторые типы маркеров проверки подлинности с каждым запросом на веб-сайт. Эта форма эксплойта также называется атакой одного щелчка или ездой на сеансе, так как атака использует ранее прошедший проверку подлинности сеанс пользователя.

Пример атаки CSRF:

  1. Пользователь входит в www.good-banking-site.example.com систему с помощью проверки подлинности форм. Сервер выполняет проверку подлинности пользователя и выдает ответ, содержащий проверку подлинности cookie. Сайт уязвим для атаки, так как он доверяет любому запросу, который он получает с допустимой проверкой подлинности cookie.

  2. Пользователь посещает вредоносный сайт. www.bad-crook-site.example.com

    Вредоносный сайт www.bad-crook-site.example.comсодержит HTML-форму, аналогичную следующему примеру:

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

    Обратите внимание, что записи формы action на уязвимый сайт, а не на вредоносный сайт. Это "кросс-сайт" части CSRF.

  3. Пользователь выбирает кнопку отправки. Браузер выполняет запрос и автоматически включает проверку подлинности cookie для запрошенного домена www.good-banking-site.example.com.

  4. Запрос выполняется на сервере www.good-banking-site.example.com с контекстом проверки подлинности пользователя и может выполнять любое действие, которое разрешено выполнить пользователю, прошедшему проверку подлинности.

Помимо сценария, когда пользователь выбирает кнопку для отправки формы, вредоносный сайт может:

  • Запустите скрипт, который автоматически отправляет форму.
  • Отправьте отправку формы в виде запроса AJAX.
  • Скрытие формы с помощью CSS.

Эти альтернативные сценарии не требуют никаких действий или входных данных от пользователя, кроме первоначального посещения вредоносного сайта.

Использование HTTPS не предотвращает атаку CSRF. Вредоносный https://www.good-banking-site.com/ сайт может отправлять запрос так же легко, как он может отправлять небезопасный запрос.

Некоторые атаки предназначены для конечных точек, реагирующих на запросы GET, в этом случае тег изображения можно использовать для выполнения действия. Эта форма атаки распространена на сайтах форума, которые разрешают изображения, но блокируют JavaScript. Приложения, изменяющие состояние запросов GET, в которых переменные или ресурсы изменяются, уязвимы для вредоносных атак. Запросы GET, изменяющие состояние, небезопасны. Рекомендуется никогда не изменять состояние запроса GET.

Атаки CSRF возможны для веб-приложений, которые используются cookieдля проверки подлинности, так как:

  • Браузеры хранят cookieприложения, выданные веб-приложением.
  • Сохраненные cookieсеансы включают сеансы cookieдля прошедших проверку подлинности пользователей.
  • Браузеры отправляют все cookieсвязанные с доменом домены в веб-приложение каждый запрос независимо от того, как был создан запрос к приложению в браузере.

Однако атаки CSRF не ограничиваются эксплойтами cookie. Например, обычная и дайджест-проверка подлинности также уязвимы. После входа пользователя с помощью базовой или дайджест-проверки подлинности браузер автоматически отправляет учетные данные до окончания сеанса.

В этом контексте сеанс ссылается на сеанс на стороне клиента, в течение которого пользователь проходит проверку подлинности. Он не связан с сеансами на стороне сервера или по промежуточному слоям основных сеансов ASP.NET.

Пользователи могут защищать уязвимости CSRF, принимая меры предосторожности:

  • Выйдите из веб-приложений после завершения работы с ними.
  • Периодически очищать браузер cookie.

Однако уязвимости CSRF являются основной проблемой веб-приложения, а не конечным пользователем.

Основы проверки подлинности

CookieПроверка подлинности на основе — это популярная форма проверки подлинности. Системы проверки подлинности на основе маркеров растут в популярности, особенно для одностраничных приложений (SPAs).

Когда пользователь проходит проверку подлинности с помощью имени пользователя и пароля, он выдает маркер, содержащий билет проверки подлинности, который можно использовать для проверки подлинности и авторизации. Маркер хранится в виде отправленного cookie с каждым запросом клиента. Создание и проверка этого cookie выполняется ПО промежуточного Cookie слоя проверки подлинности. ПО промежуточного слоя сериализует субъект-пользователя в зашифрованный cookie. В последующих запросах ПО промежуточного слоя проверяет cookie, повторно создает субъект и назначает субъекту свойству HttpContext.User .

Проверка подлинности на основе токенов

Когда пользователь проходит проверку подлинности, он выдает маркер (а не маркер антифоргерии). Маркер содержит сведения о пользователях в виде утверждений или маркера ссылки, который указывает приложению на состояние пользователя, поддерживаемое в приложении. Когда пользователь пытается получить доступ к ресурсу, требующий проверки подлинности, маркер отправляется приложению с дополнительным заголовком авторизации в виде маркера носителя. Такой подход делает приложение без отслеживания состояния. В каждом последующем запросе маркер передается в запросе на проверку на стороне сервера. Этот маркер не шифруется; он закодирован. На сервере маркер декодируется для доступа к его информации. Чтобы отправить маркер на последующие запросы, сохраните маркер в локальном хранилище браузера. Не беспокойтесь об уязвимости CSRF, если маркер хранится в локальном хранилище браузера. CSRF является проблемой, когда маркер хранится в объекте cookie. Дополнительные сведения см. в примере cookieкода SPA проблемы GitHub.

Несколько приложений, размещенных в одном домене

Среды общего размещения уязвимы для перехвата сеанса, входа CSRF и других атак.

Хотя example1.contoso.net и example2.contoso.net являются разными узлами, существует неявная связь доверия между узлами в домене *.contoso.net . Эта неявная связь доверия позволяет потенциально ненадежным узлам влиять друг на друга cookie(политики того же источника, которые управляют запросами AJAX, не обязательно применяются к HTTP-адресам cookie).

Атаки, которые используют доверенные cookies между приложениями, размещенными в одном домене, можно предотвратить, не предоставляя общий доступ к доменам. Если каждое приложение размещено в собственном домене, не существует неявных cookie отношений доверия для эксплойтов.

конфигурация защиты от ASP.NET Core

Предупреждение

ASP.NET Core реализует антифоргерию с помощью ASP.NET Core Data Protection. Стек защиты данных должен быть настроен для работы в ферме серверов. Дополнительные сведения см. в разделе "Настройка защиты данных".

По промежуточному слоям защиты добавляется в контейнер внедрения зависимостей при вызове одного из следующих API:Startup.ConfigureServices

В ASP.NET Core 2.0 или более поздней версии FormTagHelper внедряет маркеры антифоргерии в элементы формы HTML. Следующая разметка в Razor файле автоматически создает маркеры антифоргерии:

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

Аналогичным образом создается маркеры антифоргерии по умолчанию, IHtmlHelper.BeginForm если метод формы не является GET.

Автоматическое создание маркеров антифоргерии для элементов формы HTML происходит, когда <form> тег содержит method="post" атрибут, и любой из следующих значений имеет значение true:

  • Атрибут действия пуст (action="").
  • Атрибут действия не предоставляется (<form method="post">).

Автоматическое создание маркеров антифоргерии для элементов формы HTML можно отключить:

  • Явным образом отключите маркеры защиты от атрибута asp-antiforgery :

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • Элемент формы отказано в вспомогательных функциях тегов с помощью символа отказа от тега!

    <!form method="post">
        ...
    </!form>
    
  • Удалите его FormTagHelper из представления. Его FormTagHelper можно удалить из представления, добавив следующую директиву в Razor представление:

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

Примечание.

Razor Страницы автоматически защищены от XSRF/CSRF. Дополнительные сведения см. в разделе XSRF/CSRF и Razor Pages.

Наиболее распространенный подход к защите от атак CSRF — использовать шаблон токена синхронизатора (STP). STP используется, когда пользователь запрашивает страницу с данными формы:

  1. Сервер отправляет маркер, связанный с удостоверением текущего пользователя клиенту.
  2. Клиент отправляет маркер на сервер для проверки.
  3. Если сервер получает маркер, который не соответствует удостоверению пользователя, прошедшего проверку подлинности, запрос отклоняется.

Маркер является уникальным и непредсказуемым. Маркер также можно использовать для обеспечения правильной последовательности запросов (например, обеспечения последовательности запросов: страницы 1 > страницы 2 > страницы 3). Все формы в ASP.NET шаблонах Core MVC и Razor Pages создают маркеры защиты от подделки. Следующая пара примеров представления создает маркеры антифоргерии:

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

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

Явным образом добавьте маркер антифоргерии в <form> элемент без использования вспомогательных средств тегов с вспомогательным элементом @Html.AntiForgeryTokenHTML:

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

В каждом из предыдущих случаев ASP.NET Core добавляет скрытое поле формы, аналогичное следующему примеру:

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

ASP.NET Core включает три фильтра для работы с маркерами защиты от подделки:

Параметры антифоргерии

Настройка AntiforgeryOptions в Startup.ConfigureServices:

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

Задайте свойства антифоргерии Cookie с помощью свойств CookieBuilder класса, как показано в следующей таблице.

Вариант Описание
Cookie Определяет параметры, используемые для создания антифоргерийных cookies.
FormFieldName Имя скрытого поля формы, используемого системой антифоргерии для отображения маркеров антифоргерии в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

Дополнительные сведения см. в разделе CookieAuthenticationOptions.

Настройка функций защиты с помощью IAntiforgery

IAntiforgery предоставляет API для настройки функций антифоргерии. IAntiforgery можно запросить в Configure методе Startup класса.

В следующем примере :

  • ПО промежуточного слоя на домашней странице приложения используется для создания маркера защиты от подделки и отправки его в ответ в виде cookie.
  • Маркер запроса отправляется как доступный cookie для чтения JavaScript с соглашением об именовании Angular по умолчанию, описанным в разделе 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);
    });
}

Требовать проверку антифоргерии

ValidateAntiForgeryToken — это фильтр действий, который можно применить к отдельному действию, контроллеру или глобально. Запросы к действиям, которые применяются к этому фильтру, блокируются, если запрос не содержит допустимый маркер антифоргерии.

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

Атрибуту ValidateAntiForgeryToken требуется маркер для запросов к методам действия, которые он помечает, включая HTTP-запросы GET. ValidateAntiForgeryToken Если атрибут применяется к контроллерам приложения, его можно переопределить атрибутомIgnoreAntiforgeryToken.

Примечание.

ASP.NET Core не поддерживает автоматическое добавление маркеров защиты от подделки в запросы GET.

Автоматическая проверка маркеров защиты от подделки только для небезопасных методов HTTP

ASP.NET приложения Core не создают маркеры защиты от подделки для безопасных методов HTTP (GET, HEAD, OPTIONS и TRACE). Вместо широкого применения атрибута ValidateAntiForgeryToken и переопределения его атрибутами IgnoreAntiforgeryTokenможно использовать атрибут AutoValidateAntiforgeryToken . Этот атрибут работает идентично атрибуту ValidateAntiForgeryToken , за исключением того, что он не требует маркеров для запросов, выполненных с помощью следующих методов HTTP:

  • GET
  • HEAD
  • ПАРАМЕТРЫ
  • ТРАССИРОВКА

Рекомендуется использовать AutoValidateAntiforgeryToken широко для сценариев, отличных от API. Этот атрибут гарантирует, что действия POST защищены по умолчанию. Альтернативой является игнорировать маркеры антифоргерии по умолчанию, если ValidateAntiForgeryToken к отдельным методам действия не применяется. Скорее всего, в этом сценарии метод действия POST остается незащищенным по ошибке, оставляя приложение уязвимым к атакам CSRF. Все POS-адреса должны отправлять маркер антифоргерии.

Api не имеют автоматического механизма отправки токена, отличногоcookie от части маркера. Реализация, вероятно, зависит от реализации клиентского кода. Ниже показаны некоторые примеры:

Пример уровня класса:

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

Глобальный пример:

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

Переопределение глобальных или антифоргерийных атрибутов контроллера

Фильтр IgnoreAntiforgeryToken используется для устранения необходимости в маркере защиты от определенного действия (или контроллера). При применении этот фильтр переопределяет и AutoValidateAntiforgeryToken фильтруетValidateAntiForgeryToken, указанные на более высоком уровне (глобально или на контроллере).

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

Обновление маркеров после проверки подлинности

Маркеры должны обновляться после проверки подлинности пользователя, перенаправляя пользователя на страницу представления или Razor страницы.

JavaScript, AJAX и spAs

В традиционных приложениях на основе HTML маркеры антифоргерии передаются серверу с помощью скрытых полей формы. В современных приложениях и spAs на основе JavaScript многие запросы выполняются программными средствами. Эти запросы AJAX могут использовать другие методы (например, заголовки запросов или cookies) для отправки маркера.

Если cookieиспользуются для хранения маркеров проверки подлинности и проверки подлинности запросов API на сервере, CSRF является потенциальной проблемой. Если локальное хранилище используется для хранения маркера, уязвимость CSRF может быть устранена, так как значения из локального хранилища не отправляются автоматически на сервер с каждым запросом. Использование локального хранилища для хранения маркера защиты от подделки на клиенте и отправка маркера в качестве заголовка запроса рекомендуется.

JavaScript

С помощью JavaScript с представлениями маркер можно создать с помощью службы в представлении. IAntiforgery Внедрение службы в представление и вызов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>

Этот подход устраняет необходимость непосредственного решения параметров cookieс сервера или считывания их из клиента.

В предыдущем примере используется JavaScript для чтения значения скрытого поля для заголовка AJAX POST.

JavaScript также может получить доступ к маркерам и cookieиспользовать cookieего содержимое для создания заголовка со значением маркера.

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

Если скрипт запрашивает отправку маркера в заголовке с именем X-CSRF-TOKEN, настройте службу защиты от подделки для поиска заголовка X-CSRF-TOKEN :

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

В следующем примере используется JavaScript для выполнения запроса AJAX с соответствующим заголовком:

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

УгловойJS

AngularJS использует соглашение для решения CSRF. Если сервер отправляет cookie имя XSRF-TOKEN, служба AngularJS$http добавляет cookie значение в заголовок при отправке запроса на сервер. Этот процесс автоматический. Клиенту не нужно явно задавать заголовок. Имя заголовка — X-XSRF-TOKEN. Сервер должен обнаружить этот заголовок и проверить его содержимое.

Чтобы ASP.NET Core API работал с этим соглашением в запуске приложения:

  • Настройте приложение для предоставления маркера в вызываемом объекте cookieXSRF-TOKEN.
  • Настройте службу защиты для поиска заголовка с именем X-XSRF-TOKENзаголовка, который является именем заголовка Angular по умолчанию для отправки маркера 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");
}

Примечание.

Если маркер антифоргерии указан как в заголовке запроса, так и в полезных данных формы, проверяется только маркер в заголовке.

проверка подлинности Windows и антифоргерии cookie

При использовании проверки подлинности Windows конечные точки приложений должны быть защищены от атак CSRF так же, как и для cookies. Браузер неявно отправляет контекст проверки подлинности серверу и поэтому конечные точки должны быть защищены от атак CSRF.

Расширение антифоргерии

Тип IAntiforgeryAdditionalDataProvider позволяет разработчикам расширить поведение системы защиты от CSRF путем обхода дополнительных данных в каждом токене. Метод GetAdditionalData вызывается каждый раз при создании маркера поля, а возвращаемое значение внедрено в созданный маркер. Реализующий может возвращать метку времени, nonce или любое другое значение, а затем вызывать ValidateAdditionalData проверку этих данных при проверке маркера. Имя пользователя клиента уже внедрено в созданные маркеры, поэтому не нужно включать эти сведения. Если маркер содержит дополнительные данные, но не IAntiForgeryAdditionalDataProvider настроен, дополнительные данные не проверяются.

Дополнительные ресурсы