Аутентифікація в односторінкових застосунках. Частина 2.

11 хв. читання

В першій частині ми розглядали заголовок Authorization як один з популярних способів аутентифікації в односторінкових застосунках. Він має свої переваги та недоліки. Використовуючи заголовок Authorization, доведеться також користуватися веб сховищем (англ. Web Storage), яке є не найкращим рішенням для безпечного зберігання облікових даних. Взяти до прикладу The Android Keystore, Keychain для macOS та iOS, Credentials Manager для Windows. Всі вони вживають заходів для захисту облікових даних застосунку від несанкціонованого доступу. Наразі, в браузері є лише одне безпечне з точки зору зчитування сховище облікових даних: кукі з атрибутами HttpOnly та Secure.

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

Та це все ж достатньо надійний спосіб забезпечення безпеки на рівні сайтів:

  • Захист облікових даних від інших веб-застосунків;
  • Захист облікових даних від несанкціонованого доступу з боку самого веб застосунку.

На жаль, в браузерах все ще немає безпечного з точки зору запису сховища облікових даних. Про наслідки цього мова йтиме трохи пізніше.

Аутентифікація через кукі

Найвідоміший приклад аутентифікації через кукі - це сесії. Цей метод застосовується десятиліттями і все ще залишається де-факто методом аутентифікації для більшості сайтів та веб застосунків.

Аутентифікація через кукі працює наступним чином:

У відповідь на запит входу, сервер надсилає заголовок відповіді Set-Cookie, наприклад:

Set-Cookie: access_token=ce073b61; HttpOnly; Secure; SameSite=strict

Браузер автоматично запам'ятовує кукі та надсилає їх в кожному наступному запиті:

Cookie: access_token=ce073b61

Доки застосунок розташований на тому самому домені, що й API, дуже легко застосовувати кукі для аутентифікації в односторінкових застосунках. Ваш застосунок буде робити ті самі дії для входу, що і звичайний веб-сайт, а сервер надішле у відповідь кукі й всі наступні запити будуть аутентифіковані.

Які недоліки у кукі?

Кукі прив'язані до певного домену. Якщо ваш застосунок вимагає взаємодії з кількома API доменами, знадобиться більш складна логіка для аутентифікації.

Які слабкі місця у кукі?

  • Обхід аутентифікації (не потрібно читати та/чи писати кукі):

    • За замовчуванням, вразливі до CSRF атак;
  • Читання кукі:

    • Доступність через JavaScript, якщо HttpOnly не використовується;

    • Вразливість до МІТМ при незахищеному HTTP з'єднанні, якщо Secure не використовується;

  • Запис (зараження) кукі

    • Вразливість до атак, що переповнюють сховище кукі;
    • Кукі з атрибутом httpOnly все ще можуть бути перезаписані через JavaScript;
    • Кукі без атрибута Secure все ще надсилаються через HTTPS з'єднання;
    • Кукі можуть бути встановлені підробленою незахищеною HTTP відповіддю;
    • Кукі можуть бути встановлені від більш до менш глибоко вкладених доменів (наприклад, www.example.com може встановити кукі для example.com)

Які переваги HttpOnly кукі?

Мабуть, чи не єдина справжня перевага полягає в тому, що жоден веб-сайт не в змозі прочитати їх, навіть сам веб-застосунок. Це може прозвучати дещо дивно, враховуючи всі описані вище недоліки, проте ця перевага може бути вирішальною для застосунків, що вимагають безпеки вищого рівня. І пам'ятайте, що неможливо обійти обмеження веб-сховища (доступність для будь-якого JavaScript коду на сторінці), але можна захиститись від згаданих вразливостей кукі. Проте, за це доведеться заплатити підвищенням складності.

Аналіз небезпеки

Обхід аутентифікації (CSRF)

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

X-Requested-With: XMLHttpRequest

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

Хоча це не найкращий спосіб захисту від CSRF. Відомо про багато вразливостей в Java та Flash, які дозволяють надсилання власних заголовків. Зараз все стало набагато безпечніше. Але чи знаєте ви про все ще не знайдені та невідомі вразливості?

Чому власного заголовка було достатньо у випадку з заголовком Authorization?

Найважливіша частина надійного захисту від CSRF — унікальний токен, який неможливо підібрати, і який повинен бути присутній у всіх запитах. З заголовком Authorization ми вбиваємо двох зайців: аутентифікацію та унікальний токен. Нападник не зможе підробити міжсайтовий запит не знаючи токен (а знати токен дорівнює викраденню сесії й виходить за рамки CSRF).

Але у випадку аутентифікації через кукі, сесія або токен доступу надсилається автоматично. Тобто, нам потрібен окремий токен для захисту від CSRF.

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

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

Чи безпечно передавати CSRF токен через AJAX?

Природа CSRF атаки дозволяє нападнику зробити запит, але не дозволяє переглянути результат. Тобто, якою б не була відповідь, вона залишиться прихованою від нападника. Прочитати її може тільки сам застосунок, або домени авторизовані через CORS.

