Wzorzec logowania do interfejsu API REST

181

Tworzę REST api, ściśle przestrzegając sugestii apigee, używając rzeczowników, a nie czasowników, wersji API zapieczętowanej w adresie URL, dwóch ścieżek API na kolekcję, użycie GET POST PUT DELETE itp.

Pracuję nad systemem logowania, ale nie mam pewności co do prawidłowego sposobu logowania użytkowników REST. W tym momencie nie pracuję nad bezpieczeństwem, tylko nad wzorcem logowania lub przepływem. (Później dodamy 2-stopniową OAuth, z HMAC itp.)

Możliwe opcje

  • POST do czegoś w rodzaju https://api...com/v1/login.json
  • PUT na coś takiego https://api...com/v1/users.json
  • Coś, o czym nie pomyślałem ...

Jaki jest właściwy styl REST do logowania użytkowników?

Scott Roepnack
źródło
9
To jest format odpowiedzi. .json mówi serwerowi, aby odpowiedział przy użyciu json, .xml mówi serwerowi, aby odpowiedział w formacie xml. Raczej uczynienie go opcjonalnym parametrem za?. blog.apigee.com/detail/…
Scott Roepnack
28
Nigdy nie widziałem negocjacji treści pod adresem URL, tylko w nagłówkach. W przypadku adresu URL oznacza to utratę korzyści z buforowania i nie tylko.
Oded
10
@ScottRoepnack, powinieneś rozważyć Acceptnagłówek HTTP.
Alessandro Vendruscolo
2
@Oded Jeśli użyłeś Acceptnagłówka, miałbyś również znak Vary: Accept, więc nie wpłynie to na buforowanie. Conneg w rozszerzeniu został omówiony wcześniej ; Zgodziłbym się jednak tam z odpowiedzią Shonzilli.
cmbuckley,
2
@Oded - nie rozumiem. Dlaczego miałbyś stracić korzyści z buforowania, jeśli określisz typ zawartości w adresie URL (jako sufiks .json do ścieżki zapytania lub jako parametr zapytania type = json)? A kim jest „ty” w tym przypadku? Kim jest osoba, która traci korzyści z buforowania? wydaje mi się, że wyniki każdego zapytania można przechowywać w pamięci podręcznej, niezależnie od tego, co znajduje się w ścieżce zapytania lub parametrach.
Cheeso

Odpowiedzi:

138

Principled Design of the Modern Web Architecture autorstwa Roya T. Fieldinga i Richarda N. Taylora , czyli sekwencja prac z całej terminologii REST, zawiera definicję interakcji klient-serwer:

Wszystkie interakcje REST są bezpaństwowe . Oznacza to, że każde żądanie zawiera wszystkie informacje niezbędne łącznikowi do zrozumienia żądania, niezależnie od wszelkich żądań, które mogły je poprzedzać .

To ograniczenie spełnia cztery funkcje, pierwsza i trzecia są ważne w tym konkretnym przypadku:

  • Po pierwsze : eliminuje potrzebę zachowania przez łączniki stanu aplikacji między żądaniami , zmniejszając w ten sposób zużycie zasobów fizycznych i poprawiając skalowalność;
  • Po trzecie : umożliwia pośrednikowi przeglądanie i rozumienie żądania oddzielnie , co może być konieczne, gdy usługi są dynamicznie rearanżowane;

A teraz wróćmy do twojego zabezpieczenia. Każde żądanie powinno zawierać wszystkie wymagane informacje, a autoryzacja / uwierzytelnienie nie jest wyjątkiem. Jak to osiągnąć? Dosłownie wysyłaj wszystkie wymagane informacje przez przewody z każdym żądaniem.

Jednym z przykładów, jak to zrobić, jest kod uwierzytelniania wiadomości oparty na skrócie lub HMAC . W praktyce oznacza to dodawanie skrótu aktualnej wiadomości do każdego żądania. Kod skrótu obliczany przez kryptograficzną funkcję skrótu w połączeniu z tajnym kluczem kryptograficznym . Kryptograficzna funkcja skrótu jest predefiniowana lub stanowi część koncepcji REST kodu na żądanie (na przykład JavaScript). Tajny klucz kryptograficzny powinien być dostarczony przez serwer do klienta jako zasób, a klient używa go do obliczenia kodu skrótu dla każdego żądania.

