Zrozumienie tokenu autentyczności Railsów

982

Mam wiele problemów związanych z tokenem autentyczności w Railsach, tak jak wiele razy.

Ale tak naprawdę nie chcę po prostu rozwiązać tego problemu i kontynuować. Naprawdę chciałbym zrozumieć token autentyczności. Cóż, moje pytanie brzmi: czy masz jakieś pełne źródło informacji na ten temat, czy poświęciłbyś czas na szczegółowe wyjaśnienie tutaj?

Ricardo Acras
źródło
7
Zobacz także: „Dlaczego Google Prepend while (1) do odpowiedzi JSON?” stackoverflow.com/questions/2669690/…
Chloe

Odpowiedzi:

1462

Co się dzieje

Gdy użytkownik wyświetla formularz w celu utworzenia, aktualizacji lub zniszczenia zasobu, aplikacja Rails tworzy losowy authenticity_token, przechowuje ten token w sesji i umieszcza go w ukrytym polu w formularzu. Gdy użytkownik prześle formularz, Rails szuka go authenticity_token, porównuje z tym zapisanym w sesji i jeśli są zgodne, żądanie może być kontynuowane.

Dlaczego tak się dzieje

Ponieważ token uwierzytelnienia jest przechowywany w sesji, klient nie może poznać jego wartości. Zapobiega to przesyłaniu formularzy do aplikacji Rails bez przeglądania formularza w samej aplikacji. Wyobraź sobie, że korzystasz z usługi A, zalogowałeś się do usługi i wszystko jest w porządku. Teraz wyobraź sobie, że poszedłeś skorzystać z usługi B i zobaczyłeś zdjęcie, które ci się podoba, i nacisnąłeś je, aby zobaczyć większy rozmiar. Teraz, jeśli w usłudze B był jakiś zły kod, może on wysłać żądanie do usługi A (do której jesteś zalogowany) i poprosić o usunięcie konta, wysyłając żądanie na adres http://serviceA.com/close_account. Jest to tak zwane CSRF (Cross Site Request Forgery) .

Jeśli usługa A korzysta z tokenów autentyczności, ten wektor ataku nie ma już zastosowania, ponieważ żądanie z usługi B nie będzie zawierało prawidłowego tokenu autentyczności i nie będzie można kontynuować.

Dokumenty API opisują szczegóły dotyczące metatagu:

Ochrona CSRF jest włączana za pomocą protect_from_forgerymetody, która sprawdza token i resetuje sesję, jeśli nie jest zgodna z oczekiwaniami. Wywołanie tej metody jest generowane domyślnie dla nowych aplikacji Railsowych. Parametr token ma authenticity_tokendomyślną nazwę . Nazwę i wartość tego tokena należy dodać do każdego układu wyświetlającego formularze poprzez włączenie csrf_meta_tagsdo nagłówka HTML.

Notatki

Pamiętaj, że Railsy weryfikują tylko metody bez idempotentne (POST, PUT / PATCH i DELETE). Żądanie GET nie jest sprawdzane pod kątem tokenu autentyczności. Dlaczego? ponieważ specyfikacja HTTP stwierdza, że ​​żądania GET są idempotentne i nie powinny tworzyć, zmieniać ani niszczyć zasobów na serwerze, a żądanie powinno być idempotentne (jeśli uruchomisz to samo polecenie wiele razy, za każdym razem powinieneś uzyskać ten sam wynik).

Również rzeczywista implementacja jest nieco bardziej skomplikowana, jak określono na początku, zapewniając lepsze bezpieczeństwo. Railsy nie wydają tego samego przechowywanego tokena w każdym formularzu. Nie generuje i nie zapisuje za każdym razem innego tokena. Generuje i przechowuje hash kryptograficzny w sesji i wydaje nowe tokeny kryptograficzne, które można dopasować do przechowywanego, za każdym razem, gdy strona jest renderowana. Zobacz request_forgery_protection.rb .

Lekcje

Służy authenticity_tokendo ochrony nie idempotentnych metod (POST, PUT / PATCH i DELETE). Upewnij się także, aby nie dopuścić do żadnych żądań GET, które mogłyby potencjalnie modyfikować zasoby na serwerze.


