Rails: nie można zweryfikować autentyczności tokena CSRF podczas wysyłania żądania POST

89

Chcę napisać POST requestdo mojego lokalnego dewelopera w następujący sposób:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

Jednak z konsoli serwera to raportuje

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

Oto konfiguracja mojego kontrolera i tras, jest to dość proste.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

Nie jestem pewien, co muszę zrobić? Wyłączenie CSRF na pewno by się udało, ale myślę, że to powinien być mój błąd podczas tworzenia takiego API.

Czy muszę wykonać jakąś inną konfigurację?

cqcn1991
źródło
5
W przypadku interfejsów API ogólnie przyjmuje się wyłączenie weryfikacji tokenu CSRF . Używam protect_from_forgery with: :null_session.
dcestari

Odpowiedzi:

110

Fałszerstwo żądań między lokacjami (CSRF / XSRF) ma miejsce, gdy złośliwa strona internetowa nakłania użytkowników do wykonania żądania, które nie jest przeznaczone, na przykład za pomocą bookmarkletów, ramek iframe lub po prostu tworząc stronę, która jest wizualnie na tyle podobna, aby oszukać użytkowników.

Ochrona Szyny CSRF jest dla „klasycznych” aplikacji internetowych - to po prostu daje stopień pewności, że żądanie pochodzi z własnej aplikacji internetowej. Token CSRF działa jak sekret, który zna tylko twój serwer - Railsy generują losowy token i przechowuje go w sesji. Twoje formularze wysyłają token przez ukryte wejście, a Railsy sprawdzają, czy każde żądanie inne niż GET zawiera token, który pasuje do tego, co jest przechowywane w sesji.

Jednak interfejs API jest zwykle z definicji między witrynami i ma być używany nie tylko w Twojej aplikacji internetowej, co oznacza, że ​​cała koncepcja CSRF nie do końca ma zastosowanie.

Zamiast tego powinieneś użyć strategii opartej na tokenach, polegającej na uwierzytelnianiu żądań API za pomocą klucza API i klucza tajnego, ponieważ weryfikujesz, czy żądanie pochodzi od zatwierdzonego klienta API, a nie z Twojej własnej aplikacji.

Możesz dezaktywować CSRF, jak wskazał @dcestari:

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

Zaktualizowano. W Railsach 5 możesz generować aplikacje tylko dla API używając --apiopcji:

rails new appname --api

Nie obejmują one oprogramowania pośredniczącego CSRF ani wielu innych komponentów, które są superflouusem.

max
źródło
Dzięki, zdecydowałem się wyłączyć część CSRF: stackoverflow.com/questions/5669322/ ...
cqcn1991
4
sugeruje to, że wszystkie interfejsy API obsługują ruch między aplikacjami (w którym pojedynczy partner otrzymuje unikalny klucz i sekret). W tym scenariuszu, podobnie jak w przypadku komunikacji między serwerami, ta odpowiedź jest odpowiednia. JEDNAK to, co jest mylące dla większości twórców aplikacji internetowych, to fakt, że w przypadku klienta Javascript, kontrolowanego i napisanego przez Ciebie, NIE CHCESZ używać jednego klucza-tajnego (który ujawniłby jeden klucz-sekret wszystkim klientom). Zamiast tego, CSRF i mechanizm sesji plików cookie Raila działają świetnie - nawet w przypadku aplikacji Javascript, które używają twojego API - jeśli przekażesz token CSRF z powrotem do Railsów z każdym żądaniem.
Jason FB
sposób, w jaki to robisz w AJAX, jest opisany w tym wpisie SO stackoverflow.com/questions/7203304/ ...
Jason FB
@JasonFB masz rację w tym, że pojedynczy sekret z klientami javascript nie działa. Jednak używanie sesji i ochrony CSRF Railsów jest nadal problematyczne, jeśli zamierzasz zbudować API, które może być również używane przez inne typy klientów niebędących przeglądarkami.
maks.
Jeśli zapewniasz lub budujesz alternatywę dla CSRF, bądź moim gościem. Po prostu poznaj powód tego, co wymieniasz, aby wiedzieć, czym jest to właściwe. Oczywiście teraz nasze spory stają się kontekstowe.
Jason FB
77

