Co to jest token CSRF? Jakie jest jego znaczenie i jak działa?

628

Piszę aplikację (Django, tak się zdarza) i chcę po prostu wiedzieć, czym tak naprawdę jest „token CSRF” i jak chroni dane. Czy dane pocztowe nie są bezpieczne, jeśli nie korzystasz z tokenów CSRF?

Shawn
źródło
13
Jest to tajny, specyficzny dla użytkownika token we wszystkich formularzach i adresach URL skutków ubocznych, aby zapobiec fałszowaniu żądań między witrynami. Więcej informacji tutaj: en.wikipedia.org/wiki/Cross-site_request_forgery
Robert Harvey
1
wygląda na to, że istnieje delikatna granica między ochroną pytania a zakazaniem go za zbyt szerokie: D
anton1980 18.10.18
2
Z OWASP Arkusz zapobiegający żądaniom krzyżowym (CSRF) : „ Skrypty krzyżowe nie są konieczne do działania CSRF. Jednak każdą lukę w skryptach krzyżowych można wykorzystać do pokonania wszystkich technik ograniczania CSRF [...]. Wynika to z faktu, że ładunek XSS może po prostu odczytać dowolną stronę w witrynie za pomocą XMLHttpRequest [...]. Konieczne jest, aby nie było żadnych luk w zabezpieczeniach XSS, aby zapewnić, że nie można obejść obrony CSRF.
toraritte

Odpowiedzi:

1497

Fałszowanie żądań między witrynami (CSRF) w prostych słowach

  • Załóżmy, że jesteś obecnie zalogowany do bankowości internetowej pod adresem www.mybank.com
  • Załóż przelew z mybank.com spowoduje przesłanie formularza (koncepcyjnie) http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>. (Twój numer konta nie jest potrzebny, ponieważ wynika z twojego loginu.)
  • Odwiedzasz www.cute-cat-pictures.org, nie wiedząc, że jest to złośliwa strona.
  • Jeśli właściciel tej witryny zna formę powyższego żądania (łatwe!) I poprawnie zgaduje, że jesteś zalogowany mybank.com(wymaga odrobiny szczęścia!), Może dołączyć na swojej stronie żądanie takie jak http://www.mybank.com/transfer?to=123456;amount=10000(gdzie 123456jest numer ich konta na Kajmanach i 10000jest to kwota, którą wcześniej myślałeś, że jesteś zadowolony z ją posiadasz).
  • Ci pobrać tę www.cute-cat-pictures.orgstronę, więc Twoja przeglądarka uczyni ten wniosek.
  • Twój bank nie może rozpoznać tego źródła żądania: Twoja przeglądarka prześle żądanie wraz z www.mybank.complikiem cookie i będzie wyglądać całkowicie poprawnie. Tam idą twoje pieniądze!

To świat bez tokenów CSRF .

Teraz lepszy z tokenami CSRF :

  • Żądanie przeniesienia zostaje przedłużony o trzeci argument: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Ten token jest ogromną, niemożliwą do odgadnięcia losową liczbą, która mybank.compojawi się na własnej stronie internetowej, gdy Ci ją poda. To jest inna za każdym razem służą dowolną stronę nikomu.
  • Atakujący nie jest w stanie odgadnąć tokena, nie jest w stanie przekonać przeglądarki internetowej do oddania go (jeśli przeglądarka działa poprawnie ...), dlatego atakujący nie będzie w stanie utworzyć prawidłowego żądania, ponieważ żąda za pomocą zły token (lub brak tokena) zostanie odrzucony przez www.mybank.com.

Wynik: zachowujesz swoje 10000jednostki pieniężne. Proponuję przekazać trochę tego na Wikipedię.

(Twój przebieg może się różnić.)

EDYTUJ z komentarza, który warto przeczytać:

