Token autentyczności w Rails 4

198

Pracowałem nad nową aplikacją Rails 4 (na Ruby 2.0.0-p0), kiedy napotkałem pewne problemy z tokenami autentyczności.

Podczas pisania kontrolera, który reaguje na json (przy użyciu respond_tometody klasy), doszedłem do createakcji, w której zacząłem uzyskiwać ActionController::InvalidAuthenticityTokenwyjątki, gdy próbowałem utworzyć rekord za pomocą curl.

Upewniłem się, że ustawiłem -H "Content-Type: application/json"i ustawiłem dane, -d "<my data here>"ale nadal nie miałem szczęścia.

Próbowałem napisać ten sam kontroler przy użyciu Rails 3.2 (na Ruby 1.9.3) i nie dostałem żadnych problemów z tokenem autentyczności. Rozejrzałem się i zobaczyłem, że w Railsie 4 wprowadzono pewne zmiany dotyczące tokenów autentyczności. Z tego, co rozumiem, nie są już one automatycznie wstawiane do formularzy? Przypuszczam, że to w jakiś sposób wpływa na typy treści inne niż HTML.

Czy jest jakiś sposób na obejście tego bez konieczności żądania formularza HTML, wyłapywania tokena autentyczności, a następnie wysyłania kolejnego żądania z tym tokenem? A może zupełnie brakuje mi czegoś, co jest całkowicie oczywiste?

Edycja: Właśnie próbowałem utworzyć nowy rekord w nowej aplikacji Rails 4 przy użyciu rusztowania bez zmiany czegokolwiek i napotykam ten sam problem, więc chyba nie zrobiłem tego.

alexcoco
źródło

Odpowiedzi:

277

Myślę, że właśnie to rozgryzłem. Zmieniłem (nowy) domyślny

protect_from_forgery with: :exception

do

protect_from_forgery with: :null_session

jak na komentarz w ApplicationController.

# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.

Różnicę można zobaczyć, patrząc na źródło request_forgery_protecton.rb, a dokładniej następujące wiersze:

W Railsach 3.2 :

# This is the method that defines the application behavior when a request is found to be unverified.
# By default, \Rails resets the session when it finds an unverified request.
def handle_unverified_request
  reset_session
end

W szynach 4 :

def handle_unverified_request
  forgery_protection_strategy.new(self).handle_unverified_request
end

Który wywoła następujące :

def handle_unverified_request
  raise ActionController::InvalidAuthenticityToken
end
alexcoco
źródło
20
Możesz usunąć: z opcją razem, domyślnie: null_session
Casey
6
Czy istnieje sposób wykorzystania wyjątku dla wywołań innych niż JSON i sesji zerowej dla wywołań JSON (zwanych także wywołaniami API)?
James McMahon
@JamesMcMahon Możesz napisać własny, before_actionktóry sprawdza format i czy żądanie jest zweryfikowane. Nie znam żadnego wbudowanego sposobu ustawiania warunków.
alexcoco
1
W szynach 4.1.6 musiałem również określić skip_before_action :verify_authenticity_tokenw kontrolerze aplikacji mojego API, aby to działało.
Waseem
1
Nie powinieneś już :verify_authenticy_tokenwięcej wyłączać w Rails 4.2. Domyślnie jest to :null_session, co, jak sama nazwa wskazuje, po prostu wysyła żądanie bez sesji. Zamiast tego możesz zweryfikować żądanie za pomocą klucza API.
lobati,
72

Zamiast wyłączać ochronę csrf, lepiej dodać następujący wiersz kodu do formularza

<%= tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) %> 

a jeśli używasz form_for lub form_tag do wygenerowania formularza, wówczas automatycznie doda powyższy wiersz kodu w formularzu

xiaoboa
źródło
9
Jest to dobra rada, jeśli tworzysz formularz, ale pytanie dotyczy wykonywania wywołań interfejsu API za pomocą JSON.
James McMahon
1
Dziękuję, to odpowiedziało na pytanie, które miałem o pisanie kodu podczas używania kierownicy!
David Routen
70

Dla mnie zadziałało dodanie następującego wiersza do formularza:

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
Carlos
źródło
12
tak naprawdę nie dotyczy połączeń api
courtimas
2
W moim przypadku używam Rails4. Mogę przesłać formularz, klikając przycisk Prześlij. Ale jeśli prześlę formularz za pomocą kodu JS, wystąpi ten błąd. Ta odpowiedź naprawiła dla mnie problem.
Chris.Zou
2
Dla mojej aplikacji opartej na Rails 4.0.8 wystarczyło napisać =token_tag nillub (w .erb)<%= token_tag nil %>
jmarceli
1
Wygląda na to, że wyłączasz uwierzytelnianie, ustawiając token na zero?
Carlos
czy jest to bezpieczne?
Angel Garcia
33

