Czy token_authenticatable devise jest bezpieczny?

79

Tworzę prosty interfejs API za pomocą Rails API i chcę się upewnić, że jestem na dobrej drodze. Używam devise do obsługi logowań i zdecydowałem się na token_authenticatableopcję Devise , która generuje klucz API, który musisz wysłać z każdym żądaniem.

Łączę API z front-endem szkieletowym / marionetkowym i ogólnie zastanawiam się, jak mam obsługiwać sesje. Moją pierwszą myślą było po prostu przechowywanie klucza API w pamięci lokalnej lub pliku cookie i pobieranie go podczas ładowania strony, ale coś w przechowywaniu klucza API w ten sposób przeszkadzało mi z punktu widzenia bezpieczeństwa. Czy nie byłoby łatwo złapać klucz API, przeszukując lokalną pamięć / plik cookie lub węsząc każde żądanie, które przechodzi i używać go do podszywania się pod tego użytkownika w nieskończoność? Obecnie resetuję klucz API przy każdym logowaniu, ale nawet to wydaje się częste - za każdym razem, gdy logujesz się na dowolnym urządzeniu, oznacza to, że będziesz wylogowywany na każdym innym, co jest trochę uciążliwe. Gdybym mógł porzucić ten reset, czuję, że poprawiłby się z punktu widzenia użyteczności.

Mogę się tu całkowicie mylić (i mam nadzieję, że się mylę), czy ktoś może wyjaśnić, czy uwierzytelnianie w ten sposób jest niezawodnie bezpieczne, a jeśli nie, jaka byłaby dobra alternatywa? Ogólnie rzecz biorąc, szukam sposobu, w jaki mogę bezpiecznie utrzymywać użytkowników „zalogowanych” w dostępie do interfejsu API bez częstego wymuszania ponownego uwierzytelniania.

Jeff Escalante
źródło

Odpowiedzi:

190

token_authenticatablejest podatny na ataki czasowe, co bardzo dobrze wyjaśniono w tym poście na blogu . Te ataki były powodem token_authenticatableusunięcia z Devise 3.1. Więcej informacji można znaleźć w poście na blogu plataformatec .

Aby mieć najbezpieczniejszy mechanizm uwierzytelniania tokena, token:

  1. Musi być wysłany przez HTTPS.

  2. Musi być losowy, kryptograficzny.

  3. Należy bezpiecznie porównać.

  4. Nie mogą być przechowywane bezpośrednio w bazie danych. Można tam przechowywać tylko skrót tokena. (Pamiętaj, token = hasło. Nie przechowujemy haseł w postaci zwykłego tekstu w bazie danych, prawda?)

  5. Zgodnie z pewną logiką powinien wygasnąć.

Jeśli zrezygnujesz z niektórych z tych punktów na rzecz użyteczności, otrzymasz mechanizm, który nie jest tak bezpieczny, jak mógłby być. To takie proste. Powinieneś być wystarczająco bezpieczny, jeśli spełniasz pierwsze trzy wymagania i ograniczysz dostęp do bazy danych.

