Захист від CSRF атак в ASP.NET Core додатку »Блог Віталія Лещенко
У цьому пості я хочу розповісти про те, що таке CSRF (Cross-Site Request Forgery) атака і як від неї захиститися.
акт 1
Уявімо собі сайт інтернет банкінгу якогось вигаданого банку. Клієнт цього банку цілком легальним чином логинится на сайті, виконує ряд операцій і закриває сторінку Не зробивши вихід. Після цієї операції в браузері клієнта залишилася кука.
акт 2
Тепер спробуємо зрозуміти, що сталося.
Оскільки запит відправляється на інший домен, і ми не розглядаємо ситуацію XSS ін'єкції на сайті інтернет банкінгу, то браузер, за замовчуванням (якщо не налаштований CORS), не дасть прочитати відповідь від сервера. А нашим шахраям це по суті і не треба. Важливо, що сам запит був відправлений на сервер і оброблений. Тобто відбулося списання грошових коштів. Атаку можна вважати успішно виконаною.
акт 3
Перейдемо до захисту.
Для захисту від цієї атаки використовується 2 AntiForgery-токена. Один відправляється на клієнт у вигляді куки, а другий в прихованому полі в формі. Коли клієнт самостійно виконує запит на сайті інтернет банкінгу, то на сервер йдуть 2 токена. Один у формі або в заголовку, другий в Кука. Сервер перевіряє їх валідність і з разі недоброго повертає відповідь зі статусом 400.
Для початку змінимо клас Startup
public class Startup {public void ConfigureServices (IServiceCollection services) {// ... services.AddMvc (options => {options.Filters.Add (new AutoValidateAntiforgeryTokenAttribute ());}); }}ASP.NET MVC за замовчуванням в кожну форму додає приховане поле такого виду:
<Input name = "__ RequestVerificationToken" type = "hidden" value = "CfEJ6IxjsTbAPh1AhIp ......... 5PEbMY4s-2ALCzGETVQe6VixWhh3WMMXzVSXJ3w">і створює куку з ім'ям виду .AspNetCore.Antiforgery.leVP-PGSx2U
Цього достатньо для захисту форм створених ASP.NET MVC Core. А як бути з AJAX запитами?
Все просто. Додамо в файл _Layout.cshtml такий шматок коду:
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf @functions {public string GetAntiXsrfRequestToken () {return Xsrf.GetAndStoreTokens (Context) .RequestToken; }} <Meta name = "requests-validation-token" content = "@ GetAntiXsrfRequestToken ()" />Тепер модифікуємо скрипт відправки запиту на сервер:
function getRequestsValidationToken (): string {const key = "requests-validation-token"; const elements = document.getElementsByTagName ( "meta"); for (let i = 0; i <elements.length; i ++) {const element = elements.item (i); if (element.name == key) {return element.content; }} Return null; } Const token = getRequestsValidationToken (); if (token == null) {throw new Error ( "Can not read requests validation token"); } $ .Ajax ({type: 'POST', url: url, headers: { "RequestVerificationToken": token,} // OR // beforeSend: function (xhr) {// xhr.setRequestHeader ( "RequestVerificationToken", token) ; //}}). done (function (data) {alert (data);});Уважний читач зараз може задати питання: якщо сам токен знаходиться в тілі сторінки, то, що заважає зловмисникам зробити GET запит нашої сторінки і прочитати цей токен? Може, але браузер скаже: Failed to load https://www.vitaliy.org/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://hacker-site.com' is therefore not allowed access.
Якщо сайт працює на декількох серверах (в фермі або в докері), то для того, щоб один сервер зміг розшифрувати токен, створений іншим сервером, необхоідмо налаштувати DataProtection. У моєму випадку це зроблено за допомогою шаринга ключа шіфорванія в Redis .
using Microsoft.AspNetCore.DataProtection; public void ConfigureServices (IServiceCollection services) {var redis = ConnectionMultiplexer.Connect (Configuration.GetConnectionString ( "RedisConnection")); services.AddDataProtection (). PersistKeysToRedis (redis, Configuration [ "Services: Redis: Key"]); }PS: що ще треба зробити? налаштувати Content-Security-Policy і боротися з XSS. Але, це вже інша історія ...
А як бути з AJAX запитами?