Nie sądzę, że dobrze jest wyłączyć ochronę CSRF, o ile nie implementujesz wyłącznie interfejsu API.

Przeglądając dokumentację API Rails 4 dla ActionController , zauważyłem, że możesz wyłączyć ochronę przed fałszowaniem na kontrolerze lub podstawie metody.

Na przykład, aby wyłączyć ochronę CSRF dla metod, których możesz użyć

class FooController < ApplicationController
  protect_from_forgery except: :index
wędrowanie
źródło
Zrobiło to dla mnie w Railsach 4 - błąd został zgłoszony po próbie usunięcia. ^^
zero_cool
9

Wystąpił ten sam problem. Naprawiono to poprzez dodanie do mojego kontrolera:

      skip_before_filter :verify_authenticity_token, if: :json_request?
użytkownik1756254
źródło
niezdefiniowana metoda `json_request? ' dla # <SettingsController: 0x000000030a54a8>, Masz pomysł?
Deano
Musisz zdefiniować tam metodę, taką jak: def json_request? request.format.json? koniec
Jai Kumar Rajput,
7

Próbowałeś?

 protect_from_forgery with: :null_session, if: Proc.new {|c| c.request.format.json? }
zechtz
źródło
2

Funkcje te zostały dodane do celów bezpieczeństwa i ochrony przed fałszowaniem.
Jednak, aby odpowiedzieć na twoje pytanie, oto kilka danych wejściowych. Możesz dodać te linie po nazwie kontrolera.

Tak jak

class NameController < ApplicationController
    skip_before_action :verify_authenticity_token

Oto kilka linii dla różnych wersji szyn.

Szyny 3

skip_before_filter: token_autentyczności

Szyny 4 :

pominąć_początek: token_autentyczności


Jeśli zamierzasz wyłączyć tę funkcję bezpieczeństwa dla wszystkich procedur kontrolera, możesz zmienić wartość protect_from_forgery na : null_session w pliku application_controller.rb.

Tak jak

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end
Kent Aguilar
źródło
1

Jeśli używasz jQuery z szynami, zachowaj ostrożność, pozwalając na dostęp do metod bez weryfikacji tokena autentyczności.

jquery-ujs może zarządzać tokenami za ciebie

Powinieneś go mieć już jako część klejnotu jquery-rails, ale może być konieczne dołączenie go do application.js z

//= require jquery_ujs

To wszystko, czego potrzebujesz - Twoje wywołanie ajaxowe powinno teraz działać

Aby uzyskać więcej informacji, zobacz: https://github.com/rails/jquery-ujs

użytkownik1555400
źródło
1

Dodaj authenticity_token: truedo znacznika formularza

Salma Gomaa
źródło
0

Podczas definiowania własnego formularza HTML należy dołączyć ciąg tokena uwierzytelniającego, który ze względów bezpieczeństwa należy przesłać do kontrolera. Jeśli używasz szyn pomocnika formularza do wygenerowania tokena autentyczności, dodajesz go do formularza w następujący sposób.

<form accept-charset="UTF-8" action="/login/signin" method="post">
  <div style="display:none">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="authenticity_token" type="hidden" value="x37DrAAwyIIb7s+w2+AdoCR8cAJIpQhIetKRrPgG5VA=">
  </div>
    ...
</form>

Rozwiązaniem problemu jest więc dodanie pola token_ autentyczności lub użycie szyn pomocników zamiast zagrażać bezpieczeństwu itp.

amjad
źródło
1
Dziękuję za odpowiedź, ale to nie odpowiada na pierwotne pytanie. Pytanie dotyczące odpowiedzi na żądania JSON, ale zapewniasz rozwiązanie dla od podstaw formularza HTML. Podczas wysyłania żądań JSON (pomyśl API) nie przesyłasz formularza HTML i możesz nie mieć łatwego dostępu do tokena autentyczności.
alexcoco
chyba że treść żądania jest w rzeczywistości JSON, w którym to przypadku chciałbyś ją ustawić application/json.
alexcoco
Rozumiem, że odpowiedź nie jest dokładnie tym, czego szukasz. Ale celem odpowiedzi jest udzielenie pomocy użytkownikom szukającym podobnych „problemów z autentycznością”.
amjad
przepraszam usunąłem omyłkowo - „możesz ustawić Content-Type: application / x-www-form-urlencoded z powyższym rozwiązaniem”.
amjad
0

Wszystkie moje testy działały dobrze. Ale z jakiegoś powodu ustawiłem moją zmienną środowiskową na nietestową:

export RAILS_ENV=something_non_test

Zapomniałem rozbroić tę zmienną, z powodu której zacząłem uzyskiwać ActionController::InvalidAuthenticityTokenwyjątek.

Po rozbrojeniu $RAILS_ENVmoje testy znów zaczęły działać.

Mridula
źródło