Jak zabezpieczyć wywołania REST API?

92

Tworzę spokojną aplikację internetową, która używa popularnego frameworka internetowego na zapleczu, powiedzmy (rails, sinatra, flask, express.js). Idealnie, chciałbym rozwijać stronę klienta za pomocą Backbone.js. Jak zezwolić tylko mojej stronie klienta javascript na interakcję z tymi wywołaniami API? Nie chcę, aby te wywołania API były publiczne i były wywoływane przez curllub po prostu przez wpisanie łącza w przeglądarce.

knd
źródło
Czy wszystkie wywołania interfejsu API wymagają tokenu, który jest przekazywany klientowi podczas udostępniania strony?
hajpoj
Amazon AWS javascript SDK używa wstępnie podpisanego adresu URL obiektu: - docs.aws.amazon.com/AmazonS3/latest/dev/…
rjha94

Odpowiedzi:

91

Po pierwsze, jeśli twoje API jest używane przez twojego klienta JS, musisz założyć, że jest publiczne: Prosty debugger JS umieszcza atakującego w pozycji, w której może wysłać identyczne żądanie bajt po bajcie z wybrane przez siebie narzędzie.

To powiedziawszy, jeśli dobrze przeczytałem twoje pytanie, to nie jest to, czego chcesz uniknąć: To, czego naprawdę nie chcesz, to to, że twoje API jest zużywane (regularnie) bez udziału klienta JS. Oto kilka pomysłów, jak egzekwować, a przynajmniej zachęcać do korzystania z klienta:

  • Jestem pewien, że twoje API ma jakieś pole uwierzytelniania (np. Hash obliczany na kliencie). Jeśli nie, spójrz na to pytanie SO . Upewnij się, że używasz soli (lub nawet klucza API), który jest przekazywany klientowi JS na podstawie sesji (zakodowany na stałe). W ten sposób nieautoryzowany konsument Twojego interfejsu API jest zmuszony do znacznie większej pracy.

  • Podczas ładowania klienta JS zapamiętaj niektóre nagłówki HTTP (przychodzi mi na myśl agent użytkownika) i adres IP i poproś o ponowne uwierzytelnienie, jeśli ulegną zmianie, stosując czarne listy dla zwykłych podejrzanych. Zmusza to napastnika do ponownego dokładniejszego odrabiania pracy domowej.

  • Po stronie serwera pamiętaj o kilku ostatnich wywołaniach API, a przed zezwoleniem na kolejne sprawdź, czy logika biznesowa pozwala teraz na nowe: To uniemożliwia atakującemu skoncentrowanie wielu swoich sesji w jednej sesji z Twoim serwerem: W w połączeniu z innymi środkami ułatwi to wykrycie sprawcy.

Mógłbym nie powiedzieć tego z niezbędną jasnością: uważam, że niemożliwe jest całkowite uniemożliwienie sprawcy korzystania z Twojej usługi, ale możesz to tak utrudnić, że może to nie być warte zachodu.

Eugen Rieck
źródło
to jest przydatna informacja, ale co zrobić, jeśli chcę dokonać autoryzacji z mojego zaplecza api do innej aplikacji API, takiej jak oddzielny serwer, aby uprościć moje pytanie, chcę, aby mój back-end, czyli node.js, wysyłał żądanie pobrania do innego zaplecza serwer końcowy, który jest moim własnym, z pewnych powodów jest to potrzebne, ale chcę zabezpieczyć wywołania API, ponieważ może on uzyskać dostęp do poufnych danych i nie mogę używać sesji ani jwt, ponieważ nie mogę ich przechowywać w przeglądarce.
Piramida
@Thepyramid Nie ma znaczenia, co wywołanie API robi po stronie serwera, zwłaszcza jeśli strona serwera wykonuje kolejne wywołanie API drugiego poziomu. Ważne jest, aby traktować serwer nie jako proxy, ale jako aplikację.
Eugen Rieck,
czy możesz wyjaśnić więcej, jak zrobić aplikację, a nie proxy
Piramida
1
Chodzi mi o to: Aby uzyskać przyzwoity poziom bezpieczeństwa, musisz zastosować wszystkie narzędzia, które ma aplikacja internetowa: sesje, bazę danych uwierzytelniania, logikę biznesową. Jeśli tego nie zrobisz i potraktujesz swój serwer jako sposób przekazywania żądań do innego serwera, używasz go po prostu jako proxy dla tego innego serwera i jesteś ograniczony przez wszelkie zabezpieczenia, które oferuje inny serwer.
Eugen Rieck,
1
@PirateApp Osoba atakująca może łatwo zignorować nagłówki CSRF. Działają tylko wtedy, gdy urządzeniem końcowym jest niezatwierdzona przeglądarka
Eugen Rieck
12

