2011-11-21 12 views
11

Projektuję interfejs API dla grupy witryn. Strony są bardzo podobne (takie jak StackOverflow, SuperUser i ServerFault) i ma to sens, aby mieć współużytkowany backend. Dlatego zdecydowaliśmy się wypróbować ładny interfejs REST API jako backend i kilka bardzo podobnych, ale różnych frontendów korzystających z tego API. Frontendy powinny być wszystkie statyczne, ale nie jest to trudne, jeśli okaże się, że granica jest niemożliwa.W jaki sposób zabezpieczyć RESTful API zużywane przez przeglądarkę przed atakami CSRF?

Pracuję nad zaprojektowaniem tego API teraz i martwię się konsekwencjami bezpieczeństwa, szczególnie CSRF. Z mojego podstawowego rozumienia ataków CSRF składają się z dwóch ważnych komponentów:

  1. Możliwość nazwania zasobu i treści żądania.

  2. Oszukiwanie użytkownika/przeglądarki w użyciu uwierzytelniania otoczenia (np. Sesji) w celu wysłania żądania do zasobu, który wygląda na uwierzytelniony.

Wiele klasycznych podejść do naprawy ataków CSRF opiera się na sesji. Ponieważ mój REST API tak naprawdę nie wykonuje sesji, to zarówno zapobiega wielu wektorom, jak i prawie wszystkim, aby je naprawić. Na przykład podwójne przesyłanie nie ma sensu, ponieważ nie ma nic do podwójnego przesłania.

Moje wstępne podejście polegało na zaatakowaniu części 2 ataku CSRF. Jeśli uwierzytelniam wszystkie żądania (np. Za pomocą protokołu HTTP Basic Auth), a przeglądarka nie zachowuje tych poświadczeń (np. Niektóre JS złożyło żądanie), tylko JS, który ma poświadczenia, może wysłać żądanie, a my skończymy . Oczywistym minusem jest to, że aplikacja musi znać referencje użytkownika. Inną nieco mniej oczywistą wadą jest to, że jeśli chcę bezpiecznie przechowywać referencje na końcu API, weryfikacja hasła powinna trwać ustaloną, nie trywialną ilość czasu. Jeśli weryfikacja hasła bezpiecznie zajmie 100ms, wtedy każde inne żądanie zajmie co najmniej 100ms + eps, a to sprawi, że nie będzie to powolne. Być może będę w stanie to ukryć (ponieważ dane uwierzytelniające będą zawsze takie same) i jeśli będę bardzo ostrożny, uda mi się to zrobić bez wprowadzania luki czasowej, ale to brzmi jak gniazdo szerszeni.

OAuth 2.0 wydaje się nieco przesadzony, ale myślę, że to może być najlepsze rozwiązanie, bo inaczej źle go wprowadzę. Przypuszczam, że mogłem na razie wykonać zadanie HTTP Basic Auth i przejść do OAuth, gdy mamy programistów aplikacji zewnętrznych.

W przypadku OAuth występuje niewielkie niedopasowanie impedancji. OAuth naprawdę chce pomóc aplikacjom w uzyskaniu dostępu do rzeczy w innej aplikacji. Chcę, aby użytkownicy zarejestrowali się na jednym z frontendów, zanim takie konto jeszcze istnieje.

Rozważyłem także atakowanie punktu 1, tworząc adresy losowe - tj. Dodając tokeny do ciągu zapytania. To z pewnością zadziałałoby i jest bardzo zbliżone do tradycyjnego losowego tokena w formularzu, a biorąc pod uwagę HATEOAS, powinno to być nawet WYDAJNE, chociaż rodzi to dwa pytania: 1) od czego zacząć? Czy istnieje obowiązkowy punkt początkowy interfejsu API, w którym logujesz się za pomocą protokołu HTTP Basic Auth? 2) W jakim stopniu deweloperzy aplikacji byliby zadowoleni, gdyby nie mogli przewidzieć adresu URL z góry, HATEOAS się potępili?

Widziałem How to prevent CSRF in a RESTful application?, ale nie zgadzam się z założeniem, że losowe identyfikatory URI są koniecznie nieuczciwe. Również to pytanie nie dostarcza żadnych satysfakcjonujących odpowiedzi i nie wspomina o OAuth. Ponadto, rozwiązanie do podwójnego przesyłania sesji jest nieprawidłowe, jak już wspomniałem powyżej (inna domena dla statycznego interfejsu niż punkt końcowy API).

Zdaję sobie sprawę, że to, co zasadniczo próbuję tutaj zrobić, polega na próbie dopuszczenia żądań z innej strony z jednej domeny i zabronienia ich innym, co nie jest łatwe. Na pewno musi być jakieś rozsądne rozwiązanie?

+0

Problem polega, jak pan twierdzi, na tym, że zarówno zapobiegają żądaniom między lokacjami, jak i pozwalają na to. Zapytaj Schrödingera. –

+0