EDYCJA: Sprawdź komentarz @erturne, że żądania GET są idempotentne. Wyjaśnia to lepiej niż ja tutaj.

Faisal
źródło
25
@Faisal, czy osoba atakująca może po prostu odczytać / przechwycić „ukryty” element formularza dla usługi A i uzyskać ten unikalny token wygenerowany dla użytkownika - biorąc pod uwagę, że uzyskał on dostęp do sesji rozpoczętej przez użytkownika dla usługi A?
marcamillion,
11
@marcamillion: Jeśli ktoś porwał twoją sesję w serwisie A, token autentyczności cię nie ochroni. Porywacz będzie mógł złożyć wniosek i będzie mógł kontynuować.
Faisal
12
@zabba: Railsy zgłaszają wyjątek ActionController :: InvalidAuthenticityToken, jeśli formularz zostanie przesłany bez odpowiedniego tokena. Możesz uratować_z wyjątku i wykonać dowolne przetwarzanie.
Faisal
5
re „Upewnij się także, aby nie składać żadnych żądań GET, które mogłyby potencjalnie modyfikować zasoby na serwerze.” - Dotyczy to nie używając match () na trasach, które mogłyby potencjalnie pozwalają żądań dostać się do działań kontrolerów przeznaczona do odbioru tylko wiadomości
Steven Soroka
102
„... a żądanie powinno być idempotentne (jeśli uruchomisz to samo polecenie wiele razy, za każdym razem powinieneś otrzymać ten sam wynik).” Tylko subtelne wyjaśnienie tutaj. Bezpieczny oznacza brak skutków ubocznych. Idempotent oznacza ten sam efekt uboczny, bez względu na to, ile razy usługa jest wywoływana. Wszystkie bezpieczne usługi są z natury idempotentne, ponieważ nie występują żadne skutki uboczne. Wielokrotne wywołanie GET dla zasobu czasu bieżącego zwróci za każdym razem inny wynik, ale jest to bezpieczne (a więc idempotentne).
erturne
137

Token autentyczności został zaprojektowany tak, abyś wiedział, że formularz jest przesyłany z Twojej witryny. Jest generowany z komputera, na którym działa, z unikalnym identyfikatorem, który zna tylko twoja maszyna, co pomaga zapobiegać atakom polegającym na fałszowaniu żądań w różnych witrynach.

Jeśli po prostu masz trudności z szynami odmawiającymi dostępu do skryptu AJAX, możesz użyć

<%= form_authenticity_token %>

aby wygenerować poprawny token podczas tworzenia formularza.

Możesz przeczytać więcej na ten temat w dokumentacji .

Topher Fangio
źródło
88

Co to jest CSRF?

Token autentyczności stanowi środek przeciwdziałający fałszowaniu żądań między witrynami (CSRF). Co to jest CSRF, pytasz?

Jest to sposób, w jaki osoba atakująca może potencjalnie przejąć sesje, nie znając nawet tokenów sesji.

Scenariusz :

  • Wejdź na stronę swojego banku, zaloguj się.
  • Następnie odwiedź stronę atakującego (np. Reklama sponsorowana z niezaufanej organizacji).
  • Strona atakującego zawiera formularz z tymi samymi polami, co formularz banku „Transfer Funds”.
  • Atakujący zna informacje o koncie i ma wstępnie wypełnione pola formularza, aby przesłać pieniądze z konta na konto atakującego.
  • Strona atakującego zawiera Javascript, który przesyła formularz do Twojego banku.
  • Po przesłaniu formularza przeglądarka zawiera pliki cookie dla witryny banku, w tym token sesji.
  • Bank przekazuje pieniądze na konto atakującego.
  • Forma może być w niewidocznym elemencie iframe, więc nigdy nie wiadomo, że nastąpił atak.
  • Nazywa się to fałszowaniem żądań krzyżowych (CSRF).

Rozwiązanie CSRF :

  • Serwer może oznaczać formularze pochodzące z samego serwera
  • Każdy formularz musi zawierać dodatkowy token uwierzytelnienia jako ukryte pole.
  • Token musi być nieprzewidywalny (atakujący go nie zgadnie).
  • Serwer udostępnia poprawny token w formularzach na swoich stronach.
  • Serwer sprawdza token po wysłaniu formularza, odrzuca formularze bez odpowiedniego tokena.
  • Przykładowy token: identyfikator sesji zaszyfrowany tajnym kluczem serwera.
  • Railsy automatycznie generują takie tokeny: zobacz pole wejściowe token_weryfikacji w każdej formie.