Warto zauważyć, że skrypt z www.cute-cat-pictures.orgreguły nie ma dostępu do tokena anty-CSRF z www.mybank.compowodu kontroli dostępu HTTP. Ta notatka jest ważna dla niektórych osób, które nierozsądnie wysyłają nagłówek Access-Control-Allow-Origin: *dla każdej odpowiedzi strony internetowej, nie wiedząc, do czego to służy, tylko dlatego, że nie mogą używać interfejsu API z innej witryny.

Lutz Prechelt
źródło
36
Oczywiście token byłby idealnie nazwany tokenem anty- CSSRF, ale nazwa jest prawdopodobnie wystarczająco skomplikowana.
Lutz Prechelt
3
@LutzPrechelt dziękuję. dlaczego javascript nie może uzyskać żadnych tokenów autentyczności z przeglądarki?
BKSpurgeon,
72
Warto zauważyć, że skrypt z www.cute-cat-pictures.orgreguły nie ma dostępu do tokena anty-CSRF z www.mybank.compowodu kontroli dostępu HTTP. Ta notatka jest ważna dla niektórych osób, które nierozsądnie wysyłają nagłówek Access-Control-Allow-Origin: *dla każdej odpowiedzi strony internetowej, nie wiedząc, do czego to służy, tylko dlatego, że nie mogą używać interfejsu API z innej witryny.
SOFe,
9
@AugustinRiedinger Jeśli atakujący otworzy stronę na swoim komputerze - ponieważ nie ma pliku cookie zalogowanego użytkownika - nie otrzyma odpowiedniego tokena csrf (każdy token csrf powinien być ważny tylko dla określonej sesji użytkownika). Jeśli atakujący spróbuje załadować stronę internetową zawierającą token na komputerze użytkownika, ze skryptem umieszczonym na stronie cute-cat-pictures, przeglądarka uniemożliwi mu odczytanie strony www.mybank.com (i tokena) z powodu ta sama polityka pochodzenia.
Marcel
13
@LutzPrechelt Myślę, że nie wystarczy, że token jest zawsze inny, należy go sparować z sesją, a serwer musi sprawdzić, czy token, który otrzymuje, został wygenerowany dla sesji, którą serwer identyfikuje na podstawie otrzymanego pliku cookie. W przeciwnym razie haker może po prostu odwiedzić mój bank i uzyskać ważny token. Jeśli więc używasz nowego tokena w każdym formularzu, musisz zapisać go sparowany z sessionid na serwerze. Prawdopodobnie łatwiej jest użyć tego samego tokena na sesję.
Marcel
222

Tak, dane wpisu są bezpieczne. Ale pochodzenie tych danych nie jest. W ten sposób ktoś może nakłonić użytkownika JS do zalogowania się na Twojej stronie podczas przeglądania strony atakującego.

Aby temu zapobiec, django wyśle ​​losowy klucz zarówno w pliku cookie, jak i danych formularza. Następnie, gdy użytkownicy POST, sprawdzą, czy dwa klucze są identyczne. W przypadku oszukania użytkownika witryna innej firmy nie może uzyskać plików cookie witryny, co powoduje błąd uwierzytelnienia.

Dmitrij Szewczenko
źródło
@DmitryShevchenko Cześć, próbuję zrozumieć, w jaki sposób ta metoda ciasteczka + wprowadzanie formularza różni się od walidacji strony polecającej po stronie serwera? Wszystkie znalezione przeze mnie przykłady dotyczą hakera nakłaniającego użytkownika do zamieszczania postów z jego witryny na rzeczywistej stronie.
Ethan,
Ok, dowiedziałem się, dlaczego odsyłacz nie jest używany. W wielu przypadkach jest zablokowany, ponieważ czasami przechowuje poufne informacje. Korporacje i ich pełnomocnicy zazwyczaj to robią. Jeśli jednak zostanie użyty HTTPS, istnieje większe prawdopodobieństwo, że nie zostanie zablokowany.
Ethan
4
Łatwo jest zmienić stronę odsyłającą, nie powiedziałbym, że jest to wiarygodna informacja. Token CSRF jest jednak generowany przy użyciu tajnego klucza serwera i zwykle powiązany z użytkownikiem
Dmitrij Szewczenko
1
Naprawdę nie rozumiem, dlaczego jest to zagrożenie bezpieczeństwa. Użytkownik zostanie zalogowany do innej witryny ... ale oryginalna witryna nie będzie miała możliwości odzyskania tych informacji. Dobrze?
Aakil Fernandes
6
Załóżmy, że wstrzykuję złośliwy element iframe „ bank.com/transfer?from=x&to=y ” na przykład na Facebooku.com. Jeśli jesteś klientem bank.com i przejdziesz na Facebooka, iframe załaduje stronę banku wraz z plikami cookie (ponieważ przeglądarka wyśle ​​je do znanej domeny) i dokona przelewu. Bez twojej wiedzy.
Dmitrij Szewczenko
74

