Rails: non è possibile verificare l'autenticità del token CSRF quando si effettua una richiesta POST


91

Voglio fare POST requestal mio sviluppatore locale, in questo modo:

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

Tuttavia, dalla console del server segnala

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

Ecco il mio controller e la configurazione delle rotte, è abbastanza semplice.

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

  post 'fetch_heroku' => 'admin#fetch_heroku'

Non sono sicuro di cosa devo fare? Disattivare il CSRF funzionerebbe sicuramente, ma penso che dovrebbe essere un mio errore durante la creazione di tale API.

C'è qualche altra configurazione che devo fare?


5
Per le API è generalmente accettato di disattivare la convalida del token CSRF . Io uso protect_from_forgery with: :null_session.
dcestari

Risposte:


110

Cross site request forgery (CSRF / XSRF) si verifica quando una pagina Web dannosa induce gli utenti a eseguire una richiesta non intesa, ad esempio, utilizzando bookmarklet, iframe o semplicemente creando una pagina visivamente abbastanza simile da ingannare gli utenti.

La protezione Rails CSRF è fatta per le app web "classiche" - dà semplicemente un certo grado di sicurezza che la richiesta abbia avuto origine dalla tua app web. Un token CSRF funziona come un segreto che solo il tuo server conosce: Rails genera un token casuale e lo memorizza nella sessione. I tuoi moduli inviano il token tramite un input nascosto e Rails verifica che qualsiasi richiesta non GET includa un token che corrisponde a ciò che è memorizzato nella sessione.

Tuttavia, un'API è solitamente per definizione cross-site e pensata per essere utilizzata in più della tua app web, il che significa che l'intero concetto di CSRF non si applica del tutto.

Dovresti invece utilizzare una strategia basata su token per autenticare le richieste API con una chiave API e un segreto poiché stai verificando che la richiesta proviene da un client API approvato, non dalla tua app.

Puoi disattivare CSRF come indicato da @dcestari:

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

Aggiornato. In Rails 5 puoi generare applicazioni solo API utilizzando l' --apiopzione:

rails new appname --api

Non includono il middleware CSRF e molti altri componenti superflui.


Grazie, ho scelto di girare una parte del CSRF off: stackoverflow.com/questions/5669322/...
cqcn1991

4
questo suggerisce che tutte le API servono il traffico da applicazione ad applicazione (in cui a un singolo partner viene rilasciata una chiave e un segreto univoci). In quello scenario, come una comunicazione da server a server, questa risposta è appropriata. TUTTAVIA, ciò che confonde la maggior parte degli sviluppatori di app Web è che per un client Javascript, controllato e scritto da te, NON VUOI utilizzare un singolo segreto chiave (che esporrebbe un singolo segreto chiave a tutti i client). Invece, il CSRF di Rail e il meccanismo di sessione dei cookie funzionano alla grande, anche per le app Javascript che utilizzano la tua API, se passi il token CSRF a Rails con ogni richiesta.
Jason FB

il modo in cui lo fai in AJAX è descritto in questo post SO stackoverflow.com/questions/7203304/…
Jason FB

@ JasonFB hai ragione in quanto un singolo segreto con i client javascript non funziona. Tuttavia, l'utilizzo delle sessioni e della protezione CSRF di Rails è ancora problematico se si intende creare un'API che può essere utilizzata anche da altri tipi di client non browser.
max

Se fornisci o costruisci un'alternativa a CSRF, sii mio ospite. Basta conoscere il motivo di ciò che si sta sostituendo in modo da sapere con cosa è appropriato sostituirlo. Ovviamente, ora il nostro cavillo diventa contestuale.
Jason FB

79

Un altro modo per disattivare CSRF che non renderà una sessione nulla è aggiungere:

skip_before_action :verify_authenticity_token

nel tuo controller Rails. In questo modo avrai ancora accesso alle informazioni sulla sessione.

Ancora una volta, assicurati di farlo solo nei controller API o in altri luoghi in cui la protezione CSRF non si applica.


1
Questo è lo stesso di protect_from_forgery except: [:my_method_name]?
Arnold Roa,

19

Ci sono informazioni rilevanti su una configurazione di CSRF rispetto ai controller API su api.rubyonrails.org :

È importante ricordare che anche le richieste XML o JSON sono interessate e se stai creando un'API dovresti cambiare il metodo di protezione dalla contraffazione in ApplicationController(per impostazione predefinita:) :exception:

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

Potremmo voler disabilitare la protezione CSRF per le API poiché in genere sono progettate per essere prive di stato. Cioè, il client API di richiesta gestirà la sessione per te invece di Rails.


4
Questo è così confuso. Sto oscillando tra le fonti che dicono che protect_from_forgeryè ancora necessario per le API e le fonti che dicono che non lo è. Cosa succede se l'API è per un'app a pagina singola, che utilizza il cookie di sessione per l'autenticazione dell'utente?
Wylliam Judd

Il meccanismo CSRF è il modo integrato di Rails per gestire il vettore di attacco utilizzando i cookie di sessione. Il meccanismo protegge i tuoi controller, mentre opera insieme (in realtà all'interno) del cookie di sessione di Rails. Senza protect_from_forgery, o disattivandolo o impostando un'eccezione su di esso, stai dicendo a Rails di NON proteggere quell'azione usando le informazioni dal token CSRF (che è preso dal cookie di sessione).
Jason FB

5
Penso che ciò che crea confusione sia che per la documentazione di Rails "API" significa un'applicazione server-to-server che riceverà richieste API da partner remoti fidati (non tutti gli utenti web che visitano il tuo sito). Per quelle persone, fornire coppie chiave-segreto univoche tramite un meccanismo sicuro non hackerabile. Per le applicazioni Web moderne, in cui molte persone caricheranno la tua app Web tramite un client Web, utilizzi comunque una sessione o un meccanismo basato su token per identificare in modo univoco ogni persona che visita il tuo sito. Quindi, a meno che tu non stia usando QUALCHE ALTRO meccanismo per farlo (token Web Json, ecc.), Attenersi alle cose integrate di Rails.
Jason FB

1
il modo in cui lo fai in AJAX è descritto in questo post SO stackoverflow.com/questions/7203304/…
Jason FB

13

Da Rails 5 puoi anche creare una nuova classe con :: API invece di :: Base:

class ApiController < ActionController::API
end

3

Se si desidera escludere l'azione di esempio del controller di esempio

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

  def sample
     render json: @hogehoge
  end
end

Puoi elaborare le richieste dall'esterno senza problemi.


0

La soluzione più semplice per il problema è fare le cose standard nel tuo controller o puoi metterlo direttamente in ApplicationController

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


0

Se vuoi saltare solo la protezione CSRF per una o più azioni del controller (invece dell'intero controller), prova questo

skip_before_action :verify_authenticity_token, only [:webhook, :index, :create]

Dove [:webhook, :index, :create]salterà il controllo per queste 3 azioni, ma puoi passare a quella che vuoi saltare

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.