Поделиться через


Предотвращение атак с межсайтовой подделкой запросов (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://www.good-banking-site.example.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click to collect your prize!" />
    </form>
    

    Обратите внимание, что отправка формы 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.example.com/ запрос так же легко, как он может отправлять небезопасный запрос.

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

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

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

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

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

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

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

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

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

Аутентификация на основе Cookie является популярной формой проверки подлинности. Системы аутентификации на основе токенов становятся всё более популярными, особенно для одностраничных приложений (SPA).

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

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

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

Не беспокойтесь об уязвимости CSRF, если маркер хранится в локальном хранилище браузера. CSRF является проблемой, когда токен хранится в cookie. Для получения дополнительной информации см. проблему GitHub SPA code sample adds two cookies.

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

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

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

Атаки, которые используют доверенные файлы cookie между приложениями, размещенными в одном домене, можно предотвратить, не предоставляя общий доступ к доменам. Если каждое приложение размещено в собственном домене, не существует неявных 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" атрибут, и выполняется любое из следующих условий:

  • Атрибут действия пуст (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-вспомогателем @Html.AntiForgeryToken.

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

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

Несколько вкладок, вошедших в систему как разные пользователи, или одна, вошедшая в систему как анонимный пользователь, не поддерживаются.

Настройка защиты от подделки с помощью 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 Определяет параметры, используемые для создания файлов cookie антивзломной защиты.
FormFieldName Имя скрытого поля формы, используемого системой антифоргерии для отображения маркеров антифоргерии в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

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

Генерация маркеров защиты от подделки с помощью IAntiforgery

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

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

Требовать антифальсификационной проверки

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

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

    return RedirectToAction();
}

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

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

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

  • ПОЛУЧИТЬ
  • Заголовок
  • ПАРАМЕТРЫ
  • ТРАССИРОВКА

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

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

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

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

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

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

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

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

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

    return RedirectToAction();
}

Обновление токенов после аутентификации

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

JavaScript, AJAX и SPA

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

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

Blazor

Для получения дополнительной информации см. раздел ASP.NET Core Blazor аутентификация и авторизация.

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

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

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

  • Конечная точка содержит метаданные, реализующие IAntiforgeryMetadata, где RequiresValidation=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-запрос к:

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

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

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

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

файлы cookie аутентификации Windows и защиты от подделки

При использовании проверки подлинности Windows конечные точки приложений должны быть защищены от атак CSRF так же, как и для файлов cookie. Браузер неявно отправляет контекст проверки подлинности серверу и конечным точкам, которые необходимо защитить от атак 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://www.good-banking-site.example.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click to collect your prize!" />
    </form>
    

    Обратите внимание, что отправка данных формы 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.example.com/ запрос так же легко, как он может отправлять небезопасный запрос.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Защита от подделок в ASP.NET Core

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

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

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

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

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

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

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

  • Атрибут действия пуст (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-хелпера @Html.AntiForgeryToken.

<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 Определяет параметры, используемые для создания антимошеннических файлов cookie.
FormFieldName Имя скрытого поля формы, используемого системой антиподделки для отображения токенов антиподделки в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

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

Генерация токенов защиты от подделки с помощью IAntiforgery

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

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

Требовать антифальсификационную проверку

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

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

    return RedirectToAction();
}

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

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

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

  • GET
  • ЗАГОЛОВОК
  • ПАРАМЕТРЫ
  • ТРАССИРОВКА

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

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

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

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

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

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

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

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

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

    return RedirectToAction();
}

Обновление токенов после аутентификации

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

JavaScript, AJAX и СПА

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

Если файлы 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");

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

файлы cookie аутентификации Windows и антифальсификации

При использовании проверки подлинности Windows конечные точки приложений должны быть защищены от атак CSRF так же, как и для файлов cookie. Браузер неявно отправляет контекст проверки подлинности серверу и конечным точкам, которые необходимо защитить от атак 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://www.good-banking-site.example.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click to collect your prize!" />
    </form>
    

    Обратите внимание, что отправка формы 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.example.com/ запрос так же легко, как он может отправлять небезопасный запрос.

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

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

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

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

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

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

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

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

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

Cookie-базирующаяся аутентификация является популярной формой аутентификации. Системы аутентификации на основе токенов становятся всё более популярными, особенно для одностраничных приложений.

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

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

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

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

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

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

Атаки, которые используют доверенные файлы cookie между приложениями, размещенными в одном домене, можно предотвратить, не предоставляя общий доступ к доменам. Если каждое приложение размещено в собственном домене, не существует неявных 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 метода @Html.AntiForgeryToken.

<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 Определяет параметры, используемые для создания антифальсификационных файлов cookie.
FormFieldName Имя скрытого поля формы, используемого системой защиты от подделок для отображения токенов защиты от подделок в представлениях.
HeaderName Имя заголовка, используемого антифоргерской системой. Если nullсистема рассматривает только данные формы.
SuppressXFrameOptionsHeader Указывает, следует ли подавлять создание заголовка X-Frame-Options . По умолчанию заголовок создается со значением "SAMEORIGIN". По умолчанию — false.

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

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

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

В предыдущем примере задается cookie, который называется XSRF-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:

  • ПОЛУЧИТЬ
  • Заголовок
  • ОПЦИИ
  • ТРАССИРОВКА

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

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

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

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

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

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

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

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

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

    return RedirectToAction();
}

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

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

JavaScript, AJAX и SPA

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

Если файлы 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");

В следующем примере добавляется защищенная конечная точка, которая будет записывать маркер запроса в читаемый JavaScript cookie.

app.UseAuthorization();
app.MapGet("antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
{
    var tokens = forgeryService.GetAndStoreTokens(context);
    context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!,
            new CookieOptions { HttpOnly = false });

    return Results.Ok();
}).RequireAuthorization();

В следующем примере 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 и антифальсификации

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

Расширение антифальсификации

Тип IAntiforgeryAdditionalDataProvider позволяет разработчикам расширить поведение системы защиты от CSRF путем обхода дополнительных данных в каждом токене. Метод GetAdditionalData вызывается каждый раз при создании маркера поля, а возвращаемое значение внедрено в созданный маркер. Разработчик может возвращать метку времени, одноразовый код или любое другое значение, а затем вызывать 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://www.good-banking-site.example.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click to collect your prize!" />
    </form>
    

    Обратите внимание, что форма 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.example.com/ запрос так же легко, как он может отправлять небезопасный запрос.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

конфигурация antiforgery в ASP.NET Core

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

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

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

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

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

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

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

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

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

  • Явно отключите маркеры подделки с помощью атрибута asp-antiforgery.

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • Элемент формы исключён из Tag Helpers с помощью символа отказа.

    <!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-вспомогательного элемента @Html.AntiForgeryToken.

<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 Определяет параметры, используемые для создания антифальсификационных файлов cookie.
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 используется для устранения необходимости в маркере защиты от подделки для конкретного действия (или контроллера). При применении этот фильтр переопределяет фильтры ValidateAntiForgeryToken и AutoValidateAntiforgeryToken, указанные на более высоком уровне (глобально или на контроллере).

[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 могут использовать другие методы (например, заголовки запросов или файлы cookie) для отправки маркера.

Если файлы 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#" }));

AngularJS

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

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

  • Настройте приложение для передачи токена в cookie, называемом XSRF-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");
}

Примечание.

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

файлы cookie аутентификации Windows и защиты от подделки

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

Расширение антиподделки

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

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