Istnieje wiele przykładów implementacji HMAC , ale chciałbym, abyś zwrócił uwagę na następujące trzy:

Jak to działa w praktyce

Jeśli klient zna tajny klucz, jest gotowy do pracy z zasobami. W przeciwnym razie zostanie tymczasowo przekierowany (kod stanu 307 Tymczasowe przekierowanie) w celu autoryzacji i uzyskania tajnego klucza, a następnie przekierowany z powrotem do oryginalnego zasobu. W tym przypadku nie ma potrzeby wcześniejszej znajomości (tj. Gdzieś na stałe zakodować), jaki jest adres URL do autoryzacji klienta i można z czasem dostosować ten schemat.

Mam nadzieję, że pomoże Ci to znaleźć właściwe rozwiązanie!

Akim
źródło
23
MAC ma na celu udowodnienie autentyczności wiadomości i ochronę przed manipulacją - nie ma to nic wspólnego z uwierzytelnianiem użytkownika
yrk
1
Dodano jeden z przykładów, jak radzić sobie z uwierzytelniania użytkownika / klienta, nie wiedząc o „ logowania URL ” uprzednich
Akim
Oto kolejne dwa fajne artykuły z przykładami uwierzytelniania bezpaństwowego dla usług REST: blog.jdriven.com/2014/10/... technicalrex.com/2015/02/20/…
Vladimir Rozhkov
41

TL; DR Logowanie dla każdego żądania nie jest wymaganym elementem do implementacji bezpieczeństwa API, ale uwierzytelnianie jest.

Trudno odpowiedzieć na pytanie dotyczące logowania, nie mówiąc ogólnie o bezpieczeństwie. W przypadku niektórych schematów uwierzytelniania nie ma tradycyjnego logowania.

REST nie narzuca żadnych reguł bezpieczeństwa, ale w praktyce najczęściej stosowaną implementacją jest OAuth z uwierzytelnianiem trójstronnym (jak wspomniałeś w swoim pytaniu). Nie ma logowania jako takiego, przynajmniej nie przy każdym żądaniu API. W przypadku uwierzytelniania trójstronnego po prostu używasz tokenów.

  1. Użytkownik zatwierdza klienta API i udziela uprawnień do wysyłania żądań w postaci tokenu o długiej żywotności
  2. Klient API uzyskuje krótkotrwały token przy użyciu długowiecznego.
  3. Klient API wysyła krótkotrwały token z każdym żądaniem.

Ten schemat daje użytkownikowi możliwość cofnięcia dostępu w dowolnym momencie. Praktycznie wszystkie publicznie dostępne interfejsy API RESTful, które widziałem, używają protokołu OAuth do implementacji tego.

Po prostu nie uważam, że powinieneś ująć swój problem (i pytanie) w kategoriach logowania, ale raczej pomyśleć o ogólnym zabezpieczeniu API.

Więcej informacji na temat ogólnego uwierzytelniania interfejsów REST API można znaleźć w następujących zasobach:

Slavo
źródło
Tak, OAuth! Bardzo prosta odpowiedź, powinna być odpowiedzią zaakceptowaną, imho.
Lewita
26

Dużą częścią filozofii REST jest wykorzystanie jak największej liczby standardowych funkcji protokołu HTTP podczas projektowania interfejsu API. Stosując tę ​​filozofię do uwierzytelniania, klient i serwer wykorzystywałyby standardowe funkcje uwierzytelniania HTTP w API.

Ekrany logowania świetnie sprawdzają się w przypadku użytkowników: odwiedź ekran logowania, podaj użytkownika / hasło, ustaw plik cookie, klient zapewnia ten plik cookie we wszystkich przyszłych żądaniach. Nie można oczekiwać, że ludzie używający przeglądarek internetowych będą podawać identyfikator użytkownika i hasło przy każdym indywidualnym żądaniu HTTP.

