Найкращі практики SPA для аутентифікації та управління сеансами


307

Створюючи додатки у стилі SPA, використовуючи такі рамки, як Angular, Ember, React тощо, які люди вважають найкращими методами аутентифікації та управління сеансами? Я можу придумати кілька способів розглянути питання про підхід до проблеми.

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

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

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

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


, Я вважаю за краще цей шлях, stackoverflow.com/a/19820685/454252
allenhwkim

Відповіді:


476

Це питання було розглянуто, дещо іншою формою, довгий час:

RESTful аутентифікація

Але це вирішує це з боку сервера. Давайте розглянемо це з боку клієнта. Перш ніж ми це зробимо, є важлива прелюдія:

Криптовалюта Javascript - безперспективна

Стаття Матасано про це відома, але уроки, що містяться в ній, є досить важливими:

https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

Узагальнити:

  • Атака "посередник" може тривіально замінити ваш крипто код <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • Атака "посередник" - тривіальна проти сторінки, яка обслуговує будь-який ресурс через з'єднання, яке не є SSL.
  • Коли у вас є SSL, ви все одно використовуєте справжню криптовалюту.

І додати власний слід:

  • Успішна XSS-атака може призвести до того, що зловмисник виконує код у веб-переглядачі свого клієнта, навіть якщо ви використовуєте SSL - так що навіть якщо у вас є всі збиті люки, криптовалюта браузера все одно може вийти з ладу, якщо ваш зловмисник знайде спосіб виконання будь-який код JavaScript у чужому браузері.

Це робить безліч схем аутентифікації RESTful неможливими або нерозумними, якщо ви збираєтесь використовувати клієнт JavaScript. Давай подивимося!

HTTP Basic Auth

Перш за все, HTTP Basic Auth. Найпростіші схеми: просто передайте ім’я та пароль з кожним запитом.

Звичайно, це абсолютно вимагає SSL, тому що ви передаєте кодовані ім'я та пароль Base64 (зворотно) з кожним запитом. Усі, хто слухав цю лінію, можуть вилучити ім’я користувача та пароль тривіально. Більшість аргументів "Basic Auth - це небезпечно" походить з місця "Basic Auth over HTTP", що є жахливою ідеєю.

Веб-переглядач підтримує підтримку HTTP Basic Auth, але це некрасиво як гріх, і ви, ймовірно, не повинні використовувати його для свого додатка. Однак альтернативою є зберігання імені користувача та пароля в JavaScript.