Rose Perrone
źródło
1
Oto wersja tego samego wyjaśnienia, która jest mniej precyzyjna, ale także mniej abstrakcyjna: stackoverflow.com/a/33829607/2810305
Lutz Prechelt
Nie jestem pewien, ale czy współczesne przeglądarki umożliwiają wysyłanie nie idempotentnych żądań (POST / PUT / DELETE) do innej domeny? Myślę, że musi istnieć ochrona przed takimi rzeczami w samej przeglądarce
divideByZero
45

Minimalny przykład ataku, któremu można by zapobiec: CSRF

Na mojej stronie evil.comprzekonuję Cię do przesłania następującego formularza:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="to"      value="ciro"></p>
  <p><input type="hidden" name="ammount" value="100"></p>
  <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>

Jeśli zalogujesz się do swojego banku za pomocą sesyjnych plików cookie, pliki cookie zostaną wysłane, a przelew zostanie wykonany bez Twojej wiedzy.

Oznacza to, że w grę wchodzi token CSRF:

  • z odpowiedzią GET, która zwróciła formularz, Railsy wysyłają bardzo długi losowy parametr ukryty
  • kiedy przeglądarka wysyła żądanie POST, wyśle ​​parametr wraz z serwerem, a serwer zaakceptuje go tylko wtedy, gdy będzie pasował

Tak więc formularz w autentycznej przeglądarce wyglądałby następująco:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
  <p><input type="hidden" name="to"                 value="ciro"></p>
  <p><input type="hidden" name="ammount"            value="100"></p>
  <p><button type="submit">Send 100$ to Ciro.</button></p>
</form>

Tak więc mój atak nie powiódłby się, ponieważ nie wysłał authenticity_tokenparametru, i nie ma sposobu, bym mógł go odgadnąć, ponieważ jest to ogromna liczba losowa.

Ta technika zapobiegania nosi nazwę wzorca tokenu synchronizatora .

Polityka tego samego pochodzenia

Ale co jeśli atakujący wysyła dwa żądania za pomocą JavaScript, jedno do odczytu tokena, a drugie do przeniesienia?

Sam wzorzec tokenu synchronizatora nie wystarczy, aby temu zapobiec!

W tym właśnie momencie na ratunek przychodzi Polityka tego samego pochodzenia, jak wyjaśniłem na stronie : /security/8264/why-is-the-same-origin-policy-so-important/72569# 72569

Jak Rails wysyła tokeny

Objęte: Railsy: Jak działa csrf_meta_tag?

Gruntownie:

  • Pomocnicy HTML tacy jak form_tagdodają ukryte pole do formularza, jeśli nie jest to formularz GET

  • AJAX jest obsługiwany automatycznie przez jquery-ujs , który odczytuje token z metaelementów dodanych do nagłówka przez csrf_meta_tags(obecnych w szablonie domyślnym) i dodaje go do każdego złożonego żądania.

    uJS próbuje również zaktualizować token w formularzach w nieaktualnych buforowanych fragmentach.

Inne podejścia zapobiegawcze

Ciro Santilli
źródło
Dziękujemy, ale twój punkt oparcia się na tej samej polityce pochodzenia, aby nie móc po prostu odczytać tokena CSRF, wydaje się błędny. Więc najpierw mówisz, że możesz wysłać test POST do innego źródła, ale nie możesz go odczytać, wydaje się to dziwne, ale myślę, że jest poprawne, ale możesz wstrzyknąć obraz lub tag skryptu, aby dostać się na stronę i połączyć program obsługi do analizy odpowiedzi i rozumiem tak?
bjm88
@ bjm88 wstrzyknąć skrypt gdzie? Na twojej stronie, czy na zaatakowanej stronie? W przypadku zaatakowania witryny zezwolenie na wstrzyknięcie skryptu jest dobrze znaną luką w zabezpieczeniach i skutecznie pionuje witrynę. Każda strona musi walczyć z urządzeniami sanitarnymi. W przypadku obrazów nie widzę, jak można ich użyć do ataku. Na atakującej stronie: możesz zmodyfikować swoją przeglądarkę, aby umożliwić odczyt, a tym samym automatycznie zastawiać się do woli :-), ale przyzwoite przeglądarki domyślnie temu zapobiegają, spróbuj.
Ciro Santilli 28 病毒 审查 六四 事件 法轮功
43

