Чому прийнято розміщувати маркери для запобігання CSRF у файлах cookie?


284

Я намагаюся зрозуміти всю проблему з CSRF та відповідні способи її запобігання. (Ресурси, які я читав, розумію і погоджуюсь з: Ознайомлювальний лист OWASP CSRF , Запитання про КСРР .)

Як я розумію, вразливість навколо CSRF вводиться припущенням, що (з точки зору веб-сервера) дійсне cookie-сеанс у вхідному HTTP-запиті відображає побажання автентифікованого користувача. Але всі файли cookie для початкового домену магічно приєднуються до запиту браузером, тому дійсно весь сервер може зробити висновок про наявність дійсного файлу cookie сеансу у запиті, що запит надходить від браузера, який має аутентифікований сеанс; вона більше не може припускати нічого про кодпрацює в цьому веб-переглядачі, чи відображає це дійсно побажання користувача. Спосіб запобігти цьому - включити в запит додаткову інформацію про аутентифікацію ("маркер CSRF"), яку переносять іншими способами, крім автоматичної обробки файлів cookie браузера. Точно кажучи, файли cookie сеансу аутентифікують користувача / браузера, а маркер CSRF автентифікує код, що працює в браузері.

Отже, у двох словах, якщо ви використовуєте файли cookie сеансу для автентифікації користувачів вашої веб-програми, ви також повинні додати маркер CSRF до кожної відповіді та вимагати відповідного маркера CSRF у кожному запиті, що мутує. Токен CSRF потім робить зворотний перехід від сервера до браузера назад до сервера, доводячи до сервера, що сторінка, що робить запит, затверджується (генерується, навіть) цим сервером.

Що стосується мого запитання, що стосується конкретного способу транспорту, який використовується для цього маркера CSRF на цьому переході.

Здається, звичайно (наприклад, у AngularJS , Django , Rails ) надсилати маркер CSRF з сервера на клієнт у вигляді файлу cookie (тобто у заголовку Set-Cookie), а потім Javascript у клієнта викреслювати його з файлу cookie та приєднувати його як окремий заголовок XSRF-TOKEN для повернення на сервер.

(Альтернативний метод - це той, який рекомендується, наприклад, Express , де маркер CSRF, згенерований сервером, включається в тіло відповіді за допомогою розширення шаблону на стороні сервера, приєднаного безпосередньо до коду / розмітки, що надасть його назад на сервер, наприклад як прихований прийом форми. Цей приклад - це більш зручний для веб-способів ведення справ, але він узагальнить штраф для більш важкого для JS клієнта.)

Чому так часто використовується Set-Cookie як транспорт низхідного потоку для маркера CSRF / чому це гарна ідея? Я думаю, що автори всіх цих рамок уважно розглядали свої варіанти, і не зрозуміли це. Але, на перший погляд, використання файлів cookie для вирішення того, що по суті є обмеженням дизайну файлів cookie, здається непростим. Насправді, якщо ви використовували файли cookie як транспорт у зворотний бік (Set-Cookie: заголовок вниз за потоком для сервера, щоб повідомити браузеру токен CSRF, а Cookie: заголовок вгору для браузера, щоб повернути його на сервер), ви б знову ввели вразливість, яку ви намагаються виправити.

Я розумію, що вищевказані рамки не використовують файли cookie для цілого переходу для маркера CSRF; вони використовують Set-Cookie вниз за потоком, потім щось інше (наприклад, заголовок X-CSRF-Token) вгору за течією, і це повністю усуває вразливість. Але навіть використання Set-Cookie в якості транспорту нижче за течією потенційно вводить в оману та небезпечно; тепер браузер приєднає маркер CSRF до кожного запиту, включаючи справжні шкідливі запити XSRF; в кращому випадку, що робить запит більшим, ніж потрібно, а в гіршому - якийсь добронамерений, але неправильно керований фрагмент коду сервера може насправді спробувати його використати, що було б дуже погано. Крім того, оскільки фактичним призначеним одержувачем маркера CSRF є клієнтський Javascript, це означає, що цей файл cookie не може бути захищений лише http-адресою.


Це чудове запитання про потрапляння в потрібне місце.
kta

Відповіді:


263