Powinieneś zaimplementować jakiś system uwierzytelniania. Dobrym sposobem na rozwiązanie tego problemu jest zdefiniowanie pewnych oczekiwanych zmiennych nagłówka. Na przykład możesz mieć wywołanie interfejsu API uwierzytelniania / logowania, które zwraca token sesji. Kolejne wywołania Twojego interfejsu API będą oczekiwać, że token sesji zostanie ustawiony w zmiennej nagłówka HTTP o określonej nazwie, np. „Twój-api-token”.

Alternatywnie, wiele systemów tworzy tokeny dostępu lub klucze, które są oczekiwane (np. Youtube, facebook czy twitter) za pomocą jakiegoś systemu kont API. W takich przypadkach Twój klient musiałby przechowywać je w jakiś sposób w kliencie.

Wtedy wystarczy po prostu dodać czek sesji do środowiska REST i zgłosić wyjątek. Jeśli to w ogóle możliwe, kod statusu (aby był spokojny) byłby błędem 401.

gview
źródło
8
Chociaż nic nie powstrzymuje ich przed spojrzeniem na nagłówki i odtworzeniem ich.
cdmckay
1
@cdmckay - token musi pasować do tokena przechowywanego w sesji. Zwykłe odtworzenie nagłówka spowoduje wyświetlenie odpowiedzi „Unauthorized”, jeśli pochodzi on z innej sesji.
Andrei Volgin
3
Mogą jednak nadal korzystać z tej samej sesji i zmieniać żądania przed wysłaniem ich do API ... lub nawet używając konsoli w czasie wykonywania wygenerować wywołanie z dopasowanymi nagłówkami / polami, modyfikującymi tylko potrzebne części ...
Potter Rafed
2
@PotterRafed: Jeśli użytkownik uzyskuje dostęp do swojej własnej ważnej sesji, czyli przy użyciu aplikacji, a nie atakuje jej. Celem uwierzytelniania jest uniemożliwienie dostępu do sesji / danych innych użytkowników .
Andrei Volgin
@AndreiVolgin tak, w porządku, ale nadal jest to luka
Potter Rafed
9

Istnieje teraz otwarty standard o nazwie „JSON Web Token”,

patrz https://jwt.io/ & https://en.wikipedia.org/wiki/JSON_Web_Token

JSON Web Token (JWT) to oparty na JSON otwarty standard (RFC 7519) do tworzenia tokenów, które potwierdzają pewną liczbę oświadczeń. Na przykład serwer może wygenerować token z oświadczeniem „zalogował się jako administrator” i przekazać go klientowi. Klient może następnie użyć tego tokenu, aby udowodnić, że jest zalogowany jako administrator. Tokeny są podpisywane kluczem serwera, dzięki czemu serwer może zweryfikować, czy token jest ważny. Tokeny są zaprojektowane tak, aby były kompaktowe, bezpieczne dla adresów URL i nadające się do użytku, szczególnie w kontekście logowania jednokrotnego (SSO) przeglądarki internetowej. Oświadczenia JWT mogą być zwykle używane do przekazywania tożsamości uwierzytelnionych użytkowników między dostawcą tożsamości a dostawcą usług lub dowolnego innego rodzaju oświadczeń, zgodnie z wymaganiami procesów biznesowych. [1] [2] Tokeny mogą być również uwierzytelniane i szyfrowane. [3] [4]