Jednak w przypadku interfejsu API REST ekran logowania i pliki cookie sesji nie są bezwzględnie konieczne, ponieważ każde żądanie może zawierać poświadczenia bez wpływu na użytkownika; a jeśli klient nie współpracuje w dowolnym momencie, 401można udzielić „nieautoryzowanej” odpowiedzi. RFC 2617 opisuje obsługę uwierzytelniania w protokole HTTP.

TLS (HTTPS) byłby również opcją i pozwoliłby na uwierzytelnianie klienta na serwerze (i odwrotnie) w każdym żądaniu poprzez weryfikację klucza publicznego drugiej strony. Dodatkowo zapewnia to kanałowi premię. Oczywiście, aby to zrobić, konieczna jest wymiana pary kluczy przed komunikacją. (Uwaga: dotyczy to w szczególności identyfikacji / uwierzytelniania użytkownika za pomocą protokołu TLS. Zabezpieczanie kanału za pomocą protokołu TLS / Diffie-Hellman jest zawsze dobrym pomysłem, nawet jeśli nie identyfikujesz użytkownika za pomocą jego klucza publicznego).

Przykład: załóżmy, że token OAuth to Twoje pełne dane logowania. Gdy klient ma token OAuth, można go podać jako identyfikator użytkownika w standardowym uwierzytelnianiu HTTP przy każdym żądaniu. Serwer może zweryfikować token przy pierwszym użyciu i zapisać wynik sprawdzenia w pamięci podręcznej z czasem wygaśnięcia, który jest odnawiany przy każdym żądaniu. Każde żądanie wymagające uwierzytelnienia jest zwracane, 401jeśli nie zostanie podane.

jagoda
źródło
1
„ponieważ każde żądanie może zawierać dane uwierzytelniające bez wpływu na użytkownika”, wymyślono trójstronne uwierzytelnianie i OAuth, ponieważ rzecz w cudzysłowie jest zła. Jeśli podasz poświadczenia przy każdym żądaniu bez mechanizmu na serwerze, aby je odwołać, byłoby to niebezpieczne, gdyby było używane bez SSL.
Slavo
1
Zawsze, gdy pojawia się pojęcie użytkowników, coś musi zostać przekazane od klienta do serwera, aby zidentyfikować użytkownika. Token OAuth może z pewnością służyć jako „poświadczenie” zamiast rzeczywistej kombinacji użytkownika i hasła. Zabezpieczanie kanału za pomocą TLS jest z pewnością zawsze dobrą rzeczą, ale to prawie nie ma znaczenia. Nawet jeśli korzystasz z pliku cookie, z każdym żądaniem do serwera nadal jest wysyłany jakiś rodzaj tokenów, tylko z nagłówkiem pliku cookie zamiast nagłówka uwierzytelniania.
jagoda
A jeśli z jakiegoś powodu nie używasz TLS ani OAuth, czy wysyłanie użytkownika / hasła za każdym razem jest naprawdę gorsze niż wysyłanie go tylko raz? Jeśli osoba atakująca może uzyskać użytkownika / hasło, prawdopodobnie może również uzyskać plik cookie sesji.
jagoda
Różnica między plikiem cookie a nagłówkiem uwierzytelniania będącym poświadczeniami polega na tym, że pliki cookie są zawsze powiązane z określoną domeną. Oznacza to, że kiedy API otrzymuje plik cookie, wie, skąd pochodzi (zostało wcześniej napisane przez tę samą domenę). Z nagłówkiem nigdy nie wiesz i musisz zaimplementować w tym celu określone kontrole. Ogólnie zgadzam się, oba są poświadczeniami, ale myślę, że przekazanie poświadczeń nie oznacza logowania. Logowanie to aktywna czynność otwierania drzwi. W przypadku uwierzytelniania 3-way, tylko pierwsze zatwierdzenie klienta byłoby logowaniem.
Slavo