Witryna generuje unikalny token podczas tworzenia strony formularza. Ten token jest wymagany do wysyłania / pobierania danych z powrotem na serwer.

Ponieważ token jest generowany przez twoją witrynę i dostarczany tylko wtedy, gdy strona z formularzem jest generowana, niektóre inne witryny nie mogą naśladować twoich formularzy - nie będą miały tokenów i dlatego nie będą mogły publikować na twojej stronie.

tkone
źródło
9
Czy użytkownik może pobrać dane wyjściowe tokena ze źródła, pobrać plik cookie przesłany do niego, a następnie przesłać z witryny innej firmy?
Jack Marchetti
9
@JackMarchetti tak. ale byłoby to kosztowne, ponieważ za każdym razem, gdy chcesz przesłać formularz z witryny innej firmy, musisz załadować stronę i przeanalizować token. Tokeny CSRF powinny być idealnie połączone z innymi formami bezpieczeństwa, jeśli
martwisz się
3
Mam to samo pytanie co @JackMarchetti, nie jest jasne, czy token CSRF zmienia się przy każdym logowaniu. Jeśli pozostanie taki sam, co uniemożliwiłoby atakującemu zalogowanie się, pobranie tokena żądania, a następnie wstawienie tego tokena do ataku?
Paul Preibisch
7
@PaulPreibisch powinien się zmieniać przy każdym ładowaniu strony - nie przy każdym logowaniu. W ten sposób atakujący musiałby poprosić o stronę za każdym razem, gdy chciałby przesłać formularz. To znacznie utrudnia.
tkone
8
@tkone, To naprawdę nie utrudnia znacznie. Jeśli podwoi ilość wysiłku i czasu. Nie dodaje żadnego wygórowanego przetwarzania. Sztuką jest również powiązanie tokena CSRF z plikiem cookie specyficznym dla domeny i wysłanie tego pliku cookie wraz z formularzem. Zarówno plik cookie, jak i dane formularza muszą zostać przesłane do serwera na żądanie POST. Ten sposób wymagałby ataku z wykorzystaniem plików cookie, aby móc naśladować uzasadnione żądanie.
Pedro Cordeiro
55

Blog Cloud Under ma dobre objaśnienie tokenów CSRF.

Wyobraź sobie, że masz witrynę internetową jak uproszczony Twitter, hostowaną na a.com. Zalogowani użytkownicy mogą wprowadzić tekst (tweet) do formularza, który jest wysyłany na serwer jako żądanie POST i publikowany po naciśnięciu przycisku przesyłania. Na serwerze użytkownik jest identyfikowany przez plik cookie zawierający jego unikalny identyfikator sesji, dzięki czemu Twój serwer wie, kto opublikował tweeta.

Formularz może być tak prosty:

 <form action="http://a.com/tweet" method="POST">
   <input type="text" name="tweet">
   <input type="submit">
 </form> 

Teraz wyobraź sobie, że zły facet kopiuje i wkleja ten formularz do swojej złośliwej strony, powiedzmy b.com. Formularz nadal działałby. Tak długo, jak użytkownik jest zalogowany na Twoim Twitterze (tj. Ma prawidłowy plik cookie sesji dla a.com), żądanie POST będzie wysyłane http://a.com/tweeti przetwarzane jak zwykle, gdy użytkownik kliknie przycisk przesyłania.

