Tworzę REST API, które wymaga uwierzytelnienia. Ponieważ samo uwierzytelnianie odbywa się za pośrednictwem zewnętrznej usługi sieciowej za pośrednictwem protokołu HTTP, doszedłem do wniosku, że będziemy wydawać tokeny, aby uniknąć wielokrotnego wywoływania usługi uwierzytelniania. Co prowadzi mnie zgrabnie do mojego pierwszego pytania:
Czy to naprawdę lepsze niż wymaganie od klientów używania podstawowego uwierzytelniania HTTP przy każdym żądaniu i buforowanie wywołań po stronie serwera usługi uwierzytelniania?
Zaletą rozwiązania Basic Auth jest to, że nie wymaga pełnej podróży w obie strony do serwera przed rozpoczęciem żądań treści. Tokeny mogą potencjalnie mieć bardziej elastyczny zakres (tj. Przyznawać prawa tylko do określonych zasobów lub akcji), ale wydaje się to bardziej odpowiednie w kontekście OAuth niż mój prostszy przypadek użycia.
Obecnie tokeny są pozyskiwane w następujący sposób:
curl -X POST localhost/token --data "api_key=81169d80...
&verifier=2f5ae51a...
×tamp=1234567
&user=foo
&pass=bar"
Plik api_key
, timestamp
I verifier
są wymagane przez wszystkich żądań. „Weryfikator” jest zwracany przez:
sha1(timestamp + api_key + shared_secret)
Moim zamiarem jest zezwalanie na połączenia tylko od znanych osób i zapobieganie wielokrotnemu używaniu połączeń.
Czy to wystarczy? Underkill? Overkill?
Z tokenem w ręku klienci mogą pozyskiwać zasoby:
curl localhost/posts?api_key=81169d80...
&verifier=81169d80...
&token=9fUyas64...
×tamp=1234567
Dla najprostszego możliwego połączenia wydaje się to okropnie rozwlekłe. Biorąc pod uwagę, że shared_secret
testament zostanie osadzony (przynajmniej) w aplikacji na iOS, z której zakładam, że można ją wyodrębnić, czy oferuje to w ogóle coś poza fałszywym poczuciem bezpieczeństwa?
źródło
Odpowiedzi:
Oddzielę wszystko i rozwiążę każdy problem z osobna:
Poświadczenie
Jeśli chodzi o uwierzytelnianie, baseauth ma tę zaletę, że jest dojrzałym rozwiązaniem na poziomie protokołu. Oznacza to, że „może pojawić się później” problemów, które , zostało już rozwiązanych. Na przykład w przypadku BaseAuth agenci użytkownika wiedzą, że hasło jest hasłem, więc nie buforują go.
Obciążenie serwera uwierzytelniania
Jeśli wydajesz token użytkownikowi, zamiast buforować uwierzytelnianie na serwerze, nadal robisz to samo: buforujesz informacje uwierzytelniające. Jedyna różnica polega na tym, że odpowiedzialność za buforowanie przekazujesz użytkownikowi. Wydaje się, że jest to niepotrzebna praca dla użytkownika bez żadnych korzyści, więc polecam załatwić to w sposób przejrzysty na serwerze, tak jak zasugerowałeś.
Bezpieczeństwo transmisji
Jeśli możesz użyć połączenia SSL, to wszystko, połączenie jest bezpieczne *. Aby zapobiec przypadkowemu wielokrotnemu wykonaniu, możesz odfiltrować wiele adresów URL lub poprosić użytkowników o dołączenie do adresu URL elementu losowego („jednorazowego”).
Jeśli nie jest to możliwe, a przesyłane informacje nie są tajne, polecam zabezpieczyć żądanie hashem, tak jak zasugerowałeś w podejściu tokenowym. Ponieważ hash zapewnia bezpieczeństwo, możesz poinstruować użytkowników, aby podali skrót jako hasło baseauth. Aby zwiększyć niezawodność, zalecam użycie losowego ciągu znaków zamiast znacznika czasu jako „jednorazowego”, aby zapobiec atakom typu „powtórka” (w ciągu tej samej sekundy można by wysłać dwa uzasadnione żądania). Zamiast udostępniać oddzielne pola „shared secret” i „api key”, możesz po prostu użyć klucza API jako wspólnego sekretu, a następnie użyć soli, która się nie zmienia, aby zapobiec atakom tęczowej tablicy. Pole nazwy użytkownika wydaje się dobrym miejscem na umieszczenie nonce, ponieważ jest częścią autoryzacji. Więc teraz masz czysty telefon w ten sposób:
Prawdą jest, że jest to trochę pracochłonne. Dzieje się tak, ponieważ nie używasz rozwiązania na poziomie protokołu (takiego jak SSL). Dlatego dobrym pomysłem może być udostępnienie użytkownikom pewnego rodzaju SDK, aby przynajmniej nie musieli przez to samodzielnie przechodzić. Jeśli musisz to zrobić w ten sposób, uważam, że poziom bezpieczeństwa jest odpowiedni (po prostu zabij).
Bezpieczne tajne przechowywanie
Zależy, komu próbujesz udaremnić. Jeśli uniemożliwiasz osobom mającym dostęp do telefonu użytkownika korzystanie z usługi REST w imieniu użytkownika, dobrym pomysłem byłoby znalezienie jakiegoś API do obsługi kluczy w docelowym systemie operacyjnym i przechowywanie przez SDK (lub implementatora) klucz tam. Jeśli nie jest to możliwe, możesz przynajmniej nieco utrudnić uzyskanie sekretu, szyfrując go i przechowując zaszyfrowane dane i klucz szyfrowania w oddzielnych miejscach.
Jeśli próbujesz uniemożliwić innym dostawcom oprogramowania uzyskanie klucza API, aby zapobiec tworzeniu alternatywnych klientów, prawie działa tylko metoda szyfrowania i przechowywania osobno . To jest krypto whitebox i do tej pory nikt nie wymyślił naprawdę bezpiecznego rozwiązania problemów tej klasy. Jedyne, co możesz zrobić, to nadal wydawać jeden klucz dla każdego użytkownika, aby móc blokować nadużywane klucze.
(*) EDYCJA: połączenia SSL nie powinny być już uważane za bezpieczne bez podejmowania dodatkowych kroków w celu ich weryfikacji .
źródło
Czysty interfejs API RESTful powinien korzystać z podstawowych funkcji standardowych protokołów:
W przypadku protokołu HTTP interfejs RESTful API powinien być zgodny z istniejącymi standardowymi nagłówkami HTTP. Dodanie nowego nagłówka HTTP narusza zasady REST. Nie wymyślaj ponownie koła, używaj wszystkich standardowych funkcji standardów HTTP / 1.1 - w tym kodów odpowiedzi stanu, nagłówków i tak dalej. Usługi sieciowe RESTFul powinny wykorzystywać standardy HTTP i polegać na nich.
Usługi RESTful MUSZĄ BYĆ BEZSTATELOWE. Wszelkie sztuczki, takie jak uwierzytelnianie oparte na tokenach, próbujące zapamiętać stan poprzednich żądań REST na serwerze, naruszają zasady REST. Ponownie, jest to MUSI; to znaczy, jeśli serwer WWW zapisuje jakiekolwiek informacje związane z kontekstem żądania / odpowiedzi na serwerze, próbując nawiązać na serwerze jakąkolwiek sesję, wówczas usługa sieciowa NIE jest bezstanowa. A jeśli NIE jest bezpaństwowy, NIE jest to RESTFul.
Konkluzja: Do celów uwierzytelniania / autoryzacji należy używać standardowego nagłówka autoryzacji HTTP. Oznacza to, że należy dodać nagłówek autoryzacji / uwierzytelniania HTTP w każdym kolejnym żądaniu, które wymaga uwierzytelnienia. REST API powinien być zgodny ze standardami HTTP Authentication Scheme.Szczegóły dotyczące formatowania tego nagłówka są zdefiniowane w standardach RFC 2616 HTTP 1.1 - sekcja 14.8 Autoryzacja RFC 2616 oraz w RFC 2617 HTTP Authentication: Basic and Digest Access Authentication .
Opracowałem usługę RESTful dla aplikacji Cisco Prime Performance Manager. Wyszukaj w Google dokument interfejsu API REST, który napisałem dla tej aplikacji, aby uzyskać więcej informacji na temat zgodności z RESTFul API tutaj . W tej implementacji zdecydowałem się użyć schematu autoryzacji HTTP „Basic”. - sprawdź wersję 1.5 lub nowszą tego dokumentu REST API i wyszukaj autoryzację w dokumencie.
źródło
W sieci protokół stanowy opiera się na posiadaniu tymczasowego tokena, który jest wymieniany między przeglądarką a serwerem (poprzez nagłówek pliku cookie lub przepisywanie identyfikatora URI) przy każdym żądaniu. Ten token jest zwykle tworzony po stronie serwera i jest to fragment nieprzezroczystych danych, który ma określony czas życia, a jego jedynym celem jest identyfikacja określonego internetowego agenta użytkownika. Oznacza to, że token jest tymczasowy i staje się STANEM, który serwer WWW musi utrzymywać w imieniu klienta użytkownika w czasie trwania tej konwersacji. Dlatego komunikacja przy użyciu tokena w ten sposób jest STANOWA. A jeśli konwersacja między klientem a serwerem jest STANOWA, to nie jest RESTful.
Nazwa użytkownika / hasło (wysyłane w nagłówku Authorization) są zwykle utrwalane w bazie danych w celu identyfikacji użytkownika. Czasami użytkownik może mieć na myśli inną aplikację; jednakże nazwa użytkownika / hasło NIGDY nie służy do identyfikowania konkretnego klienta użytkownika WWW. Konwersacja między agentem internetowym a serwerem oparta na użyciu nazwy użytkownika / hasła w nagłówku autoryzacji (po autoryzacji podstawowej HTTP) jest BEZSTANOWA, ponieważ front-end serwera WWW nie tworzy ani nie obsługuje żadnych informacji STANcokolwiek w imieniu konkretnego klienta internetowego klienta. Opierając się na moim zrozumieniu REST, protokół jasno stwierdza, że konwersacja między klientami a serwerem powinna być BEZSTANOWA. Dlatego jeśli chcemy mieć prawdziwą usługę RESTful, powinniśmy użyć nazwy użytkownika / hasła (patrz RFC wspomniany w moim poprzednim poście) w nagłówku Authorization dla każdego połączenia, a NIE rodzaj tokena typu sension (np. Tokeny sesji utworzone na serwerach internetowych , Tokeny OAuth utworzone na serwerach autoryzacji i tak dalej).
Rozumiem, że kilku zwanych dostawców REST używa tokenów, takich jak tokeny akceptacji OAuth1 lub OAuth2, które mają być przekazywane jako „Authorization: Bearer” w nagłówkach HTTP. Jednak wydaje mi się, że używanie tych tokenów do usług RESTful naruszyłoby prawdziwe znaczenie STATELESS, które obejmuje REST; ponieważ te tokeny są tymczasowymi fragmentami danych utworzonymi / utrzymywanymi po stronie serwera w celu zidentyfikowania konkretnego klienta użytkownika sieci WWW na czas trwania konwersacji tego klienta / serwera. Dlatego żadna usługa korzystająca z tych tokenów OAuth1 / 2 nie powinna nazywać się REST, jeśli chcemy trzymać się PRAWDZIWEGO znaczenia protokołu STATELESS.
Rubens
źródło