Вагомою причиною, яку ви начебто торкнулися, є те, що після отримання файлу cookie CSRF він стає доступним для використання у всій програмі в клієнтському скрипті для використання як у звичайних формах, так і в AJAX POST. Це матиме сенс у важкому додатку JavaScript, такому, як той, який використовується у AngularJS (для використання AngularJS не потрібно, щоб програма була додатком для однієї сторінки, тому було б корисно, коли держава повинна переходити між різними запитами на сторінки, де значення CSRF не може нормально зберігатись у браузері).

Розгляньте наступні сценарії та процеси в типовому додатку для деяких плюсів і мінусів кожного описаного підходу. Вони засновані на шаблоні токена синхронізатора .

Попросити підхід до органу

  1. Користувач успішно входить у систему.
  2. Сервер видає auth cookie.
  3. Користувач клацає, щоб перейти до форми.
  4. Якщо ще не створено для цього сеансу, сервер генерує маркер CSRF, зберігає його в сеанс користувача та виводить його в приховане поле.
  5. Користувач подає форму.
  6. Сервер перевіряє приховані поля відповідності сеансу, що зберігається.

Переваги:

  • Простий у виконанні.
  • Працює з AJAX.
  • Працює з формами.
  • Файл cookie насправді може бути лише HTTP .

Недоліки:

  • Усі форми повинні виводити приховане поле в HTML.
  • Будь-які порти AJAX також повинні містити значення.
  • Сторінка повинна заздалегідь знати, що їй потрібен маркер CSRF, щоб вона могла включити її до вмісту сторінки, тому всі сторінки десь повинні містити значення токена, що може зайняти час для впровадження великого сайту.

Спеціальний заголовок HTTP (вниз)

  1. Користувач успішно входить у систему.
  2. Сервер видає auth cookie.
  3. Користувач клацає, щоб перейти до форми.
  4. Сторінка завантажується у браузері, тоді запит AJAX робиться для отримання маркера CSRF.
  5. Сервер генерує токен CSRF (якщо він ще не створений для сеансу), зберігає його у сеансі користувача та виводить його в заголовок.
  6. Користувач подає форму (маркер надсилається через приховане поле).
  7. Сервер перевіряє приховані поля відповідності сеансу, що зберігається.

Переваги:

  • Працює з AJAX.
  • Файл cookie може бути лише HTTP .

Недоліки:

  • Не працює без запиту AJAX, щоб отримати значення заголовка.
  • Усі форми мають динамічно додавати значення до його HTML.
  • Будь-які порти AJAX також повинні містити значення.
  • Сторінка спочатку повинна зробити запит AJAX, щоб отримати маркер CSRF, тому це буде означати додаткову подорож у кожний раз.
  • Можна також просто вивести маркер на сторінку, що дозволить зберегти зайвий запит.

Спеціальний HTTP-заголовок (вище)

  1. Користувач успішно входить у систему.
  2. Сервер видає auth cookie.
  3. Користувач клацає, щоб перейти до форми.
  4. Якщо ще не сформовано для цього сеансу, сервер генерує маркер CSRF, зберігає його у сеансі користувача та виводить його десь у вміст сторінки.
  5. Користувач надсилає форму через AJAX (маркер надсилається через заголовок).
  6. Сервер перевіряє спеціальний заголовок відповідності сеансу збереженого маркера.

Переваги:

  • Працює з AJAX.
  • Файл cookie може бути лише HTTP .

Недоліки:

  • Не працює з формами.
  • Усі порти AJAX повинні містити заголовок.

Спеціальні заголовки HTTP (вище та нижче)

  1. Користувач успішно входить у систему.
  2. Сервер видає auth cookie.
  3. Користувач клацає, щоб перейти до форми.
  4. Сторінка завантажується у браузері, тоді запит AJAX робиться для отримання маркера CSRF.
  5. Сервер генерує токен CSRF (якщо він ще не створений для сеансу), зберігає його у сеансі користувача та виводить його в заголовок.
  6. Користувач надсилає форму через AJAX (маркер надсилається через заголовок).
  7. Сервер перевіряє спеціальний заголовок відповідності сеансу збереженого маркера.

Переваги:

  • Працює з AJAX.
  • Файл cookie може бути лише HTTP .