Jak dotąd nie jest to duży problem, o ile użytkownik zostanie poinformowany o tym, co dokładnie robi formularz, ale co, jeśli nasz zły facet poprawi formularz w ten sposób:

 <form action="https://example.com/tweet" method="POST">
   <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad">
   <input type="submit" value="Click to win!">
 </form> 

Teraz, jeśli jeden z Twoich użytkowników znajdzie się na stronie złego faceta i kliknie „Kliknij, aby wygrać!” przycisk, formularz jest przesyłany do Twojej witryny, użytkownik jest poprawnie identyfikowany przez identyfikator sesji w pliku cookie, a ukryty Tweet zostaje opublikowany.

Gdyby nasz zły facet był jeszcze gorszy, zmusiłby niewinnego użytkownika do przesłania tego formularza, gdy tylko otworzą swoją stronę internetową za pomocą JavaScript, być może nawet całkowicie ukrytego w niewidzialnym iframe. Zasadniczo jest to fałszowanie żądań w różnych witrynach.

Formularz można łatwo przesłać z dowolnego miejsca na cały. Zasadniczo jest to wspólna funkcja, ale istnieje wiele innych przypadków, w których ważne jest, aby zezwolić na przesyłanie formularza tylko z domeny, do której należy.

Gorzej, jeśli twoja aplikacja internetowa nie rozróżnia żądań POST i GET (np. W PHP za pomocą $ _REQUEST zamiast $ _POST). Nie rób tego! Żądania zmiany danych mogą być przesyłane tak łatwo, jak <img src="http://a.com/tweet?tweet=This+is+really+bad">osadzone w złośliwej witrynie lub nawet w wiadomości e-mail.

Jak mogę się upewnić, że formularz można przesłać tylko z mojej strony internetowej? W tym miejscu pojawia się token CSRF. Token CSRF to losowy, trudny do odgadnięcia ciąg. Na stronie z formularzem, który chcesz chronić, serwer wygeneruje losowy ciąg, token CSRF, doda go do formularza jako ukrytego pola, a także zapamięta w jakiś sposób, przechowując go w sesji lub ustawiając plik cookie zawierający wartość. Teraz formularz będzie wyglądał następująco:

    <form action="https://example.com/tweet" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="tweet">
      <input type="submit">
    </form> 

Gdy użytkownik prześle formularz, serwer musi po prostu porównać wartość wysłanego pola csrf-token (nazwa nie ma znaczenia) z tokenem CSRF zapamiętanym przez serwer. Jeśli oba ciągi są równe, serwer może kontynuować przetwarzanie formularza. W przeciwnym razie serwer powinien natychmiast przerwać przetwarzanie formularza i odpowiedzieć błędem.

Dlaczego to działa? Istnieje kilka powodów, dla których zły facet z powyższego przykładu nie może uzyskać tokenu CSRF:

Kopiowanie statycznego kodu źródłowego z naszej strony na inną stronę byłoby bezużyteczne, ponieważ wartość ukrytego pola zmienia się u każdego użytkownika. Bez strony internetowej złego faceta, która zna token CSRF bieżącego użytkownika, Twój serwer zawsze odrzuca żądanie POST.

Ponieważ złośliwa strona złego faceta jest ładowana przez przeglądarkę użytkownika z innej domeny (b.com zamiast a.com), złoczyńca nie ma szans na kodowanie JavaScript, który ładuje treść, a zatem bieżący token CSRF naszego użytkownika z Twoja strona internetowa. Wynika to z faktu, że przeglądarki internetowe domyślnie nie obsługują żądań AJAX między domenami.

Zły facet również nie może uzyskać dostępu do pliku cookie ustawionego przez serwer, ponieważ domeny się nie zgadzają.