Rozszerzając i wyjaśniając moją odpowiedź:

  1. Użyj protokołu HTTPS . Jest to zdecydowanie najważniejszy punkt, ponieważ dotyczy on snifferów.

    Jeśli nie używasz protokołu HTTPS, wiele może się nie udać. Na przykład:

  2. Wygeneruj swój token za pomocą:

    Aby wyjaśnić, dlaczego jest to konieczne, sugeruję przeczytanie pliku sysrandomREADME i wpisu na blogu Jak generować bezpieczne liczby losowe w różnych językach programowania .

  3. Znajdź rekord użytkownika za pomocą identyfikatora użytkownika, adresu e-mail lub innego atrybutu. Następnie porównaj token tego użytkownika z tokenem żądania z Devise.secure_compare(user.auth_token, params[:auth_token]. Jeśli korzystasz z Railsów 4.2.1+, możesz również użyć ActiveSupport::SecurityUtils.secure_compare.

    Czy nie znaleźliśmy rekord użytkownika z wyszukiwarki Rails podobnego User.find_by(auth_token: params[:auth_token]). Jest to podatne na ataki czasowe!

  4. Jeśli zamierzasz mieć kilka aplikacji / sesji w tym samym czasie na użytkownika, masz dwie możliwości:

    • Przechowuj niezaszyfrowany token w bazie danych, aby można go było udostępniać między urządzeniami. To zła praktyka, ale myślę, że możesz to zrobić w imię UX (i jeśli ufasz swoim pracownikom z dostępem do bazy danych).

    • Przechowuj tyle zaszyfrowanych tokenów na użytkownika, ile chcesz zezwolić na bieżące sesje. Jeśli więc chcesz zezwolić na 2 sesje na 2 różnych urządzeniach, zachowaj 2 różne skróty tokenów w bazie danych. Ta opcja jest trochę trudniejsza do wdrożenia, ale zdecydowanie bezpieczniejsza. Ma również tę zaletę, że pozwala zapewnić użytkownikom opcję zakończenia bieżących aktywnych sesji na określonych urządzeniach poprzez unieważnienie ich tokenów (tak jak robią to GitHub i Facebook).

  5. Powinien istnieć jakiś mechanizm, który powoduje wygaśnięcie tokena. Wdrażając ten mechanizm, weź pod uwagę kompromis między UX a bezpieczeństwem.

    Google traci ważność tokena, jeśli nie był używany przez sześć miesięcy .

    Facebook traci ważność tokena, jeśli nie był używany przez dwa miesiące :

    Natywne aplikacje mobilne korzystające z SDK Facebooka otrzymają długoterminowe tokeny dostępu, ważne przez około 60 dni. Te tokeny będą odświeżane raz dziennie, gdy osoba korzystająca z Twojej aplikacji wyśle ​​żądanie do serwerów Facebooka. Jeśli nie zostaną zgłoszone żadne żądania, token wygaśnie po około 60 dniach i osoba będzie musiała ponownie przejść przez proces logowania, aby uzyskać nowy token.

  6. Zaktualizuj do Rails 4, aby korzystać z zaszyfrowanego magazynu plików cookie. Jeśli nie możesz, zaszyfruj samodzielnie magazyn plików cookie, jak sugerowano tutaj . Nie byłoby absolutnie żadnego problemu w przechowywaniu tokena uwierzytelniania w zaszyfrowanym magazynie plików cookie.

Powinieneś także mieć plan awaryjny, na przykład zadanie prowizji, aby zresetować podzbiór tokenów lub każdy pojedynczy token w bazie danych.

Na początek możesz zapoznać się z tym streszczeniem (autorstwa jednego z autorów Devise) na temat implementacji uwierzytelniania tokenów w Devise. Na koniec, Railscast o zabezpieczaniu API powinien być pomocny.

Ashitaka
źródło
Świetnie, to bardzo pomaga - dziękuję! To prawie na pewno da właściwą odpowiedź. Bounty jest twoje, jeśli dodasz do tego swoją opinię / rekomendację (konkretnie) na temat najlepszego sposobu obsługi uwierzytelniania API :)
Jeff Escalante
1
Podczas gdy obie metody generują losowy ciąg, urlsafe_base64generuje ciąg bezpieczny dla adresu URL. Wszystko jest w nazwie. Jeśli nie chcesz używać tokena w swoim adresie URL ( czego nie powinieneś ), użyj hex.
Ashitaka,
2
token! = hasło. Nie ma nic złego w przechowywaniu tokena w postaci zwykłego tekstu. Problem z przechowywaniem haseł w postaci zwykłego tekstu polega na tym, że hasła można użyć w innym miejscu, co nie powinno mieć miejsca w przypadku tokena.
fatfrog
2
@fatfrog Nr. Token == hasło. Jeśli haker lub niezadowolony pracownik ma dostęp do Twojej bazy danych, nie powinien mieć możliwości uwierzytelnienia się jako określony użytkownik lub administrator.
Ashitaka,
1
Nie zgadzam się, jeśli haker lub niezadowolony pracownik ma dostęp do Twojej bazy danych, to tokeny są ostatnią rzeczą, o którą musisz się martwić. Mają już Twoje dane.
fatfrog
3

Możesz spróbować użyć rails4 ze swoim API, zapewnia większe bezpieczeństwo i używa devise 3.1.0rc

Aby uzyskać token, magazyn sesji, możesz przejść przez http://ruby.railstutorial.org/chapters/sign-in-sign-out i http://blog.bigbinary.com/2013/03/19/cookies-on-rails .html, aby był bardziej zrozumiały.

W końcu powinieneś przejść przez ten rodzaj szyfrowania i deszyfrowania „ Nie można odszyfrować przechowywanych zaszyfrowanych danych ”, aby uzyskać większe bezpieczeństwo.

Gaurav Sharma
źródło
Czy ktoś ma przykładową niestandardową alternatywę?
Daniel Morris,