Udostępniamy interfejs API, którego partnerzy mogą używać tylko w domenach, które zarejestrowali u nas. Jego zawartość jest częściowo publiczna (ale najlepiej, aby była wyświetlana tylko w znanych nam domenach), ale w większości jest prywatna dla naszych użytkowników. Więc:
Aby określić, co jest wyświetlane, nasz użytkownik musi być u nas zalogowany, ale jest to obsługiwane osobno.
Aby określić, gdzie dane są wyświetlane, używany jest publiczny klucz API w celu ograniczenia dostępu do znanych nam domen, a przede wszystkim w celu zapewnienia, że prywatne dane użytkownika nie są narażone na CSRF .
Ten klucz API jest rzeczywiście widoczny dla każdego, nie uwierzytelniamy naszego partnera w żaden inny sposób i nie potrzebujemy REFERERA . Mimo to jest bezpieczny:
Gdy get-csrf-token.js?apiKey=abc123
zostaniesz o to poproszony:
Wyszukaj klucz abc123
w bazie danych i uzyskaj listę prawidłowych domen dla tego klucza.
Poszukaj pliku cookie walidacji CSRF. Jeśli nie istnieje, wygeneruj bezpieczną wartość losową i umieść ją w pliku cookie sesji HTTP . Jeśli plik cookie istniał, pobierz istniejącą losową wartość.
Utwórz token CSRF na podstawie klucza API i losowej wartości z pliku cookie i podpisz go . (Zamiast przechowywać listę tokenów na serwerze, podpisujemy wartości. Obie wartości będą czytelne w podpisanym tokenie, to dobrze).
Ustaw odpowiedź tak, aby nie była buforowana, dodaj plik cookie i zwróć skrypt, taki jak:
var apiConfig = apiConfig || {};
if(document.domain === 'expected-domain.com'
|| document.domain === 'www.expected-domain.com') {
apiConfig.csrfToken = 'API key, random value, signature';
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
Uwagi:
Powyższe nie zapobiega sfałszowaniu żądania przez skrypt po stronie serwera, a jedynie zapewnia dopasowanie domeny, jeśli zażąda tego przeglądarka.
Ta sama zasada pochodzenia dla JavaScript gwarantuje, że przeglądarka nie może używać XHR (Ajax) do załadowania, a następnie sprawdzenia źródła JavaScript. Zamiast tego zwykła przeglądarka może załadować go tylko przy użyciu <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">
(lub dynamicznego odpowiednika), a następnie uruchomi kod. Oczywiście Twój serwer nie powinien obsługiwać udostępniania zasobów między źródłami ani JSONP dla wygenerowanego kodu JavaScript.
Skrypt przeglądarki może zmienić wartość document.domain
przed załadowaniem powyższego skryptu. Ale ta sama polityka pochodzenia pozwala tylko na skrócenie domeny przez usunięcie prefiksów, na przykład przepisywanie subdomain.example.com
do just example.com
lub myblog.wordpress.com
to wordpress.com
, aw niektórych przeglądarkach nawet bbc.co.uk
na co.uk
.
Jeśli plik JavaScript jest pobierany za pomocą skryptu po stronie serwera, serwer również otrzyma plik cookie. Jednak serwer strony trzeciej nie może przypisać przeglądarce użytkownika tego pliku cookie do naszej domeny. Dlatego token CSRF i plik cookie walidacji, które zostały pobrane za pomocą skryptu po stronie serwera, mogą być używane tylko przez kolejne wywołania po stronie serwera, a nie w przeglądarce. Jednak takie wywołania po stronie serwera nigdy nie będą zawierać pliku cookie użytkownika, a zatem mogą pobierać tylko dane publiczne. Są to te same dane, które skrypt po stronie serwera mógłby pobrać bezpośrednio z witryny internetowej partnera.
Kiedy użytkownik się loguje, ustaw plik cookie użytkownika w dowolny sposób. (Użytkownik mógł się już zalogować, zanim zażądano JavaScript).
Wszystkie kolejne żądania API do serwera (w tym żądania GET i JSONP) muszą zawierać token CSRF, plik cookie walidacji CSRF i (jeśli jest zalogowany) plik cookie użytkownika. Serwer może teraz określić, czy żądanie ma być zaufane:
Obecność prawidłowego tokenu CSRF gwarantuje, że JavaScript został załadowany z oczekiwanej domeny, jeśli zostanie załadowany przez przeglądarkę.
Obecność tokenu CSRF bez pliku cookie walidacji wskazuje na fałszerstwo.
Obecność zarówno tokena CSRF, jak i pliku cookie walidacji CSRF niczego nie gwarantuje: może to być sfałszowane żądanie po stronie serwera lub prawidłowe żądanie z przeglądarki. (Nie mogło to być żądanie przeglądarki wysłane z nieobsługiwanej domeny).
Obecność pliku cookie użytkownika zapewnia jego zalogowanie, ale nie gwarantuje, że jest on członkiem danego partnera, ani że przegląda poprawną stronę internetową.
Obecność pliku cookie użytkownika bez pliku cookie walidacji CSRF wskazuje na fałszerstwo.
Obecność pliku cookie użytkownika zapewnia, że aktualne żądanie jest wysyłane za pośrednictwem przeglądarki. (Zakładając, że użytkownik nie wprowadziłby swoich danych uwierzytelniających na nieznanej stronie internetowej i zakładając, że nie obchodzi nas, że użytkownicy używają własnych poświadczeń do wysyłania żądań po stronie serwera). Jeśli mamy również plik cookie walidacji CSRF, to ten plik cookie walidacji CSRF otrzymane również za pomocą przeglądarki. Następnie, jeśli mamy również token CSRF z ważnym podpisem, alosowa liczba w pliku cookie walidacji CSRF jest zgodna z numerem w tym tokenie CSRF, wówczas kod JavaScript dla tego tokenu został również odebrany podczas tego samego wcześniejszego żądania, podczas którego ustawiono plik cookie CSRF, a więc również przy użyciu przeglądarki. Oznacza to również, że powyższy kod JavaScript został wykonany przed ustawieniem tokenu i że w tym czasie domena była ważna dla danego klucza API.
A więc: serwer może teraz bezpiecznie używać klucza API z podpisanego tokena.
Jeśli w dowolnym momencie serwer nie ufa żądaniu, zwracany jest błąd 403 Forbidden. Widżet może na to zareagować, wyświetlając użytkownikowi ostrzeżenie.
Podpisywanie pliku cookie walidacji CSRF nie jest wymagane, ponieważ porównujemy go z podpisanym tokenem CSRF. Brak podpisania pliku cookie sprawia, że każde żądanie HTTP jest krótsze, a walidacja serwera jest nieco szybsza.
Wygenerowany token CSRF jest ważny przez czas nieokreślony, ale tylko w połączeniu z plikiem cookie walidacji, a więc efektywnie do momentu zamknięcia przeglądarki.
Moglibyśmy ograniczyć czas życia podpisu tokena. Możemy usunąć plik cookie walidacji CSRF po wylogowaniu użytkownika, aby spełnić zalecenie OWASP . Aby nie udostępniać losowej liczby na użytkownika wielu partnerom, można dodać klucz API do nazwy pliku cookie. Ale nawet wtedy nie można łatwo odświeżyć pliku cookie walidacji CSRF, gdy żądany jest nowy token, ponieważ użytkownicy mogą przeglądać tę samą witrynę w wielu oknach, współużytkując jeden plik cookie (który podczas odświeżania byłby aktualizowany we wszystkich oknach, po czym Token JavaScript w innych oknach nie będzie już pasował do tego pojedynczego pliku cookie).
Dla tych, którzy używają OAuth, zobacz także OAuth i Widżety po stronie klienta , z których wziąłem pomysł na JavaScript. W przypadku korzystania z interfejsu API po stronie serwera , w którym nie możemy polegać na kodzie JavaScript w celu ograniczenia domeny, używamy tajnych kluczy zamiast publicznych kluczy API.