Це найрізноманітніше рішення. Сервер не вимагає жодних знань про стан і автентифікує кожну індивідуальну взаємодію з користувачем. Деякі ентузіасти REST (в основному солом'яники) наполягають на тому, що збереження будь-якого стану єресі і піне в рот, якщо ви думаєте про будь-який інший метод аутентифікації. Існують теоретичні переваги такого роду дотримання стандартів - він підтримується Apache поза коробкою - ви можете зберігати свої об'єкти у вигляді файлів у папках, захищених файлами .htaccess, якщо ваше серце бажає!

Проблема ? Ви кешуєте на стороні клієнта ім'я користувача та пароль. Це дає зла.ru краще зламати його - навіть найпростіші вразливості XSS можуть призвести до того, що клієнт передасть своє ім'я користувача та пароль на злий сервер. Ви можете спробувати полегшити цей ризик хешированием і солінням пароля, але пам’ятайте: JavaScript Crypto не є надійним . Ви можете полегшити цей ризик, залишивши його до основної підтримки браузера Auth, але .. негарно, як гріх, як згадувалося раніше.

Підтримка HTTP Auth

Чи можлива автентифікація дайджесту за допомогою jQuery?

Більш "захищений" автор, це хеш-запит на запит / відповідь. За винятком того, що JavaScript Crypto не є надійним , тому він працює лише через SSL, і вам все одно доведеться кешувати ім'я користувача та пароль на стороні клієнта, що робить його складнішим ніж HTTP Basic Auth, але не більш безпечно .

Перевірка автентичності за допомогою додаткових параметрів підпису.

Ще один "захищений" auth, де ви шифруєте свої параметри без даних та часових даних (для захисту від повторних та тимчасових атак) та надсилаєте їх. Одним з найкращих прикладів цього є протокол OAuth 1.0, який, наскільки я знаю, є досить приголомшливим способом реалізації автентифікації на сервері REST.

http://tools.ietf.org/html/rfc5849

О, але для JavaScript немає клієнтів OAuth 1.0. Чому?

Криптовалют JavaScript безперспективний , пам’ятайте. JavaScript не може брати участь в OAuth 1.0 без SSL, і ви все одно повинні зберігати ім'я користувача та пароль клієнта на локальному рівні - що ставить це до тієї ж категорії, що і Digest Auth - це складніше, ніж HTTP Basic Auth, але не є більш безпечним .

Токен

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

Це дещо безпечніше, ніж HTTP Basic Auth, оскільки, як тільки транзакція з ім'ям користувача / паролем завершена, ви можете відкинути конфіденційні дані. Це також менш RESTful, оскільки маркери складають "стан" і ускладнюють реалізацію сервера.

SSL

Проте, все-таки потрібно надіслати це початкове ім'я користувача та пароль, щоб отримати маркер. Конфіденційна інформація все ще стосується вашого компромісного JavaScript.

Щоб захистити облікові дані користувача, вам все одно потрібно не захищати зловмисника у JavaScript, і вам все одно потрібно надсилати ім’я користувача та пароль через провід. Необхідна SSL.

Термін дії маркера

Загальноприйнято застосовувати політику токена на кшталт "ей, коли цей маркер занадто довгий, відмініть його та змусьте користувача повторно засвідчити автентифікацію". або "Я майже впевнений, що єдиною IP-адресою, дозволеною використовувати цей маркер, є XXX.XXX.XXX.XXX". Багато з цих політик є досить хорошими ідеями.

Пожежу

Однак використання токена без SSL все ще вразливе до атаки під назвою 'sidejacking': http://codebutler.github.io/firesheep/

Зловмисник не отримує облікових даних вашого користувача, але все одно може прикинутися вашим користувачем, що може бути дуже погано.

tl; dr: Надсилання незашифрованих жетонів по дроту означає, що зловмисники можуть з легкістю нарізати ці жетони і прикинутися вашим користувачем. FireSheep - це програма, яка робить це дуже просто.

Окрема, більш безпечна зона

Чим більше програма, яку ви запускаєте, тим складніше абсолютно гарантувати, що вони не зможуть ввести якийсь код, який змінює спосіб обробки конфіденційних даних. Ви абсолютно довіряєте своєму CDN? Ваші рекламодавці? Ваша власна база коду?

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

Cookie (просто означає Token)

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

Сесія (все ще просто означає токен)

Session Auth - це лише аутентифікація Token, але з кількома відмінностями, які роблять це дещо іншим:

  • Користувачі починають з неаутентифікованого маркера.
  • Заміст підтримує об'єкт "state", прив'язаний до маркера користувача.
  • Маркер надається у файлі cookie.
  • Середовище додатків абстрагує деталі від вас.

Крім цього, він нічим не відрізняється від Token Auth.

Це ще більше відстає від реалізації RESTful - з об'єктами стану ви йдете все далі і далі вниз по шляху простого RPC на стаціонарному сервері.

OAuth 2.0

OAuth 2.0 розглядає проблему "Як Програмне забезпечення A надає Програмному продукту B доступ до даних користувача X, без Програмного забезпечення B доступу до облікових даних користувача X".

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

По суті, OAuth 2.0 - це лише протокол маркера. Він має ті ж властивості, що й інші протоколи лексем - вам все одно потрібен SSL для захисту цих маркерів - він просто змінює спосіб їх генерування.

Є два способи, якими OAuth 2.0 може допомогти вам:

  • Надання автентифікації / інформації іншим
  • Отримання автентифікації / інформації від інших

Але коли справа доходить до цього, ви просто ... використовуєте жетони.

Поверніться до свого питання

Отже, питання, яке ви задаєте, - "чи потрібно я зберігати маркер у файлі cookie та чи повинен автоматичне управління сеансом мого оточення дбати про деталі, чи я повинен зберігати маркер у Javascript та самостійно обробляти ці деталі?"

А відповідь така: робіть все, що робить вас щасливими .

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

Мені 21 рік, так що SSL - так

Інша відповідь: Використовуйте https для всього, або розбійники вкрадуть ваші користувачі паролі та жетони.


3
Чудова відповідь. Я ціную рівновагу між системами аутентифікації токенів та базовими аутентивними файлами cookie (що часто вбудовується у веб-рамки). Це щось таке, що я шукав. Я вдячний, що ви також охоплюєте так багато потенційних питань для розгляду. Ура!
Кріс Нікола

11
Я знаю, що минув час, але мені цікаво, чи слід це розширити, щоб включити JWT? auth0.com/blog/2014/01/07/…
Кріс Нікола

14
Токен It's also less RESTful, as tokens constitute "state and make the server implementation more complicated." (1) REST вимагає, щоб сервер був без стану. Маркер, що зберігається на стороні клієнта , не представляє стану для жодного змістовного способу для сервера. (2) Дещо складніший на стороні сервера код не має нічого спільного з RESTfulness.
супдог

10
lol_nope_send_it_to_me_insteadМені сподобалось ім’я цієї функції: D
Лев

6
Одне, що вам здається не помітити: Файли cookie є безпечними для XSS, якщо вони позначені httpOnly, і їх можна заблокувати далі із захищеним і istoesite. І обробка файлів cookie тривала набагато довше === більше загострена битва. Покладатися на JS та локальне сховище для обробки токенової безпеки є дурною грою.
Мартійн Пітерс

57

Ви можете підвищити безпеку в процесі аутентифікації за допомогою JWT (JSON Web Tokens) та SSL / HTTPS.

Основний ідентифікатор автентифікації / сесії можна вкрасти через:

  • MITM-атака (Man-In-The Middle) - без SSL / HTTPS
  • Зловмисник отримує доступ до комп'ютера користувача
  • XSS

Використовуючи JWT, ви шифруєте дані автентифікації користувача та зберігаєте їх у клієнті та надсилаєте їх разом із кожним запитом до API, де сервер / API перевіряє маркер. Його неможливо розшифрувати / прочитати без приватного ключа (який сервер / API зберігає таємно) Оновлення читання .

Новим (більш безпечним) потоком буде:

Вхід

  • Користувач входить у систему та надсилає облікові дані для входу в API (через SSL / HTTPS)
  • API отримує облікові дані для входу
  • Якщо дійсно:
    • Зареєструйте новий сеанс у базі даних Прочитайте оновлення
    • Шифруйте ідентифікатор користувача, ідентифікатор сесії, IP-адресу, часову позначку тощо в JWT за допомогою приватного ключа.
  • API надсилає токен JWT клієнту (через SSL / HTTPS)
  • Клієнт отримує маркер JWT і зберігає в localStorage / cookie

Кожен запит до API

  • Користувач надсилає HTTP-запит API (через SSL / HTTPS) із збереженим маркером JWT у заголовку HTTP
  • API зчитує заголовок HTTP і розшифровує JWT-маркер своїм приватним ключем
  • API перевіряє маркер JWT, відповідає IP-адресі від HTTP-запиту та посилається в маркері JWT і перевіряє, чи закінчився сеанс
  • Якщо дійсно:
    • Повернути відповідь із запитаним вмістом
  • Якщо недійсний:
    • Виняток викидання (403/401)
    • Прапор вторгнення в систему
    • Надішліть користувачеві попередження.

Оновлено 30.07.15:

Корисне навантаження / претензії JWT насправді можна прочитати без закритого ключа (секретного), і зберігати його в localStorage не можна. Мені дуже шкода цих помилкових тверджень. Однак вони, здається, працюють за JWE стандартом (JSON Web Encryption) .

Я реалізував це, зберігаючи претензії (userID, exp) у JWT, підписав його приватним ключем (секретом), про який API / бекенд знає і зберігає його як захищений файл cookie HttpOnly на клієнті. Таким чином, він не може бути прочитаний через XSS і не може ним маніпулювати, інакше JWT не може перевірити підпис. Також використовуючи захищений файл cookie HttpOnly , ви переконайтеся, що файл cookie надсилається лише через HTTP-запити (недоступний для сценарію) та надсилається лише через захищене з'єднання (HTTPS).

Оновлено 17.07.16:

JWT за своєю природою без громадянства. Це означає, що вони визнають себе недійсними / закінчуються. Додаючи SessionID у претензії маркера, ви робите його загальнодоступним, оскільки його дійсність тепер не залежить тільки від перевірки підпису та терміну придатності, але також залежить від стану сеансу на сервері. Однак у бік ви можете легко визнати недійсними жетони / сеанси, чого раніше не можна було робити з JWT без стану.


1
Зрештою, JWT все ще є "лише ознакою" з точки зору безпеки. Сервер все ще може пов'язувати ідентифікатор користувача, IP-адресу, часову позначку тощо з непрозорим маркером сеансу, і це було б не більш-менш безпечно, ніж JWT. Однак бездержавний характер СЗТ полегшує впровадження.
Джеймс

1
@James JWT має перевагу в тому, що він може бути перевірений і здатний переносити ключові деталі. Це досить корисно для різних сценаріїв API, наприклад, коли потрібна аутентифікація між доменами. Щось сеанс не буде таким хорошим. Це також визначена (або принаймні триває) специфікація, яка корисна для реалізації. Це не означає, що це краще, ніж будь-яка інша реалізація хорошого маркера, але вона чітко визначена і зручна.
Кріс Нікола

1
@Chris Так, я згоден з усіма вашими пунктами. Однак потік, описаний у відповіді вище, за своєю суттю не є більш безпечним потоком, як заявлено через використання JWT. Крім того, JWT не може бути відкликаний у описаній вище схемі, якщо ви не асоціюєте ідентифікатор з JWT і зберігаєте стан на сервері. В іншому випадку вам або потрібно регулярно отримувати новий JWT, запитуючи ім'я користувача / пароль (поганий досвід користувача) або видавати JWT з дуже тривалим терміном дії (погано, якщо маркер буде вкрадений).
Джеймс

1
Моя відповідь не на 100% правильна, тому що JWT насправді можна розшифрувати / прочитати без закритого ключа (секретного), і зберігати його в localStorage не можна. Я реалізував це, зберігаючи претензії (userID, exp) у JWT, підписав його приватним ключем (секретом), про який API / бекенд знає і зберігав його як cookie HttpOnly на клієнті. Таким чином, XSS не може його читати. Але ви повинні використовувати HTTPS, оскільки маркер може бути викрадений при атаці MITM. Я оновлю свою відповідь, щоб поміркувати над цим.
Гауї

1
@vsenko Файл cookie надсилається з кожним запитом від клієнта. Ви не маєте доступу до файлу cookie від JS, він пов'язаний із кожним запитом HTTP від ​​клієнта до API.
Гауї

7

Я б пішов на другу, систему жетонів.

Чи знали ви про ember-auth чи ember-simple-auth ? Вони обидва використовують систему, що базується на лексемах, як стани Ember-simple-auth:

Легка і ненав'язлива бібліотека для здійснення аутентифікації на основі токенів у додатках Ember.js. http://ember-simple-auth.simplabs.com

Вони мають управління сеансами, і вони також легко підключаються до існуючих проектів.

Існує також приклад версії Ember App Kit: ember-simple-auth: Робочий приклад ember-app-kit, що використовує ember-simple-auth для аутентифікації OAuth2.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.