Token autentyczności służy do zapobiegania atakom typu Cross-Site Request Forgery (CSRF). Aby zrozumieć token autentyczności, musisz najpierw zrozumieć ataki CSRF.

CSRF

Załóżmy, że jesteś autorem bank.com. Na stronie znajduje się formularz służący do przesyłania pieniędzy na inne konto z prośbą GET:

wprowadź opis zdjęcia tutaj

Haker może po prostu wysłać do serwera żądanie HTTP z pytaniem GET /transfer?amount=$1000000&account-to=999999, prawda?

wprowadź opis zdjęcia tutaj

Źle. Atak hakerów nie zadziała. Serwer w zasadzie pomyśli?

Co? Kim jest ten facet, który próbuje zainicjować przeniesienie. To na pewno nie właściciel konta.

Skąd serwer o tym wie? Ponieważ nie ma session_idpliku cookie uwierzytelniającego osobę żądającą.

Po zalogowaniu się przy użyciu nazwy użytkownika i hasła serwer ustawia session_idplik cookie w przeglądarce. W ten sposób nie musisz uwierzytelniać każdego żądania za pomocą nazwy użytkownika i hasła. Gdy przeglądarka wysyła session_idciasteczko, serwer wie:

Och, to jest John Doe. Zalogował się 2,5 minuty temu. Jest dobry do wyjścia.

Haker może pomyśleć:

Hmm Normalne żądanie HTTP nie zadziała, ale gdybym mógł dostać rękę na ten session_idplik cookie, byłbym złoty.

Przeglądarka użytkowników ma ustawiony plik cookie dla bank.comdomeny. Za każdym razem, gdy użytkownik bank.comwysyła żądanie do domeny, wszystkie pliki cookie są wysyłane. W tym session_idciasteczko.

Więc jeśli haker mógłby zmusić cię do złożenia żądania GET, które przeleje pieniądze na jego konto, odniesie sukces. Jak mógł cię oszukać? Z fałszowaniem żądań między witrynami.

Właściwie to po prostu proste. Haker może po prostu zachęcić cię do odwiedzenia jego strony internetowej. Na swojej stronie internetowej może mieć następujący tag graficzny:

<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">

Gdy przeglądarka użytkowników natrafi na ten znacznik obrazu, wysyła żądanie GET do tego adresu URL. A ponieważ żądanie pochodzi z jego przeglądarki, wyśle ​​z nim wszystkie powiązane pliki cookie bank.com. Jeśli użytkownik ostatnio zalogował się w bank.com... session_idplik cookie zostanie ustawiony, a serwer pomyśli, że użytkownik zamierzał przelać 1 000 000 USD na konto 999999!

wprowadź opis zdjęcia tutaj

Po prostu nie odwiedzaj niebezpiecznych stron, a wszystko będzie dobrze.

To nie wystarczy Co jeśli ktoś opublikuje ten obraz na Facebooku i pojawi się on na twojej ścianie? Co się stanie, jeśli zostanie wstrzyknięty do witryny odwiedzanej przez atak XSS?

Nie jest tak źle. Tylko żądania GET są wrażliwe.

Nie prawda. Formularz, który wysyła żądanie POST, może być generowany dynamicznie. Oto przykład z Poradnika na temat bezpieczeństwa w Railsach :

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

Token autentyczności

Kiedy masz ApplicationControllerto:

protect_from_forgery with: :exception

To:

<%= form_tag do %>
  Form contents
<% end %>

Jest skompilowany w to:

<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

W szczególności generowane są następujące elementy:

<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />

Aby zabezpieczyć się przed atakami CSRF, jeśli Railsy nie zobaczą tokena autentyczności wysłanego wraz z żądaniem, nie uzna wniosku za bezpieczne.

Jak atakujący powinien wiedzieć, co to jest ten token? Przy każdym generowaniu formularza losowo generowana jest inna wartość:

wprowadź opis zdjęcia tutaj