Kiedy powinienem zabezpieczyć się przed fałszowaniem żądań w różnych witrynach? Jeśli możesz upewnić się, że nie pomieszasz metod GET, POST i innych metod żądania opisanych powyżej, dobrym początkiem byłoby domyślnie zabezpieczenie wszystkich żądań POST.

Nie musisz chronić żądań PUT i DELETE, ponieważ jak wyjaśniono powyżej, standardowy formularz HTML nie może zostać przesłany przez przeglądarkę przy użyciu tych metod.

Z drugiej strony JavaScript może faktycznie wysyłać inne typy żądań, np. Używając funkcji $ .ajax () jQuery, ale pamiętaj, że aby żądania AJAX działały, domeny muszą się zgadzać (o ile nie skonfigurujesz inaczej serwera WWW) .

Oznacza to, że często nie musisz nawet dodawać tokena CSRF do żądań AJAX, nawet jeśli są to żądania POST, ale musisz upewnić się, że pomijasz sprawdzanie CSRF w swojej aplikacji internetowej, jeśli żądanie POST jest w rzeczywistości Żądanie AJAX. Możesz to zrobić, szukając obecności nagłówka takiego jak X-Requested-With, który zwykle zawiera żądania AJAX. Możesz także ustawić inny niestandardowy nagłówek i sprawdzić jego obecność po stronie serwera. Jest to bezpieczne, ponieważ przeglądarka nie dodawała niestandardowych nagłówków do zwykłego przesyłania formularza HTML (patrz wyżej), więc Pan Bad Guy nie ma szans na symulację tego zachowania za pomocą formularza.

Jeśli masz wątpliwości co do żądań AJAX, ponieważ z jakiegoś powodu nie możesz sprawdzić nagłówka takiego jak X-Requested-With, po prostu przekaż wygenerowany token CSRF do JavaScript i dodaj token do żądania AJAX. Można to zrobić na kilka sposobów; albo dodaj go do ładunku, jak zwykły formularz HTML, lub dodaj niestandardowy nagłówek do żądania AJAX. Tak długo, jak twój serwer wie, gdzie go szukać w przychodzącym żądaniu i jest w stanie porównać go z oryginalną wartością, którą pamięta z sesji lub pliku cookie, jesteś sortowany.

Dan
źródło
Dzięki za szczegółowe informacje. Podczas żądania wysyłania witryna musi wysłać token csrf na serwer, więc kiedy klient wyśle ​​ten token csrf na serwer? Czy dzieje się to podczas zgłaszania prośby o opcje inspekcji wstępnej? Proszę rozwinąć tę część.
Sm Srikanth
@Dan Dlaczego b.com może uzyskać dostęp do plików cookie innej witryny a.com?
zakir
8

Podstawą tego wszystkiego jest upewnienie się, że żądania pochodzą od rzeczywistych użytkowników witryny. Token csrf jest generowany dla formularzy i musi być powiązany z sesjami użytkownika. Służy do wysyłania żądań do serwera, na którym token je weryfikuje. Jest to jeden ze sposobów ochrony przed csrf, innym sposobem byłoby sprawdzenie nagłówka strony odsyłającej.

gladysbixly
źródło
7
Nie polegaj na nagłówku strony odsyłającej, można go łatwo sfałszować.
kag
3
To poprawna odpowiedź! Token MUSI być powiązany z sesją na serwerze. Porównanie danych Cookie + Form, takich jak najczęściej głosowana odpowiedź, sugeruje, że jest całkowicie błędna. Oba te komponenty stanowią część żądania, które konstruuje klient.
Lee Davis,
3
Właściwie nie. Token MUSI być powiązany z każdym ZAPYTANIEM do serwera. Jeśli powiążesz go tylko z sesją, ryzykujesz, że ktoś ukradnie token sesji i prześle żądanie z tym tokenem. Tak więc dla maksymalnego bezpieczeństwa token musi być powiązany z każdym wymaganiem http.
chrisl08