To nie znaczy, że nie ma sposobu na rozwiązanie problemu. Dostałem nawet taką, która, choć trochę wolno. – lvh

Odpowiedz

0

Odpowiedź zależy od tego, co należy obsłużyć. Jeśli założymy, że chcemy obsługiwać aplikację internetową, która używa usługi REST i używać tej samej usługi REST dla interfejsu API, który jest czymś innym niż aplikacja internetowa, która jest RESTFUL (możesz zdecydować, że "sesje" są dla ciebie!).

Na razie (/ ja jestem raczej zmęczony) Myślę, że sposób, w jaki zasugerowałeś używanie javascript z HTTP Basic Auth jest dobrym początkiem.

+0

Usługa HTTP Basic Auth jest podatna na działanie CSRF. – rook

+0

Czy mógłbyś opracować? Rozumiem, że przeglądarki buforują HTTP Basic Auth, ale nie prosimy przeglądarki o to; sugerujemy, aby niektóre JS zbudowały nagłówek HTTP Basic Auth. W związku z tym poświadczenia nigdy nie trafiają do puli uwierzytelnienia przeglądarki, a system staje się odporny na CSRF. Poprawny? – lvh

3

Token CSRF jest z definicji "na stan użytkownika", a zatem nie jest RESTOWN. Większość interfejsów API łamie wymagania "na użytkownika" dla celów bezpieczeństwa i wymaga podania tokenu CSRF jako parametru nagłówka HTTP.

Istnieją inne sposoby uzyskania preventing CSRF. Sprawdzanie odnośnika nie jest tak mocne, jak tokenu CSRF, ale jest mało prawdopodobne, aby podważył się RESTful i BERY. Należy pamiętać, że brak referer należy uznać za nieudane żądanie.

XSS może być używany do ominięcia zarówno profilowania CSRF opartego na tokenie, jak i zapobiegania CSRF opartego o referer.

+0

Ta sama uwaga jak w mojej odpowiedzi na twój komentarz. Czy mógłbyś wyjaśnić, dlaczego podstawowa autoryzacja HTTP nie zapobiega CSRF podczas dodawania nagłówka uwierzytelniania za pomocą JS? W jaki sposób OAuth nie zapobiega CSRF? Czy nie gwarantuje tego jednorazowa część OAuth? – lvh

+0

@lvh nazwa użytkownika i hasło to stan użytkownika, który jest wysyłany wraz z żądaniem. Cryptographic Nonce używany jako Token CSRF jest również dla każdego stanu użytkownika wysyłanego przy każdym żądaniu, ale jest bezpieczniejszy, ponieważ powinien być wartością dynamiczną związaną z sesją. Zwykle protokoły RESTful opierają się na statycznej kryptografii lub "kluczu api", który jest stanem dla każdego użytkownika, a także zatrzymuje CSRF. Wysłanie go w nagłówku HTTP nie powoduje, że jest on spokojny, ale może otworzyć drzwi do ataku, jeśli inne żądania wysłane przez tę przeglądarkę zawierają tę wartość. – rook

+0

Ale to jest moja uwaga. Jaki jest wektor ataku, jeśli nie zostanie autoryzacją przeglądarki otoczenia? Niezależnie od tego, czy jest to numer jednorazowy, czy też faktyczne poświadczenia nie mają znaczenia dla celów CSRF: przeglądarka nie * wie *, więc jak można go oszukać? – lvh

0

Mam inne potencjalne rozwiązanie, więc wymieniam je jako odpowiedź. Możesz go rozebrać :)

auth.platform.com podejmuje uwierzytelnianie i ustawia pliki cookie. Jeśli auth.site.com jest rekordem CNAME dla auth.platform.com, czy żądanie do auth.site.com (kończące się na auth.platform.com po rozwiązaniu) może ustawić plik cookie dla site.com? W ten sposób mógłbym dwukrotnie przesłać pliki cookie sesji.

Oczywiście, auth.platform.com będzie ustawiać pliki cookie tylko dla kilku domen umieszczonych na białej liście.

EDYCJA: Poza tym to w ogóle nie zadziała, ponieważ będziesz musiał używać HTTPS do bezpiecznego uwierzytelniania, a protokół HTTPS obejrzy Twoją sztuczkę.

0

Projektując swoje API jako prawdziwy relaksującego jednego, zapobiega Najczęstszymi wektory CSRF:

  • unikając ciasteczek uniemożliwia im być „skradziony”,
  • użyciu dostać za „bezpieczne” operacji zapobiega img i iframes wyzwalanie niebezpiecznych działań bez interakcji użytkownika.

Następnie należy zaimplementować CORS, aby umożliwić przeglądarkom użytkowników blokowanie żądań od źródeł, którym nie ufasz.

+1

-1 Nie ma to nic wspólnego z CSRF, z wyjątkiem komentarza dotyczącego CORS. CORS jest niebezpieczny i może być używany do odczytywania tokena CSRF lub innych poufnych informacji. CORS nie może być użyty do zapobiegania CSRF. – rook

Powiązane problemy