Najlepsze praktyki SPA w zakresie uwierzytelniania i zarządzania sesjami

Podczas budowania aplikacji w stylu SPA przy użyciu frameworków takich jak Angular, Ember, React itp. co ludzie uważają za najlepsze praktyki w zakresie uwierzytelniania i zarządzania sesjami? Mogę wymyślić kilka sposobów na rozważenie podejścia do problemu.

  1. Traktuj to nie inaczej niż uwierzytelnianie za pomocą zwykłej aplikacji internetowej, zakładając, że API i UI mają tę samą domenę początkową.

    Prawdopodobnie wiąże się to z ciasteczkiem sesji, sesją po stronie serwera pamięć masowa i prawdopodobnie jakiś punkt końcowy API sesji, który uwierzytelniony interfejs WWW może trafić, aby uzyskać bieżące informacje o użytkowniku, aby pomóc w personalizacji lub ewentualnie nawet określeniu ról/umiejętności po stronie klienta. Serwer nadal egzekwowałby Zasady chroniące dostęp do danych oczywiście interfejs użytkownika wykorzystywałby te informacje do dostosowania środowiska.

  2. Traktuj go jak każdego klienta zewnętrznego używającego publicznego API i uwierzytelniaj za pomocą jakiegoś systemu tokenów podobnego do OAuth. Ten mechanizm tokenów byłby używany przez interfejs klienta do uwierzytelniania każdego żądania złożonego do API serwera.

Nie jestem tu zbyt wielkim ekspertem, ale numer 1 wydaje się całkowicie wystarczający dla większości przypadków, ale chciałbym usłyszeć bardziej doświadczone opinie.

Author: Robert Koritnik, 2014-01-07

3 answers

To pytanie zostało poruszone, w nieco innej formie, w długości, tutaj:

RESTful Authentication

Ale to adresuje go od strony serwera. Spójrzmy na to od strony klienta. Zanim jednak to zrobimy, jest ważne preludium:

Javascript Crypto jest beznadziejne

Artykuł Matasano na ten temat jest znany, ale zawarte w nim lekcje są dość ważne:

Http://www.matasano.com/articles/javascript-cryptography/

Podsumowując:

  • atak człowieka w środku może trywialnie zastąpić Twój kod kryptograficzny <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • atak man-in-the-middle jest banalny wobec strony, która obsługuje dowolny zasób przez połączenie non-SSL.
  • Gdy masz SSL, używasz prawdziwej krypto i tak.

I dodam jeszcze moje własne:

  • udany atak XSS może w rezultacie atakujący wykonuje kod w przeglądarce klienta, nawet jeśli używasz protokołu SSL - więc nawet jeśli masz wszystkie luki zablokowane, krypto przeglądarki może nadal zawieść, jeśli atakujący znajdzie sposób na wykonanie dowolnego kodu javascript w czyjejś przeglądarce.
To sprawia, że wiele RESTful authentication schemes jest niemożliwych lub głupich, jeśli zamierzasz używać klienta JavaScript. Zobaczmy!

HTTP Basic Auth

Przede wszystkim HTTP basic Auth. Na najprostszy z programów: po prostu podaj nazwę i hasło przy każdym żądaniu.

To oczywiście absolutnie wymaga SSL, ponieważ przekazujesz zaszyfrowaną nazwę i hasło Base64 (odwracalnie) przy każdym żądaniu. Każdy słuchający na linii może wyodrębnić nazwę użytkownika i hasło trywialnie. Większość argumentów "Basic Auth is insecure" pochodzi z miejsca "Basic Auth over HTTP", co jest okropnym pomysłem.

Przeglądarka zapewnia obsługę HTTP Basic Auth, ale jest brzydka jak grzech i prawdopodobnie nie powinieneś używać go do swojej aplikacji. Alternatywą jest jednak przechowywanie nazwy użytkownika i hasła w JavaScript.