Innym sposobem na wyłączenie CSRF, który nie wyrenderuje sesji zerowej, jest dodanie:

skip_before_action :verify_authenticity_token

w kontrolerze Rails. Zapewni to, że nadal będziesz mieć dostęp do informacji o sesji.

Ponownie upewnij się, że robisz to tylko w kontrolerach API lub w innych miejscach, w których ochrona CSRF nie ma zastosowania.

Matt Waldron
źródło
1
To jest to samo niż protect_from_forgery except: [:my_method_name]?
Arnold Roa
19

Odpowiednie informacje na temat konfiguracji CSRF w odniesieniu do kontrolerów API można znaleźć na api.rubyonrails.org :

Należy pamiętać, że dotyczy to również żądań XML lub JSON, a jeśli tworzysz interfejs API , powinieneś zmienić metodę ochrony przed fałszowaniem w ApplicationController(domyślnie:) :exception:

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

Możemy chcieć wyłączyć ochronę CSRF dla interfejsów API, ponieważ są one zwykle projektowane jako bezstanowe. Oznacza to, że klient żądania API będzie obsługiwał sesję zamiast Railsów.

Panie Tao
źródło
4
To jest takie zagmatwane. Huśtam się między źródłami, które twierdzą, że protect_from_forgeryjest to nadal konieczne dla interfejsów API, a źródłami, które twierdzą, że tak nie jest. Co się stanie, jeśli interfejs API jest przeznaczony dla aplikacji z pojedynczą stroną, która używa pliku cookie sesji do uwierzytelniania użytkownika?
Wylliam Judd
Mechanizm CSRF jest wbudowanym sposobem Railsów do radzenia sobie z wektorem ataku przy użyciu plików cookie sesji. Mechanizm chroni Twoje kontrolery, działając obok (właściwie wewnątrz) sesyjnego pliku cookie Rails. Bez protect_from_forgery, wyłączając go lub ustawiając na nim wyjątek, mówisz Railsom, aby NIE chroniły tej akcji przy użyciu informacji z tokenu CSRF (który jest pobierany z cookie sesji).
Jason FB
5
Myślę, że dezorientujące jest to, że w dokumentacji Railsów „API” oznacza aplikację serwer-serwer, która będzie otrzymywała żądania API od zaufanych, zdalnych partnerów (nie wszyscy użytkownicy sieci, którzy odwiedzają Twoją witrynę). Dla tych ppl, podaj unikalne pary klucz-tajny za pośrednictwem bezpiecznego mechanizmu, którego nie można zhakować. W przypadku nowoczesnych aplikacji internetowych, w których wiele osób ładuje Twoją aplikację internetową za pośrednictwem klienta internetowego, nadal używasz sesji lub mechanizmu opartego na tokenach, aby jednoznacznie zidentyfikować każdą osobę odwiedzającą Twoją witrynę. Więc jeśli nie używasz do tego JAKICHKOLWIEK INNYCH mechanizmów (tokeny Json Web itp.), Trzymaj się wbudowanych elementów Railsów.
Jason FB
1
sposób, w jaki to robisz w AJAX, jest opisany w tym wpisie SO stackoverflow.com/questions/7203304/ ...
Jason FB
12

Od Rails 5 możesz także utworzyć nową klasę za pomocą :: API zamiast :: Base:

class ApiController < ActionController::API
end
webaholik
źródło
3

Jeśli chcesz wykluczyć przykładową akcję kontrolera próbki

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

Możesz bez problemu rozpatrywać wnioski z zewnątrz.

Ryosuke Hujisawa
źródło
0

Najprostszym rozwiązaniem tego problemu jest wykonanie standardowych czynności w kontrolerze lub bezpośrednie umieszczenie go w ApplicationController

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

Arish Khan
źródło