Atak Cross Site Scripting (XSS) - tak właśnie jest. Ale to inna wrażliwość na inny dzień.

Adam Zerner
źródło
39

Jest Authenticity Tokento metoda „szynowa ”, aby zapobiec atakom typu „fałszowanie żądań w różnych witrynach (CSRF lub XSRF)” .

Mówiąc najprościej, upewnia się, że żądania PUT / POST / DELETE (metody, które mogą modyfikować treść) do Twojej aplikacji internetowej są wysyłane z przeglądarki klienta, a nie od strony trzeciej (atakującego), która ma dostęp do utworzonego pliku cookie po stronie klienta.

i ja
źródło
34

ponieważ Authenticity Tokenjest to bardzo ważne, aw Railsach 3.0+ możesz używać

 <%= token_tag nil %>

tworzyć

<input name="authenticity_token" type="hidden" value="token_value">

gdziekolwiek

Yuan He
źródło
To mi pomogło. W rzeczywistości próbowałem to zrobić XSSna stronie logowania, nie w nikczemnych celach, ale w celu utworzenia nowej sesji z wstępnie wypełnioną nazwą użytkownika. Teraz wiem, że mogę po prostu użyć value="token_value".
Michael - Where's Clay Shirky
27

Uwaga: mechanizm tokenu autentyczności może powodować warunki wyścigu, jeśli masz wiele równoczesnych żądań od tego samego klienta. W tej sytuacji Twój serwer może wygenerować wiele tokenów autentyczności, gdy powinien być tylko jeden, a klient otrzymujący wcześniejszy token w formularzu zawiedzie przy następnym żądaniu, ponieważ token cookie sesji został nadpisany. Tutaj jest opis tego problemu i nie do końca trywialne rozwiązanie: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

jdp
źródło
11

Metody Gdzie authenticity_tokenjest wymagane

authenticity_token jest wymagane w przypadku idempotentnych metod, takich jak wysyłanie, umieszczanie i usuwanie, ponieważ metody Idempotent mają wpływ na dane.

Dlaczego jest to wymagane

Jest to konieczne, aby zapobiec złym działaniom. token autentyczności jest przechowywany w sesji, ilekroć na stronach internetowych tworzony jest formularz służący do tworzenia lub aktualizacji zasobów, token autentyczności jest przechowywany w ukrytym polu i wysyłany z formularzem na serwer. Przed wykonaniem akcji użytkownik wysłany token autentyczności jest sprawdzany krzyżowo z authenticity_tokenzapisem w sesji. Jeśli authenticity_tokenjest taki sam, wówczas proces jest kontynuowany, w przeciwnym razie nie wykonuje akcji.

uma
źródło
3
W rzeczywistości, czy nie jest odwrotnie? GET jest idempotentny, ponieważ jego wywołanie nie powinno zmieniać stanu systemu, gdzie czasowniki PUT POST i DELETE NIE są czasownikami idempotentnymi, ponieważ zmieniają stan systemu. IE: token autentyczności jest wymagany w przypadku NIE idempotentnych metod.
Jean-Théo,
2
@ Jean-Daube, uma: idempotent oznacza, że ​​po dwukrotnym wykonaniu akcja dzieje się tylko raz. GET, PUT i DELETE idempotentne: w3.org/Protocols/rfc2616/rfc2616-sec9.html Kluczową właściwością klucza nie jest tutaj idempotencja, ale jeśli metoda się zmienia lub nie dane, które są nazywane „Bezpieczną metodą”, czy nie.
Ciro Santilli 12 病毒 审查 六四 事件 法轮功
6

Co to jest token uwierzytelniania?

Jest to losowy ciąg używany przez aplikację railsową, aby upewnić się, że użytkownik żąda lub wykonuje akcję ze strony aplikacji, a nie z innej aplikacji lub witryny.

Dlaczego potrzebny jest token uwierzytelniający?

Aby chronić aplikację lub witrynę przed fałszowaniem żądań w różnych witrynach.

Jak dodać token uwierzytelnienia do formularza?

Jeśli generujesz formularz przy użyciu tagu form_for, automatycznie zostanie dodany token uwierzytelniający, którego możesz użyć <%= csrf_meta_tag %>.

Pradeep Sapkota
źródło