To najbardziej spokojne rozwiązanie. Serwer nie wymaga żadnej wiedzy o stanie i uwierzytelnia każdą indywidualną interakcję z użytkownikiem. Niektórzy entuzjaści odpoczynku (głównie strawmeni) twierdzą, że utrzymywanie jakiegokolwiek stanu jest herezją i będzie piana w ustach, jeśli pomyślisz o jakiejkolwiek innej metodzie uwierzytelniania. Są teoretyczne korzyści z tego rodzaju standards-compliance - jest obsługiwany przez Apache po wyjęciu z pudełka-możesz przechowywać swoje obiekty jako pliki w folderach chronionych przez .pliki htaccess jeśli chcesz!

Problem ? Buforujesz po stronie klienta nazwę użytkownika i hasło. To daje evil.ru lepsze złamanie-nawet najbardziej podstawowe luki w zabezpieczeniach XSS mogą skutkować przesłaniem przez Klienta swojej nazwy użytkownika i hasła do złego serwera. Możesz spróbować złagodzić to ryzyko, mieszając i soląc hasło, ale pamiętaj: JavaScript Crypto jest beznadziejne. Można zmniejszyć to ryzyko, pozostawiając go do podstawowej obsługi Auth przeglądarki, ale.. brzydkie jak grzech, jak wspomniano wcześniej.

Http Digest Auth

Czy uwierzytelnianie Digest jest możliwe za pomocą jQuery?

Bardziej" bezpieczny " auth, jest to prośba/odpowiedź hash challenge. Z wyjątkiem JavaScript Crypto jest beznadziejny , więc działa tylko przez SSL i nadal musisz buforować nazwę użytkownika i hasło po stronie klienta, co czyni go bardziej skomplikowanym niż HTTP Basic Auth, ale nie bardziej bezpiecznym .

Uwierzytelnianie zapytań z dodatkowymi parametrami podpisu.

Kolejny bardziej "bezpieczny" auth, w którym szyfrujesz swoje parametry za pomocą danych nonce i timing (w celu ochrony przed atakami powtarzającymi się i timingowymi) i wysyłasz. Jednym z najlepszych tego przykładów jest protokół OAuth 1.0, który jest, o ile wiem, dość trudnym sposobem implementacji uwierzytelniania na serwerze REST.

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

Oh, ale nie ma żadnych klientów OAuth 1.0 Dla JavaScript. Dlaczego?

JavaScript Crypto jest beznadziejny, pamiętaj. JavaScript nie może uczestniczyć w OAuth 1.0 bez SSL, i nadal musisz przechowywać nazwę użytkownika i hasło klienta lokalnie - co umieszcza to w tej samej kategorii co Digest Auth - jest to bardziej skomplikowane niż HTTP Basic Auth, ale nie jest bardziej bezpieczne .

Token

The użytkownik wysyła nazwę użytkownika i hasło, a w zamian otrzymuje token, który może być używany do uwierzytelniania żądań.

Jest to nieznacznie bezpieczniejsze niż HTTP Basic Auth, ponieważ po zakończeniu transakcji nazwa użytkownika/hasło można usunąć poufne dane. Jest również mniej RESTful, ponieważ tokeny stanowią "stan" i sprawiają, że implementacja serwera jest bardziej skomplikowana.

SSL Still

Problem polega na tym, że nadal musisz wysłać początkową nazwę użytkownika i hasło żeby zdobyć żeton. Poufne informacje nadal dotykają twojego kompromisowego JavaScript.

Aby chronić poświadczenia użytkownika, nadal musisz trzymać atakujących z dala od JavaScript, i nadal musisz wysłać nazwę użytkownika i hasło przez przewód. Wymagane SSL.

Wygaśnięcie Tokena

Powszechne jest egzekwowanie zasad dotyczących tokenów, takich jak "Hej, gdy ten token istnieje zbyt długo, odrzuć go i spraw, aby użytkownik ponownie uwierzytelnił się."lub" jestem prawie pewien, że jedyny dozwolony adres IP użycie tego tokena to XXX.XXX.XXX.XXX". Wiele z tych polityk to całkiem dobre pomysły.

Firesheeping

Jednak używanie tokena bez SSL jest nadal podatne na atak zwany "sidejacking": http://codebutler.github.io/firesheep/