Погляньмо на це з іншого боку. Якби передача CSRF токена через AJAX не була безпечною, що б зупинило нападника від запиту сторінки з формою на звичайних не односторінкових сайтах? Захист від CSRF полягає в неможливості читання відповіді неавторизованим користувачем. Отже, неважливо, яким чином був отриманий токен: через рендер звичайної сторінки чи через AJAX.

Читання кукі

Використання атрибуту HttpOnly в кукі не дозволяє прочитати кукі при XSS атаці. А це, у свою чергу, не дозволяє нападнику викрасти облікові дані для офлайн атаки. Хоча XSS дає набагато більше можливостей, важливо розрізняти онлайн та офлайн атаки. Детальніше ми звернули на це увагу в розділі Міжсайтовий скриптинг в першій частині статті.

Використання атрибуту Secure на кукі запобігає появі кукі в незахищених HTTP запитах, а також не дозволяє MITM прочитати кукі. Та він працює виключно з HTTPS запитами, тобто вам знадобиться TLS/SSL сертифікат (на щастя, є Let's Encrypt). Не варто недооцінювати важливість HTTPS! Безпарольний доступ до Wi-Fi зараз повсюди, а отже і можливостей для атах вистачає.

Запис (зараження) кукі

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

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

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

Наприклад, коли зараження кукі було виявлене на GitHub (це стало можливим, бо GitHub Pages та головний сайт були розміщені на тому самому домені), ця проблема вважалась незначною. Все, що було можливим зробити через цю вразливість, це змусити користувача вийти з облікового запису.

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

Чи підходить Ruby on Rails для сучасних односторінкових застосунків?

І так, і ні. Все залежить від того, який ви виберете механізм аутентифікації.

Сесії

Автентифікація через сесії підходить для односторінкових застосунків. Немає нічого поганого в тому, щоб не надсилати активно заголовок Authorization і не обробляти самостійно токени.

Просто використайте POST, щоб залогінитись з обліковими даними користувача і використовуйте AJAX як зазвичай. Rails має вбудоване керування сесіями. Та, оскільки сесії базуються на кукі, потрібно взяти до уваги все, що ми описували вище.

Перше, що потрібно пам'ятати: сесії — не токени доступу. Часто їх намагаються використовувати як токени доступу й очікують відповідний результат. Але механізм сесій відрізняється. Сесії продовжують існувати, навіть коли сервер змінює її вміст. Наприклад, гостьова сесія може перетворитися в користувацьку, якщо їй призначити користувача.

Важливо завжди скидати сесії після будь-якої зміни статусу (вхід чи вихід з облікового запису). У випадку з Rails треба просто викликати метод reset_session в контроллері. Наприклад:

def login
  reset_session
  # .. your login logic ...
end

CSRF

У Rails є вбудований захист від CSRF через використання токена синхронізації та шифрування для захисту від атак типу BREACH. Якщо коротко, то кожен токен є унікальним, але для одної сесії можуть існувати кілька (замаскованих) дійсних токенів, тому відкриття нової вкладки не ламає попередні. Це досить хороше безпечне рішення.

При рендері початкової сторінки застосунку за допомогою Rails, можна включити CSRF токен використовуючи вбудований метод. Для цього потрібно включити csrf_meta_tags в шаблон:

<head>
  <%= csrf_meta_tags %>
  <!-- ... other tags ... -->
</head>

При рендері клієнтської частини застосунку окремо від бекенду на Rails можна створити окрему дію щоб отримати токен через AJAX, використовуючи метод form_authenticity_token напряму, наприклад:

class TokensController < ApplicationController
  def csrf
    render json: {
      csrf_token: form_authenticity_token
    }
  end
end3

Деякі фреймворки та бібліотеки для JavaScript мають вбудований захист від CSRF атак для фронтенду. Наприклад, jQuery підтримує згадані метатеги.

Angular 1 та Angular 2+ підтримують шаблон подвійного надсилання кукі, який сумісний з Rails. Angular очікує знайти кукі з назвою XSRF-TOKEN і надішле його значення в заголовку запиту X-XSRF-TOKEN. Rails має вбудовану підтримку цього заголовку. Це означає, що використовувати Angular разом з Rails дуже просто. Потрібно тільки надати кукі в будь-якій відповіді, наприклад:

class ApplicationController < ActionController::Base
  before_action do
    cookies['XSRF-TOKEN'] = form_authenticity_token unless cookies['XSRF-TOKEN']
  end
end

Без сумніву, Rails може використовуватись для безпечної аутентифікації в односторінкових застосунках, хоч цей фреймворк і не був створений спеціально для цього типу застосунків.

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

Якщо ви збирались використовувати bearer токени і заголовок Authorization або розробити безпечну автентифікацію для односторінкового застосунку з нуля, то Rails вам в цьому не дуже допоможе (хіба що з основними функціями типу роутерів, контролерів чи моделей). Якщо ви не хочете розробляти все самостійно, просто зупиніть свій вибір на Rails.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація