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:
Możliwość nazwania zasobu i treści żądania.
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?
Problem polega, jak pan twierdzi, na tym, że zarówno zapobiegają żądaniom między lokacjami, jak i pozwalają na to. Zapytaj Schrödingera. –
To nie znaczy, że nie ma sposobu na rozwiązanie problemu. Dostałem nawet taką, która, choć trochę wolno. – lvh