Atakujący nie otrzymuje danych uwierzytelniających użytkownika, ale nadal może udawać użytkownika, co może być bardzo złe.

TL; dr: wysyłanie niezaszyfrowanych tokenów przez przewód oznacza, że atakujący mogą łatwo zdobyć te tokeny i udawaj swojego użytkownika. FireSheep to program, który bardzo to ułatwia.

Oddzielna, Bardziej Bezpieczna Strefa

Im większa jest uruchomiona aplikacja, tym trudniej jest upewnić się, że nie będą w stanie wprowadzić kodu, który zmieni sposób przetwarzania poufnych danych. Czy całkowicie ufasz swojemu CDN? Twoi reklamodawcy? Twoja własna baza kodów?

Wspólne dla danych karty kredytowej i mniej wspólne dla nazwy użytkownika i hasła-niektórzy implementatorzy zachować " wrażliwe wprowadzanie danych " na oddzielnej stronie od reszty aplikacji, stronie, która może być ściśle kontrolowana i zablokowana jak najlepiej, najlepiej taką, która jest trudna do phishowania użytkownikom.

Cookie (po prostu oznacza Token)

Możliwe jest (i powszechne) umieszczenie tokena uwierzytelniania w pliku cookie. Nie zmienia to żadnych właściwości auth z tokenem, jest to bardziej wygodna rzecz. Wszystkie poprzednie argumenty nadal mają zastosowanie.

Session (still just oznacza Token)

Auth sesji to tylko uwierzytelnianie Tokenowe, ale z kilkoma różnicami, które sprawiają, że wygląda to nieco inaczej:

  • użytkownicy zaczynają od nieautoryzowanego tokena.
  • backend utrzymuje obiekt "state", który jest powiązany z tokenem użytkownika.
  • token jest dostarczany w pliku cookie.
  • środowisko aplikacji pobiera szczegóły z dala od Ciebie.

Poza tym nie różni się niczym od Tokena Auth, naprawdę.

To wędruje jeszcze dalej od RESTful implementacji - z obiektów stanu idziesz dalej i dalej w dół ścieżki zwykłego starego RPC na stateful serwera.

OAuth 2.0

OAuth 2.0 analizuje problem " w jaki sposób oprogramowanie a daje oprogramowaniu B dostęp do danych Użytkownika X bez oprogramowania B mającego dostęp do danych logowania użytkownika X."

Implementacja jest standardowym sposobem na uzyskanie tokena przez użytkownika, a następnie przez stronę trzecią service to go " tak, ten użytkownik i ten token pasują, a teraz możesz uzyskać od nas niektóre z ich danych."

Zasadniczo jednak OAuth 2.0 jest tylko protokołem tokenowym. Wykazuje te same właściwości co inne protokoły tokenów - nadal potrzebujesz SSL, aby chronić te tokeny - po prostu zmienia sposób generowania tych tokenów.

OAuth 2.0 może Ci pomóc na dwa sposoby:]}
  • przekazywanie uwierzytelniania / informacji innym
  • Getting Uwierzytelnianie/informacje od innych
Ale kiedy do tego dojdzie, jesteś po prostu... używanie tokenów.

Wróć do pytania

Więc pytanie, które zadajesz, brzmi: "Czy powinienem przechowywać mój token w pliku cookie i mieć automatyczne zarządzanie sesjami mojego środowiska dbać o szczegóły, Czy powinienem przechowywać mój token w Javascript i obsługiwać te szczegóły samodzielnie?"

A odpowiedź brzmi: rób to, co cię uszczęśliwi .

Rzecz o automatyczne zarządzanie sesją polega jednak na tym, że za kulisami dzieje się dla Ciebie wiele magii. Często lepiej samemu kontrolować te szczegóły.

Mam 21 lat więc SSL to tak

Druga odpowiedź brzmi: używaj https do wszystkiego lub bandyci ukradną hasła i tokeny Twoich użytkowników.

 387
Author: Curtis Lassam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:03

Możesz zwiększyć bezpieczeństwo procesu uwierzytelniania za pomocą JWT (JSON Web Tokens) I SSL / HTTPS.

