Kiedy budujesz aplikacje w stylu SPA przy użyciu frameworków takich jak Angular, Ember, React itp., Co ludzie uważają za najlepsze praktyki uwierzytelniania i zarządzania sesjami? Mogę wymyślić kilka sposobów rozważenia rozwiązania problemu.
Traktuj to nie inaczej niż uwierzytelnianie za pomocą zwykłej aplikacji internetowej, zakładając, że interfejs API i interfejs użytkownika mają tę samą domenę pochodzenia.
Wymagałoby to prawdopodobnie posiadania pliku cookie sesji, przechowywania sesji po stronie serwera i prawdopodobnie pewnego punktu końcowego interfejsu API sesji, do którego uwierzytelniony internetowy interfejs użytkownika może trafić, aby uzyskać bieżące informacje o użytkowniku, aby pomóc w personalizacji, a nawet określeniu ról / zdolności po stronie klienta. Serwer nadal wymuszałby oczywiście zasady chroniące dostęp do danych, interfejs użytkownika używałby tych informacji do dostosowania sposobu działania.
Traktuj to jak klienta zewnętrznego korzystającego z publicznego interfejsu API i uwierzytelnij się za pomocą systemu tokenów podobnego do OAuth. Ten mechanizm tokenu byłby używany przez interfejs użytkownika klienta do uwierzytelniania każdego żądania przesłanego do interfejsu API serwera.
Nie jestem zbytnio ekspertem, ale nr 1 wydaje się być całkowicie wystarczający w zdecydowanej większości przypadków, ale naprawdę chciałbym usłyszeć bardziej doświadczone opinie.
źródło
Odpowiedzi:
Pytanie to zostało poruszone w nieco innej formie, tutaj:
RESTful Authentication
Ale to rozwiązuje to po stronie serwera. Spójrzmy na to od strony klienta. Zanim to jednak zrobimy, istnieje ważne preludium:
JavaScript Crypto jest beznadziejny
Artykuł Matasano na ten temat jest znany, ale zawarte w nim lekcje są dość ważne:
https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-consanted-harmful/
Podsumowując:
<script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
I aby dodać własny wniosek:
To sprawia, że wiele schematów uwierzytelniania RESTful jest niemożliwych lub niemądrych, jeśli zamierzasz używać klienta JavaScript. Spójrzmy!
Podstawowe uwierzytelnianie HTTP
Przede wszystkim uwierzytelnianie podstawowe HTTP. Najprostszy schemat: wystarczy podać nazwę i hasło przy każdym żądaniu.
To oczywiście wymaga protokołu SSL, ponieważ przekazujesz nazwę i hasło zakodowane w Base64 (odwracalnie) przy każdym żądaniu. Każdy, kto słucha przez linię, może w prosty sposób wyodrębnić nazwę użytkownika i hasło. Większość argumentów „Autoryzacja podstawowa jest niepewna” pochodzi z miejsca „Autoryzacja podstawowa przez HTTP”, co jest okropnym pomysłem.
Przeglądarka zapewnia wbudowaną obsługę uwierzytelniania podstawowego HTTP, ale jest brzydka jak grzech i prawdopodobnie nie należy jej używać w swojej aplikacji. Alternatywą jest jednak przechowywanie nazwy użytkownika i hasła w JavaScript.
To jest najbardziej RESTful rozwiązanie. Serwer nie wymaga żadnej wiedzy o stanie i uwierzytelnia każdą indywidualną interakcję z użytkownikiem. Niektórzy entuzjaści REST (głównie słowianie) twierdzą, że utrzymywanie dowolnego stanu jest herezją i będą pienieć na ustach, jeśli pomyślisz o jakiejkolwiek innej metodzie uwierzytelnienia. Istnieją teoretyczne korzyści z tego rodzaju zgodności ze standardami - jest obsługiwany przez Apache po wyjęciu z pudełka - możesz przechowywać swoje obiekty jako pliki w folderach chronionych plikami .htaccess, jeśli chcesz.
Problemem ? Buforujesz po stronie klienta nazwę użytkownika i hasło. Daje to evil.ru lepszy efekt - nawet najbardziej podstawowa słabość XSS może spowodować, że klient przesyła swoją nazwę użytkownika i hasło na zły serwer. Możesz spróbować zmniejszyć to ryzyko, mieszając i soląc hasło, ale pamiętaj: JavaScript Crypto jest beznadziejny . Możesz zmniejszyć to ryzyko, pozostawiając je do obsługi podstawowego uwierzytelniania przeglądarki, ale ... brzydkie jak grzech, jak wspomniano wcześniej.
HTTP Digest Auth
Czy możliwe jest uwierzytelnianie Digest w jQuery?
Bardziej „bezpieczne” uwierzytelnianie, jest to wyzwanie mieszania żądania / odpowiedzi. 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 jest bardziej bezpieczny .
Uwierzytelnianie zapytania z dodatkowymi parametrami podpisu.
Kolejne bardziej „bezpieczne” uwierzytelnianie, w którym szyfrujesz parametry za pomocą danych nonce i timing (w celu ochrony przed atakami powtarzającymi się i timing) i wysyłasz. Jednym z najlepszych przykładów tego jest protokół OAuth 1.0, który, o ile mi wiadomo, jest dość ciekawym sposobem na wdrożenie uwierzytelnienia na serwerze REST.
http://tools.ietf.org/html/rfc5849
Och, ale nie ma żadnych klientów OAuth 1.0 dla JavaScript. Dlaczego?
JavaScript Crypto jest beznadziejna , pamiętaj. JavaScript nie może uczestniczyć w OAuth 1.0 bez protokołu SSL, a nadal musisz przechowywać nazwę użytkownika i hasło klienta lokalnie - co stawia tę kategorię jako uwierzytelnianie szyfrowane - jest bardziej skomplikowane niż uwierzytelnianie podstawowe HTTP, ale nie jest bardziej bezpieczne .
Znak
Użytkownik wysyła nazwę użytkownika i hasło, aw zamian otrzymuje token, którego można użyć do uwierzytelnienia żądań.
Jest to nieznacznie bezpieczniejsze niż uwierzytelnianie podstawowe HTTP, ponieważ po zakończeniu transakcji nazwa użytkownika / hasło możesz odrzucić wrażliwe dane. Jest również mniej RESTful, ponieważ tokeny stanowią „stan” i komplikują implementację serwera.
SSL wciąż
Pociesz się jednak, że nadal musisz wysłać tę początkową nazwę użytkownika i hasło, aby otrzymać token. Wrażliwe informacje nadal dotykają Twojego skompromitowanego JavaScript.
Aby chronić dane uwierzytelniające użytkownika, nadal musisz powstrzymywać atakujących przed JavaScript, a także przesyłać nazwę użytkownika i hasło przez sieć. Wymagany SSL.
Wygaśnięcie tokena
Często egzekwowane są zasady dotyczące tokenów, takie jak „hej, gdy ten token jest za długi, odrzuć go i ponownie uwierzytelnij użytkownika”. lub „Jestem pewien, że jedynym adresem IP, który może używać tego tokena, jest
XXX.XXX.XXX.XXX
”. Wiele z tych zasad to całkiem dobre pomysły.Ogień
Jednak użycie tokena Bez protokołu SSL jest nadal podatne na atak o nazwie „sidejacking”: http://codebutler.github.io/firesheep/
Atakujący nie uzyskuje poświadczeń użytkownika, ale nadal może udawać, że jest użytkownikiem, co może być dość złe.
tl; dr: Wysyłanie niezaszyfrowanych tokenów przez sieć oznacza, że atakujący mogą łatwo zdobyć te tokeny i udawać, że są użytkownikiem. FireSheep to program, który sprawia, że jest to bardzo łatwe.
Oddzielna, bezpieczniejsza strefa
Im większa aplikacja, którą uruchamiasz, tym trudniej jest absolutnie upewnić się, że nie będą w stanie wstrzyknąć kodu, który zmieni sposób przetwarzania wrażliwych danych. Czy całkowicie ufasz swojemu CDN? Twoi reklamodawcy? Twoja własna baza kodu?
Wspólne dla danych karty kredytowej, a rzadziej dla nazwy użytkownika i hasła - niektórzy realizatorzy przechowują „poufne dane” na innej stronie niż reszta aplikacji, strona, którą można ściśle kontrolować i zablokować najlepiej, jak to możliwe, najlepiej taką, która jest trudny do wyłudzenia użytkowników.
Cookie (oznacza po prostu Token)
Możliwe jest (i często) umieszczanie tokena uwierzytelniającego w pliku cookie. Nie zmienia to żadnych właściwości uwierzytelniania za pomocą tokena, jest to raczej wygoda. Wszystkie poprzednie argumenty nadal obowiązują.
Sesja (wciąż oznacza tylko Token)
Session Auth to po prostu uwierzytelnianie tokena, ale z kilkoma różnicami, które sprawiają, że wygląda to nieco inaczej:
Poza tym tak naprawdę nie różni się niczym od Token Auth.
Wędruje to jeszcze dalej od implementacji RESTful - z obiektami stanu idziesz dalej i dalej ścieżką zwykłego RPC na stanowym serwerze.
OAuth 2.0
OAuth 2.0 analizuje problem: „W jaki sposób oprogramowanie A zapewnia oprogramowaniu B dostęp do danych użytkownika X bez konieczności posiadania przez oprogramowanie B dostępu do danych logowania użytkownika X”.
Implementacja jest bardzo standardowym sposobem dla użytkownika, aby uzyskać token, a następnie dla usługi innej firmy, aby przejść „tak, ten użytkownik i ten token pasują, a teraz możesz uzyskać od nas niektóre ich dane”.
Zasadniczo jednak OAuth 2.0 to tylko protokół tokena. Wykazuje te same właściwości, co inne protokoły tokenów - nadal potrzebujesz SSL do ochrony tych tokenów - po prostu zmienia sposób ich generowania.
Są dwa sposoby, w jakie OAuth 2.0 może ci pomóc:
Ale jeśli chodzi o to, po prostu ... używasz tokenów.
Powrót do twojego pytania
Pytanie, które zadajesz, brzmi: „czy powinienem przechowywać mój token w pliku cookie i pozwolić, aby automatyczne zarządzanie sesjami w moim środowisku zadbało o szczegóły, czy też powinienem przechowywać mój token w JavaScript i sam z nimi obchodzić?”
Odpowiedź brzmi: rób wszystko, co czyni cię szczęśliwym .
Jednak automatyczne zarządzanie sesjami polega na tym, że za kulisami dzieje się wiele magii. Często lepiej jest samemu kontrolować te szczegóły.
Mam 21 lat, więc SSL to tak
Inna odpowiedź brzmi: użyj https do wszystkiego, a bandyci ukradną hasła i tokeny użytkowników.
źródło
It's also less RESTful, as tokens constitute "state and make the server implementation more complicated."
REST Token (1) wymaga, aby serwer był bezstanowy. Token przechowywany po stronie klienta nie reprezentuje stanu w żaden znaczący sposób dla serwera. (2) Znacznie bardziej skomplikowany kod po stronie serwera nie ma nic wspólnego z RESTfulness.lol_nope_send_it_to_me_instead
Podobało mi się nazwa tej funkcji: DMożesz zwiększyć bezpieczeństwo procesu uwierzytelniania, używając JWT (tokeny WWW JSON) i SSL / HTTPS.
Podstawowy identyfikator uwierzytelnienia / sesji można ukraść za pomocą:
Korzystając z JWT, szyfrujesz dane uwierzytelniające użytkownika i zapisujesz je w kliencie, i 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 / interfejs API przechowuje potajemnie).Przeczytaj aktualizację .Nowy (bezpieczniejszy) przepływ byłby:
Zaloguj sie
Każde żądanie do API
Zaktualizowano 30.07.15:
Ładunek / roszczenia JWT można odczytać bez klucza prywatnego (tajnego) i przechowywanie go w localStorage nie jest bezpieczne. Przykro mi z powodu tych fałszywych oświadczeń. Wydaje się jednak, że pracują na standardzie JWE (JSON Web Encryption) .
Zaimplementowałem to, przechowując oświadczenia (userID, exp) w JWT, podpisałem go kluczem prywatnym (tajnym), o którym API / backend wie tylko i zapisałem 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 nie powiedzie się weryfikacja podpisu. Korzystając z bezpiecznego pliku cookie 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).
Zaktualizowano 17.07.16:
JWT są z natury bezpaństwowcami. Oznacza to, że same unieważniają / wygasają. Dodając identyfikator SessionID do oświadczeń tokena, nadajesz mu status, ponieważ jego ważność nie zależy teraz tylko od weryfikacji podpisu i daty wygaśnięcia, ale także od stanu sesji na serwerze. Zaletą jest jednak to, że możesz łatwo unieważnić tokeny / sesje, czego nie było wcześniej w przypadku bezpaństwowych JWT.
źródło
Wybrałbym drugi system tokenów.
Czy wiesz o ember-auth lub ember-simple-auth ? Oboje używają systemu opartego na tokenach, takich jak stany ember-simple-auth:
Mają zarządzanie sesjami i można je łatwo podłączyć do istniejących projektów.
Istnieje również przykładowa wersja ember-simple-auth Ember App Kit: działający przykład ember-app-kit używającej ember-simple-auth do uwierzytelniania OAuth2.
źródło