Jak zaprojektować uwierzytelnianie użytkownika z aplikacji klienckich?

14

Tworzę aplikację, która będzie obsługiwać wielu użytkowników. Chodzi o to, że nie jestem w stanie dowiedzieć się, jak uwierzytelnić klienta / użytkownika.

Tworzę aplikację, taką jak http://quickblox.com/, w której podam poświadczenia moim użytkownikom, którzy wykorzystają je do zbudowania aplikacji N , w których nie mogą podać swojej nazwy użytkownika i hasła w celu uwierzytelnienia.

Załóżmy, że idzie to w następujący sposób. (Podobnie jak QuickBlox)

1. Użytkownik tworzy konto w mojej witrynie.
2. Użytkownik może utworzyć N kluczy API i tajnych poświadczeń. (W przypadku wielu aplikacji)
3. Użytkownik użyje tych poświadczeń w swoich aplikacjach (Android, iOS, JavaScript itp.), Aby porozmawiać z moimi interfejsami API REST.
(Interfejsy API REST mają dostęp do odczytu i zapisu).

Moje obawy?

Użytkownicy umieszczą swoje poświadczenia (klucz API i klucz tajny) w tworzonych przez siebie aplikacjach, co jeśli ktoś zdobędzie te klucze i spróbuje naśladować użytkownika? (Dekompilując APK lub bezpośrednio patrząc na kod JavaScript.

Czy gdzieś się mylę?
Jestem zdezorientowany, aby zaprojektować ten trzypoziomowy mechanizm użytkownika.

Alok Patel
źródło
To, co próbujesz zrobić, nie jest możliwe.
user253751,
Istnieje wiele aplikacji, które robią to samo. Nie jestem pewien, w jaki sposób uwierzytelniają użytkowników.
Alok Patel
Czy próbujesz uwierzytelnić aplikację kliencką lub użytkownika ? Wiele aplikacji uwierzytelnia użytkowników. Żadna z nich nie uwierzytelnia aplikacji klienckich w niezniszczalny sposób.
user253751,
Tak, przez klienta mam na myśli, że chcę uwierzytelnić tylko użytkownika.
Alok Patel
4
No cóż, wtedy w najbardziej typowym systemie uwierzytelniania wystarczy, że użytkownik wprowadzi swoją nazwę użytkownika i hasło, a następnie wyśle ​​je bezpiecznie do serwera, który je sprawdzi i (bezpiecznie) zwróci token sesji, a następnie wyśle ​​token sesji każde przyszłe żądanie.
user253751,

Odpowiedzi:

7

Od kilku lat projektuję interfejsy API REST. Za bardzo się martwisz. Ostatnio inny użytkownik na tym forum zadał pytanie, gdzie martwił się przechowywaniem punktów końcowych URI w swoim kodzie JavaScript po stronie klienta .

Obowiązują te same zasady, co w przypadku programisty JavaScript. Jeśli zezwalasz osobom z zewnątrz na integrację interfejsu API, interfejs API ma taką samą widoczność jak zwykła witryna i powinieneś traktować go w ten sam sposób.

Cytat z oryginalnej odpowiedzi:

Gdy tworzysz witrynę internetową i nie chcesz, aby użytkownicy coś robili, nie wdrażasz tej funkcji ani nie zabraniasz jej niektórym użytkownikom. Z interfejsem API REST, który powinien mieć publiczne punkty końcowe, jest prawie taki sam, musisz traktować go jak publiczną stronę internetową.

Interfejs API REST powinien być na tyle solidny, aby nie pozwalał na nieprawidłowe operacje, takie jak dostęp do danych od innego użytkownika.

Należy zaprojektować tokeny dostępu do aplikacji, aby zezwalały tylko na operacje, które mają być dozwolone. Możesz mieć dwa rodzaje tokenów dostępu:

  • token główny : może być używany przez twórcę aplikacji i zapewniać więcej funkcji z interfejsu API,
  • token aplikacji : token, który faktycznie byłby przechowywany w aplikacjach i miałby ograniczony dostęp do interfejsu API, tylko do operacji, które nie mogą uszkodzić danych użytkownika ani programisty aplikacji.

Ale co ktoś dekoduje kod źródłowy, usuwa tokeny z aplikacji, dowiaduje się, jakie są publiczne punkty końcowe i nadużywa twojej usługi internetowej?

Jeśli nie zarządzasz bezpośrednio aplikacjami korzystającymi z interfejsu API, nic tak naprawdę nie zabrania korzystania z interfejsu API w ten sam sposób bezpośrednio z aplikacji.

Andy
źródło
Jak dotąd dobre wyjaśnienie, mam jednak pytanie. Powiedzmy, że jeden z moich interfejsów API jest zbudowany do pobierania wszystkich powiązanych danych (A + B) mojego użytkownika . Mój użytkownik używa tego interfejsu API w swojej aplikacji, aby uzyskać filtered data(B) tylko swojego użytkownika . Czy jest teraz możliwe, czy istnieje jakieś obejście, aby uniemożliwić trzeciemu użytkownikowi kradzież wszystkich danych (A + B) mojego użytkownika . Czy ma sens?
Alok Patel,
@AlokPatel Nie ma innego sposobu obejścia problemu niż po prostu nie wdrażanie tej funkcji. Kiedy to robisz, zawsze istnieje ryzyko, że ktoś sfałszuje swój skrót tożsamości, aby wyglądać jak ktoś inny. Programowo twój problem nie może zostać rozwiązany. Jak już powiedziałem, jeśli nie chcesz oferować jakiejś funkcjonalności, nie implementuj jej. Ponieważ aplikacje prawdopodobnie przechowują w nich skrót logowania, po wdrożeniu aplikacji, podobnie jak klucz API i nie jest on publicznie dostępny.
Andy
Dziękuję za wyjaśnienie. Do tej pory doszedłem do wniosku, że interfejsy API REST są takie same, jak każda witryna, którą wdrażamy, są tak otwarte jak witryna. Jeśli nie chcę, aby użytkownik coś zrobił, po prostu nie implementuję tego w moich interfejsach API REST. Mogę więc mieć osobne klucze do bibliotek klienta (Android, iOS, JS), które mogą zostać naruszone przez mniejszą funkcjonalność, oraz różne klucze, które będą używane po stronie serwera (PHP, Java, node.js) z rozszerzoną funkcjonalnością. Mam nadzieję, że to zadziała!
Alok Patel,
Czy możesz wyjaśnić mi swój punkt widzenia? Chyba że bezpośrednio zarządzasz rozwojem aplikacji korzystających z twojego API
Alok Patel
@AlokPatel Chodziło mi o to, że teraz martwisz się, że dasz komuś dostęp do interfejsu API, może on rozpowszechnić dostęp i zacząć nadużywać interfejsu API. Jeśli nie masz kontroli nad tworzeniem aplikacji korzystających z interfejsu API, mogą zrobić to samo, nawet samodzielnie.
Andy,
5

Twój problem jest nie tyle techniczny, co biznesowy.

Załóżmy, że masz interfejs API, który sprzedajesz swoim klientom (twórcom aplikacji) za zryczałtowaną stawkę 100 GBP rocznie, nieograniczony dostęp.

No cóż, oczywiście mogę kupić twoją usługę za 100 £ i sprzedać ją 10 osobom po 50 $ każda. Nie chcesz tego! więc starasz się wymyślić ograniczenie, które pozwoli ci sprzedawać API bez narażania się na arbitraż.

  • Jeśli ograniczysz tylko liczbę aplikacji, klient może utworzyć jedną aplikację, która akceptuje połączenia z innych aplikacji i przekazuje je dalej.

  • Jeśli ograniczysz użytkowników, ponownie klient może ukryć użytkowników za własnym uwierzytelnieniem i wyglądać na jednego użytkownika.

To, co musisz zrobić, to przekazać klientowi koszt każdego połączenia API. tj. opłata za połączenie API lub ustaw limit połączeń na rok.

To popycha ten sam problem arbitrażu na twoich klientów. Zmusza ich do wprowadzenia środków zapobiegających kradzieży kluczy przez użytkowników. W takim przypadku ukrywanie interfejsu API za interfejsem użytkownika uwierzytelnionego przez użytkownika.

Ewan
źródło
Już planowałem ograniczyć użycie mojego interfejsu API w oparciu o liczbę wywołań API, więc problem związany z biznesem nie jest w tej chwili moim zmartwieniem, martwię się tylko o kradzież kluczy.
Alok Patel,
Z biznesowego punktu widzenia problemu nie da się rozwiązać. Niestety nie można również rozwiązać tego programowo.
Andy
1
Problem biznesowy rozwiązuje się, naliczając opłatę za połączenie. Jeśli klucz zostanie skradziony, klient przegrywa. Nie ty. Po prostu daj swojemu klientowi sposób na anulowanie skradzionych kluczy i powiedz, że od niego zależy, aby mieć pośredni interfejs API, aby zapobiec nadużyciom
Ewan
2

Wszystkie pozostałe odpowiedzi wydają się sugerować, że problem przechowywania sekretu w aplikacji na urządzeniach konsumenckich nie jest możliwy do rozwiązania.

Jasne, że tak.

Dwie zasady (szczegóły implementacji zostaną spełnione):

  1. Rzeczywiste punkty końcowe uwierzytelnienia muszą być anonimowo otwarte dla ogółu społeczeństwa.
  2. Serwer musi w jakiś sposób być zaangażowany w uwierzytelnianie klienta i dostarczanie klucza API.

Biorąc pod uwagę, że jeśli klient wysyła żądanie do punktu końcowego uwierzytelnianie z poświadczeniami, a uwierzytelnia it serwera, serwer może wygenerować dynamicznego tymczasowego tokenu (czas oparte tymczasowy znaczenia). Ten token powinien zostać zapamiętany w kliencie i wysłany z kolejnymi żądaniami.

Będziesz potrzebował mechanizmu okresowego „odświeżania” tokena, co oznacza, że ​​dostaniesz nowy. Wystarczy zbudować punkt końcowy REST, który pozwala wygenerować nowy token z istniejącego, aby uniknąć konieczności ponownego uwierzytelnienia na podstawie poświadczeń.

Jeśli próbujesz uniknąć ponownego uwierzytelnienia użytkownika końcowego, uwierzytelnienie to może być początkową jednorazową konfiguracją aplikacji po jej zainstalowaniu.

To rozwiązanie po prostu pozwala uniknąć konieczności przechowywania statycznego tokena osadzonego w pliku binarnym aplikacji. Token jest generowany w locie przez serwer tylko w odpowiedzi na udane uwierzytelnienie. Aby złośliwy użytkownik mógł sprawdzić twoją aplikację i spróbować uzyskać nieautoryzowany dostęp do interfejsu API, musiałby się uwierzytelnić tak samo jak wszyscy inni.

Brandon
źródło
Ciekawe rozwiązanie, choć zależy głównie od tego, jak klienci OP będą kodować swoją aplikację. To powiedziawszy, proponowane podejście może pozwolić na przechowywanie strony serwera kluczy API, a po udanym uwierzytelnieniu ich własnych użytkowników może przekazać klucz tymczasowy do użycia przez rzeczywistą aplikację. w ten sposób nic nie jest przechowywane w samej aplikacji.
Newtopian,
Dotyczy to wszystkich interfejsów API. Jeśli konsument interfejsu API nie zużyje go poprawnie, rzeczy mogą nie działać. Musiałby to być udokumentowany element interfejsu API.
Brandon,
Nawiasem mówiąc, istnieją standardy ułatwiające to, takie jak OAuth.
Brandon,
0

Jeśli masz unikalne klucze dla aplikacji, możesz ich użyć tylko podczas początkowego uwierzytelniania połączenia, inicjowanego przez klienta, po czym przełączasz się na ciągły unikalny token uwierzytelniania dla aplikacji.

Twój serwer od czasu do czasu zmienia (rzuca) token dla każdej aplikacji klienckiej (na przykład okresowo plus / minus jakieś losowe rozmyte / losowe opóźnienie). Toczący się token jest znany tylko między serwerem a uwierzytelnionym klientem.

Nowe tokeny są zwracane przy pomocy zwykłych odpowiedzi. Ilekroć odpowiedź zawiera nowy token, aplikacja odbierająca musi przełączyć się na używanie go w kolejnych żądaniach.

Ilekroć wymiana z klientem nie jest zsynchronizowana (jakiś błąd protokołu), serwer zażąda ponownego uwierzytelnienia (poprzez odpowiedź błędu na następne żądanie klienta lub wspierany poprawną odpowiedzią na żądanie klienta dla przykład).

Wstępne uwierzytelnienie zainicjowane przez klientów, gdy aktywny token jest aktywnie używany, należy traktować podejrzliwie - może to być próba naśladowania. Pozwoliłbym na to tylko wtedy, gdy wymiana stanie się bezczynna przez dłuższy czas niż oczekiwano, co może być na przykład spowodowane przez awarię / restart klienta z nową instancją, która nie ma ruchomego tokena.

Byłoby jeszcze lepiej zachować token po stronie klienta, aby zrestartowany klient mógł kontynuować od miejsca, w którym pozostawił go poprzednik - znacznie zawężając możliwości naśladowania.

Taki schemat spowodowałby, że naśladowanie byłoby co najmniej dość trudne - klient naśladujący musiałby dokładnie przewidzieć okno, kiedy autoryzowany klient przestanie wysyłać żądania na tyle długo, aby serwer mógł stwierdzić, czy akceptacja nowego klienta przy użyciu określonego klucza może mieć miejsce. Takie żądania poza dozwolonym oknem mogą być wykorzystane jako wykrycie prób naśladowania i być może zainicjować pewne środki zaradcze (czarna lista IP itp.).

Dan Cornilescu
źródło
0

Z tego co wiem, to co wspomniałeś, to jedyny sposób, aby to zrobić. Aplikacja przechowująca klucz z pewnością stanowi ryzyko, ale istnieją różne sposoby jego obejścia. Zawsze możesz użyć magazynu kluczy do przechowywania klucza, niż na stałe, co wymusza jednorazowe logowanie.

Powinieneś również rozważyć powiązanie klucza z klientem, dlatego jeśli ktoś naśladuje, powinieneś mieć warstwę bezpieczeństwa, aby sprawdzić klienta, klucz i agenty użytkownika, aby natychmiast zablokować żądanie. Poproś o przypadek użycia w celu ponownego wydania lub ponownego upewnienia się, że klucze nie zostały naśladowane.

Bieg
źródło
Jak działa JavaScript? Jak to zaprojektować?
Alok Patel,
Zakładam, że mówisz o tworzeniu aplikacji hybrydowej. W takim przypadku zdecydowanie powinieneś być w stanie stworzyć wtyczkę do przechowywania kluczy. Jeśli zamierzasz hostować aplikację i rozmawiać o mobilnym rozwiązaniu internetowym, pomyśl o renderowaniu strony przy użyciu kontenera i możesz polegać na zasugerowanym wcześniej tokenie sesji
Arun
Nie, tak nie jest. Byłbym trzecim najlepszym dostawcą API, moi użytkownicy będą korzystać z mojej usługi API w swoich aplikacjach. Może to być Android, iOS, JavaScript itp.
Alok Patel
0

Jeśli zależysz od przekazania tokenów autoryzacyjnych klientom w celu zainstalowania ich aplikacji, teoretycznie zawsze będzie możliwe, aby ktoś dokonał inżynierii wstecznej aplikacji i wyodrębnił je. Aby temu zapobiec, potrzebujesz mechanizmu, który nie wymagałby tajności w aplikacji klienckiej. To trudne. Mogę jednak zasugerować kilka opcji do przemyślenia.

Masz problem po wydaniu poświadczeń, nie masz kontroli nad tym, jak bezpiecznie są one przechowywane. Ponadto, jeśli wymagasz od użytkownika przesłania poświadczeń, ktoś może MITM połączyć twoje połączenie i ukraść tokeny bezpośrednio, nie zawracając sobie głowy inżynierią wsteczną aplikacji.

Jednym ze sposobów utrudnienia wyodrębnienia tokena autoryzacji jest zaciemnienie go. To po prostu podnosi poprzeczkę, ale nie uniemożliwia, a aby to zrobić, musiałbyś zachować kontrolę nad tajemnicą. Możesz wdrożyć bibliotekę, która zawiera tajne informacje i jest specyficzna dla każdego klienta. Możesz użyć biblioteki do komunikowania się ze swoimi serwerami i może nawet nie będziesz musiał podawać użytkownikowi tajnych informacji, może to być po prostu osadzone w bibliotece. Nie rozwiązuje to problemu inżynierii wstecznej twojej biblioteki, ale daje ci kontrolę nad poziomem zaciemnienia. Wadą jest to, że gdy jedna osoba złamie zaciemnianie w bibliotece, może zaatakować dowolną twoją bibliotekę, chyba że napiszesz kod, który znacznie różni każdą bibliotekę. To wprowadza własny zestaw problemów.

To może nieco odbiegać od zakresu twojego pytania, ale jest związane z bezpieczeństwem twojego tokena, więc o tym wspomnę. Aby zapobiec trywialnej kradzieży tokena przez drut, prawdopodobnie nie chcesz wysyłać tokena bezpośrednio, zamiast tego możesz podpisywać ruch za pomocą funkcji HMAC. Możesz sprawdzić poprawność komunikatu, obliczając HMAC komunikatu na serwerze i porównując go z HMAC wysłanym od klienta. Używałbyś tokena jako klucza do funkcji HMAC, więc tylko osoba znająca token może podpisywać ruch. Jest to lepsze dla bezpieczeństwa twojego tokena, ponieważ nigdy nie wysyłasz go bezpośrednio na serwer, aby nie mógł zostać przechwycony i skradziony bezpośrednio. Aby uzyskać więcej informacji na temat HMACS, zobacz to pytanie: /security/20129/how-and-when-do-i-use-hmac/20301

Żadne rozwiązanie zabezpieczające nie będzie niemożliwe do zdobycia, musisz zdecydować, ile będzie ono kosztowało wdrożenie w porównaniu do prawdopodobieństwa i kosztu naruszenia.

ThePragmatist
źródło
Nie mogę stworzyć biblioteki i umieścić w niej sekretów, będą one różne dla każdego z moich użytkowników.
Alok Patel,
Aby to wyjaśnić, zasugerowałem, że możesz zbudować niestandardową bibliotekę dla każdego z użytkowników i ukryć ich indywidualne sekrety w bibliotece. Będziesz musiał napisać system do automatycznego generowania bibliotek na żądanie dla każdego nowego użytkownika, co może wymagać więcej pracy niż chcesz.
ThePragmatist
0

Cytując siebie:

Nie jestem w stanie dowiedzieć się, jak uwierzytelnić klienta / użytkownika ... w którym nie mogą podać swojej nazwy użytkownika i hasła, aby się uwierzytelnić.

To trochę sprzeczność, prawda? ;)

Jak powiedzieli inni, nie możesz. Jeśli aplikacja korzysta z klucza API, można go zdekompilować tak, jak chcesz uzyskać klucz (klucze) i użyć go.

Poza wymaganiem dodatkowego prawidłowego uwierzytelnienia użytkownika, możesz jedynie ograniczyć szkody:

  • IP na białej / czarnej liście
  • dławienie
  • wykrywanie „niezwykłej aktywności” i jego źródło
  • łatwa odnowa klucza
Dagnele
źródło