Podstawowy identyfikator Auth / sesji może zostać skradziony przez:

  • MITM attack (Man-In-The-Middle) - bez SSL / HTTPS
  • Intruz uzyskuje dostęp do komputera użytkownika
  • XSS

Używając JWT szyfrujesz dane uwierzytelniające użytkownika i przechowujesz je w kliencie, a następnie wysyłasz je wraz z każdym żądaniem do API, gdzie serwer / API sprawdza token. nie można go odszyfrować/odczytać bez klucza prywatnego (który serwer / API przechowuje potajemnie) Read update .

Nowy (bardziej bezpieczny) przepływ byłby:

Login

    Użytkownik loguje się i wysyła dane logowania do API (przez SSL/HTTPS)
  • API otrzymuje dane logowania
  • If valid:
    • Zarejestruj nową sesję w bazie danych przeczytaj aktualizację
    • Szyfruj identyfikator użytkownika, Identyfikator sesji, adres IP, znacznik czasu itp. w JWT z kluczem prywatnym.
  • W tym celu należy wysłać token JWT z powrotem do Klienta (poprzez SSL/HTTPS) Klient otrzymuje token JWT i przechowuje go w localStorage / cookie

Każde żądanie do API

    Użytkownik wysyła żądanie HTTP do API (poprzez SSL / HTTPS) z przechowywanym tokenem JWT w nagłówku HTTP
  • API odczytuje nagłówek HTTP i odszyfrowuje token JWT za pomocą klucza prywatnego
  • API weryfikuje token JWT, dopasowuje adres IP z żądania HTTP do adresu w tokenie JWT i sprawdza, czy sesja wygasła
  • If valid:
    • Return response with requested content
  • if invalid:
    • Throw exception (403 / 401)
    • wtargnięcie flagi do systemu
    • Wyślij wiadomość ostrzegawczą do użytkownika.

Aktualizacja 30.07.15:

JWT payload/claims można odczytać bez klucz prywatny (tajny) i przechowywanie go w localStorage nie jest bezpieczne. Przepraszam za te fałszywe zeznania. Jednak wydaje się, że działają one na JWE standard (JSON Web Encryption).

Zaimplementowałem to, przechowując roszczenia (userID, exp) w JWT, podpisałem je kluczem prywatnym (tajnym), o którym zna tylko API/backend i zapisałem je jako bezpieczny plik cookie HttpOnly na kliencie. W ten sposób nie można go odczytać przez XSS i nie można nim manipulować, w przeciwnym razie JWT zawiedzie weryfikacja podpisu. Korzystając z pliku cookie secure HttpOnly , upewniasz się, że plik cookie jest wysyłany tylko za pośrednictwem żądań HTTP (niedostępnych dla skryptu) i wysyłany tylko za pośrednictwem bezpiecznego połączenia (HTTPS).

Aktualizacja 17.07.16:

JWTs są z natury bezpaństwowe. Oznacza to, że same unieważniają / wygasają. Dodanie identyfikatora SessionID w oświadczeniach tokena sprawia, że staje się on stateczny, ponieważ jego ważność nie zależy teraz tylko od weryfikacji podpisu i wygaśnięcia data, zależy również od stanu sesji na serwerze. Jednak plusem jest to, że możesz łatwo unieważnić tokeny/sesje, czego nie mogłeś wcześniej z bezpaństwowymi JWTs.

 45
Author: Gaui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-17 21:18:25

Wybrałbym drugi, system tokenów.

Czy wiesz o ember-auth lub ember-simple-auth ? Oba używają systemu opartego na tokenach, jak Ember-simple-auth:

Lekka i dyskretna biblioteka do implementacji tokenów uwierzytelnianie w Ember.aplikacje js. http://ember-simple-auth.simplabs.com

Mają zarządzanie sesjami i są łatwe do podłączenia do istniejących projektów.

Tam jest to również przykładowa wersja Ember App Kit Dla ember-simple-auth: roboczy przykład ember-app-kit przy użyciu Ember-simple-auth do uwierzytelniania OAuth2.

 8
Author: DelphiLynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-07 11:28:25