Недоліки:

  • Не працює з формами.
  • Усі порти AJAX також повинні містити значення.
  • Сторінка повинна спочатку зробити запит AJAX, щоб отримати маркер CRSF, тому це буде означати додаткову поїздку в обидва рази.

Набір-Cookie

  1. Користувач успішно входить у систему.
  2. Сервер видає auth cookie.
  3. Користувач клацає, щоб перейти до форми.
  4. Сервер генерує токен CSRF, зберігає його проти користувальницького сеансу і виводить його на файл cookie.
  5. Користувач надсилає форму через AJAX або через HTML-форму.
  6. Сервер перевіряє власні заголовки (або приховане поле форми) на відповідність сеансу, що зберігається.
  7. Файл cookie доступний у веб-переглядачі для використання в додаткових AJAX та формуванні запитів без додаткових запитів на сервер для отримання маркера CSRF.

Переваги:

  • Простий у виконанні.
  • Працює з AJAX.
  • Працює з формами.
  • Не обов'язково потрібен запит AJAX, щоб отримати значення файлу cookie. Будь-який HTTP-запит може отримати його, і він може бути доданий до всіх форм / запитів AJAX через JavaScript.
  • Після того, як маркер CSRF буде отримано, оскільки він зберігається у файлі cookie, значення можна повторно використовувати без додаткових запитів.

Недоліки:

  • Усі форми мають динамічно додавати значення до його HTML.
  • Будь-які порти AJAX також повинні містити значення.
  • Файл cookie буде подано для кожного запиту (тобто всіх GET для зображень, CSS, JS тощо), які не задіяні в процесі CSRF), збільшуючи розмір запиту.
  • Файл cookie не може бути лише HTTP .