bbozo
źródło
Co uniemożliwiłoby użytkownikowi skopiowanie swojego tokena i użycie go w jakiejkolwiek innej odpowiedzi?
Ulad Kasach
1
@UladKasach, szczerze mówiąc, nigdy nie zagłębiałem się w nie naprawdę, ale afaik są one wygasłe, unikalne dla twojego użytkownika i zaszyfrowane przez SSL (co oczywiście
ćwiczysz
3

Przepraszam @MarkAmery i Eugene, ale to jest niepoprawne.

Twoja aplikacja js + html (klient) działająca w przeglądarce MOŻE zostać skonfigurowana tak, aby wykluczyć nieautoryzowane bezpośrednie wywołania interfejsu API w następujący sposób:

  1. Pierwszy krok: skonfiguruj interfejs API tak, aby wymagał uwierzytelniania. Klient musi najpierw uwierzytelnić się przez serwer (lub jakiegoś innego serwera zabezpieczeń) , na przykład prosząc ludzkiego użytkownika, aby zapewnić prawidłowe hasło.

Przed uwierzytelnieniem wywołania API nie są akceptowane.

Podczas uwierzytelniania zwracany jest „token”.

Po uwierzytelnieniu akceptowane będą tylko wywołania API z „tokenem” uwierzytelniania.

Oczywiście na tym etapie tylko upoważnieni użytkownicy, którzy mają hasło, mogą uzyskać dostęp do API, chociaż jeśli są programistami debugującymi aplikację, mogą uzyskać do niej bezpośredni dostęp w celach testowych.

  1. Drugi krok: Teraz skonfiguruj dodatkowy interfejs API bezpieczeństwa, który ma zostać wywołany w krótkim czasie po pierwszym zażądaniu klienta aplikacji js + html z serwera. To „wywołanie zwrotne” powie serwerowi, że klient został pomyślnie pobrany. Ogranicz wywołania interfejsu API REST do działania tylko wtedy, gdy żądanie klienta zostało wysłane niedawno i pomyślnie.

Teraz, aby używać twojego API, muszą najpierw pobrać klienta i faktycznie uruchomić go w przeglądarce. Dopiero po pomyślnym odebraniu wywołania zwrotnego, a następnie wpisie użytkownika w krótkim czasie, API będzie akceptować wywołania.

Nie musisz się więc martwić, że może to być nieautoryzowany użytkownik bez poświadczeń.

(Tytuł pytania „Jak zabezpieczyć wywołania REST API” iz większości z tego, co mówisz, jest Twoim głównym zmartwieniem, a nie dosłownym pytaniem, JAK nazywa się twoje API, ale raczej KOGO, prawda? )

pashute
źródło
5
Drugi punkt nie ma sensu. Jeśli osoba atakująca musi załadować Twoją aplikację, zrobi to (Twoje wywołanie zwrotne jest widoczne). A potem atak.
Andrei Volgin
Punkt 2 jest uzupełnieniem punktu 1. Atakujący nadal wymaga uwierzytelnienia. Punkt 2 tylko dodaje do tego konieczność faktycznego pobrania aplikacji html w celu uzyskania autoryzacji. Zatem wywołanie bezpośrednio do interfejsów API bez aplikacji (prawdopodobnie dostęp i pobranie dopiero po uwierzytelnieniu) jest niemożliwe. O co chodziło w tym pytaniu.
pashute
Możesz po prostu zezwolić na żądania tylko z Twojej domeny.
Andrei Volgin
To ogranicza tylko wywołania do wewnątrz domeny, więc teraz użytkownicy aplikacji przeglądarkowej javascript muszą znajdować się wewnątrz domeny (prawdopodobnie nie jest to coś, czego knd chciał) i ci użytkownicy nadal mogą wywoływać API bezpośrednio przez curl.
pashute
2
Wydaje się, że przeoczasz to, że Cokolwiek poprosisz przeglądarkę użytkownika, może zostać skopiowane przez atakującego - aby przeglądarka to zrobiła, musi być czytelna.
Eugen Rieck
1
  1. Ustaw SESSION var na serwerze, gdy klient po raz pierwszy ładuje twój index.html(lub backbone.jsitp.)

  2. Sprawdź tę zmienną po stronie serwera przy każdym wywołaniu interfejsu API.

PS to nie jest rozwiązanie "zabezpieczające" !!! Ma to na celu ułatwienie obciążenia serwera, aby ludzie nie nadużyli go ani nie „łączyli” interfejsu API z innymi witrynami i aplikacjami.

Alex
źródło
0

Oto co robię:

  1. Zabezpiecz API za pomocą nagłówka HTTP z wywołaniami takimi jak X-APITOKEN:

  2. Użyj zmiennych sesji w PHP. Przygotuj system logowania i zapisz token użytkownika w zmiennych sesji.

  3. Wywołaj kod JS za pomocą Ajax do PHP i użyj zmiennej sesji z curl, aby wywołać API. W ten sposób, jeśli zmienna sesji nie jest ustawiona, nie zostanie wywołana, a kod PHP zawiera token dostępu do API.

Pavneet Singh
źródło