Таким чином, підхід до файлів cookie є досить динамічним, пропонує простий спосіб отримати значення файлу cookie (будь-який запит HTTP) та використовувати його (JS може додавати значення до будь-якої форми автоматично, і його можна використовувати в запитах AJAX або в якості заголовка, або як значення форми). Після того, як маркер CSRF отриманий для сеансу, не потрібно регенерувати його, оскільки зловмисник, що використовує експлуатацію CSRF, не має жодного способу отримання цього маркера. Якщо зловмисний користувач намагається прочитати токен CSRF користувача будь-яким із перерахованих вище способів, це запобіжить політиці того самого джерела . Якщо зловмисний користувач намагається отримати сторону сервера маркера CSRF (наприклад, черезcurl), то цей маркер не буде пов’язаний з тим самим обліковим записом користувача, оскільки файл cookie аутентивної сесії жертви буде відсутній у запиті (це було б зловмисником, тому він не буде пов'язаний на стороні сервера із сеансом жертви).

Окрім схеми токена синхронізатора, існує також Cookie з подвійним поданнямМетод профілактики CSRF, який, звичайно, використовує файли cookie для зберігання типу маркера CSRF. Це легше реалізувати, оскільки для маркера CSRF не потрібно жодного стану на сервері. Маркер CSRF насправді може бути стандартним файлом cookie автентифікації при використанні цього методу, і це значення подається через cookie як зазвичай із запитом, але значення також повторюється у прихованому полі чи заголовку, звідки зловмисник не може реплікувати як вони не можуть прочитати значення в першу чергу. Однак рекомендується вибрати інший файл cookie, окрім файлу cookie автентифікації, щоб cookie автентифікації можна було захистити, позначаючи HttpOnly. Отже, це ще одна поширена причина, чому ви знайдете запобігання CSRF за допомогою методу на основі файлів cookie.


7
Я не впевнений, що розумію, як "запит AJAX робиться для отримання маркера CSRF" (крок 4 в обох розділах "спеціальний заголовок: вниз") можна зробити безпечно; оскільки це окремий запит, сервер не знає, від кого він надходить; як він знає, що безпечно оприлюднювати маркер CSRF? Мені здається, якщо ви не зможете вимкнути маркер із початкового завантаження сторінки, ви втратите (що, на жаль, робить власні заголовки відповіді нижче за течією нестартерними).
метамат

6
Тому що у підробника не буде файлу cookie сеансу. У них може бути власне файли cookie сеансу, але оскільки маркер CSRF асоційований із сеансом, їх маркер CSRF не буде відповідати жертві.
SilverlightFox

32
У моєму розумінні CSRF нападу, фальсифікатор має свої кук сесії. Ну, вони на насправді не отримати , щоб побачити печиво, але у них є можливість надати його в своїх кованих запитах, оскільки запити надходять з мого браузера , і мій браузер поставляє свої кук сесії. Отже, з точки зору сервера, печиво сеансу самостійно не може відрізнити законний запит від підробленого запиту. Це насправді атака, яку ми намагаємось запобігти. До речі, дякую за ваше терпіння говорити про це, особливо якщо я з цим плутаюсь.
метамат

8
Вони мають можливість подавати авторський cookie, але вони не можуть прочитати відповідь, що містить маркер CSRF.
SilverlightFox

8
@metamatt Вибачте за некро, але я зроблю це для людей, які блукають. На моє розуміння, нападник зазвичай не має доступу до відповіді. CSRF використовується в першу чергу, щоб викликати побічні ефекти , а не безпосередньо збирати дані. Наприклад, скрипт атаки CSRF може змусити привілейованого користувача ескалатувати привілеї зловмисника, відключити налаштування безпеки або змусити зареєстрованого користувача Paypal надсилати передачу на певну адресу електронної пошти. У жодному з цих випадків зловмисник не дбає про реакцію, яка все ще надсилається до браузера жертви; лише результат нападу.
Jonathanbruder

61

Використання файлу cookie для надання клієнту маркера CSRF не дозволяє успішну атаку, оскільки зловмисник не може прочитати значення файлу cookie та, отже, не може поставити його там, де потрібна перевірка CSRF на стороні сервера.

Зловмисник зможе викликати запит до сервера з файлом cookie auth token та файлом cookie CSRF у заголовках запиту. Але сервер не шукає маркер CSRF як cookie в заголовках запиту, він шукає корисну навантаження запиту. І навіть якщо зловмисник знає, куди поставити маркер CSRF у корисний вантаж, їм доведеться прочитати його значення, щоб розмістити його туди. Але політика перехресного походження браузера не дозволяє читати будь-яке значення файлів cookie з цільового веб-сайту.

Ця ж логіка не застосовується до файлу cookie auth token, оскільки сервер очікує цього в заголовках запиту, а зловмиснику не потрібно робити нічого особливого, щоб розмістити його там.


Напевно, зловмиснику не потрібно в першу чергу читати печиво. Вони можуть просто вставити зображення на зламаний сайт, з src='bank.com/transfer?to=hacker&amount=1000яким браузер запитає, разом із пов’язаними файлами cookie для цього сайту ( bank.com)?
developius

2
CSRF призначений для перевірки користувача на стороні клієнта, а не для захисту сайту, як правило, від компромісного рішення на стороні сервера.
Tongfa

2
@developius надсилання файлу cookie недостатньо для задоволення захисту CSRF. Файл cookie містить маркер csrf, надісланий сервером. Законний клієнт повинен прочитати маркер csrf з файлу cookie, а потім передати його десь у запиті, наприклад, у заголовку чи у корисному навантаженні. Захист CSRF перевіряє, чи відповідає значення файлу cookie значенню в запиті, інакше запит буде відхилено. Тому зловмиснику потрібно читати печиво.
Буде М.

1
Ця відповідь дуже відповідала питанням оригінального афіші і була дуже зрозумілою. +1 Дякую
java-addict301

@Tongfa - дякую, це допомогло мені зрозуміти краще. Чи правильно я вважаю, що маркер CSRF НЕ слід розміщувати в заголовку? це повинно бути десь у тілі?
нульовий знак

10

Моя найкраща здогадка щодо відповіді: Розгляньте ці 3 варіанти того, як знизити маркер CSRF з сервера до браузера.

  1. В тілі запиту (не заголовку HTTP).
  2. У користувацькому заголовку HTTP, а не Встановити cookie.
  3. Як cookie, у заголовку Set-Cookie.

Я думаю, що перший, орган запиту (в той час як це демонструє експрес-підручником, з яким я пов’язаний у запитанні ), не є настільки переносним для широкого спектру ситуацій; не всі генерують кожну відповідь HTTP динамічно; там, де вам знадобиться ввести маркер в генеровану відповідь, може бути дуже різним (в прихованій формі введення; у фрагменті коду JS або змінної, доступній іншим кодом JS; можливо, навіть в URL-адресі, хоча це, як правило, погано ставити маркери CSRF). Тож, працюючи з деякими налаштуваннями, №1 - це важке місце для підходу, що відповідає одному розміру.

Другий, власний заголовок, привабливий, але насправді не працює, оскільки, хоча JS може отримати заголовки для XHR, на який він викликав, він не може отримати заголовки сторінки, з якої завантажується .

Це залишає третій файл cookie, який використовується заголовком Set-Cookie, як підхід, який легко використовувати у будь-яких ситуаціях (будь-який сервер зможе встановити заголовки файлів cookie за запитом, і не має значення, який тип дані знаходяться в тілі запиту). Тому, незважаючи на його недоліки, це був найпростіший метод широкомасштабної реалізації.


7
Я можу констатувати очевидне, це означає, що файли cookie не можуть бути коректно https?
Фотон

1
лише для запитів ajax (де JS потрібно знати значення файлу cookie csrf, щоб надіслати його на наступний запит у другому каналі (як дані форми чи заголовка)). Немає підстав вимагати, щоб маркер csrf був HttpOnly, якщо файл сеансу cookie вже є HttpOnly (для захисту від XSS), оскільки маркер csrf сам по собі не є цінним без пов'язаного сеансу.
коуберт

2

Окрім файлу cookie сеансу (який є типовим стандартом), я не хочу використовувати додаткові файли cookie.

Я знайшов рішення, яке працює для мене під час створення веб-програми на одній сторінці (SPA) із багатьма запитами AJAX. Примітка: Я використовую серверну Java та клієнтську сторону JQuery, але ніяких магічних речей, тому я думаю, що цей принцип можна реалізувати у всіх популярних мовах програмування.

Моє рішення без зайвих файлів cookie є простим:

Сторона клієнта

Зберігайте маркер CSRF, який повертається сервером після успішного входу в глобальну змінну (якщо ви, звичайно, хочете використовувати веб-сховище замість глобального, це добре). Попросіть JQuery поставити заголовок X-CSRF-TOKEN у кожному дзвінку AJAX.

Основна сторінка "індексу" містить цей фрагмент JavaScript:

// Intialize global variable CSRF_TOKEN to empty sting. 
// This variable is set after a succesful login
window.CSRF_TOKEN = '';

// the supplied callback to .ajaxSend() is called before an Ajax request is sent
$( document ).ajaxSend( function( event, jqXHR ) {
    jqXHR.setRequestHeader('X-CSRF-TOKEN', window.CSRF_TOKEN);
}); 

Сторона сервера

Після успішного входу в систему створіть випадковий (і досить довгий) маркер CSRF, збережіть його в сеансі на сервері та поверніть його клієнту. Фільтруйте певні (чутливі) вхідні запити, порівнюючи значення заголовка X-CSRF-TOKEN зі значенням, збереженим у сеансі: вони повинні відповідати.

Чутливі дзвінки AJAX (дані POST форми та GET JSON-дані) та фільтр на стороні сервера, що їх перехоплюють, знаходяться під шляхом / dataservice / *. Запити на вхід не повинні потрапляти до фільтра, тому вони знаходяться на іншому шляху. Запити на HTML, CSS, JS та ресурси зображення також не знаходяться на шляху / dataservice / *, тому не фільтруються. Вони не містять нічого секретного і не можуть завдати шкоди, тому це добре.

@WebFilter(urlPatterns = {"/dataservice/*"})
...
String sessionCSRFToken = req.getSession().getAttribute("CSRFToken") != null ? (String) req.getSession().getAttribute("CSRFToken") : null;
if (sessionCSRFToken == null || req.getHeader("X-CSRF-TOKEN") == null || !req.getHeader("X-CSRF-TOKEN").equals(sessionCSRFToken)) {
    resp.sendError(401);
} else
    chain.doFilter(request, response);
}   

Думаю, ви хочете, щоб CSRF отримав запит на вхід. Здається, ви використовуєте маркер CSRF також як маркер сеансу входу. Це також працює, щоб мати їх як окремі лексеми, і тоді ви можете використовувати CSRF у будь-якій кінцевій точці, незалежно від того, чи користувач увійшов чи ні.
Tongfa
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.