Comprensione del token di autenticità di Rails


983

Sto riscontrando alcuni problemi relativi al token di autenticità in Rails, come ho fatto molte volte ora.

Ma davvero non voglio solo risolvere questo problema e andare avanti. Mi piacerebbe davvero capire il token di autenticità. Bene, la mia domanda è: hai qualche fonte completa di informazioni su questo argomento o passeresti il ​​tempo per spiegare in dettaglio qui?


7
Vedi anche: "Perché Google Prepend mentre (1) alla sua risposta JSON?" stackoverflow.com/questions/2669690/…
Chloe,

Risposte:


1463

Che succede

Quando l'utente visualizza un modulo per creare, aggiornare o distruggere una risorsa, l'app Rails crea un casuale authenticity_token, memorizza questo token nella sessione e lo posiziona in un campo nascosto nel modulo. Quando l'utente invia il modulo, Rails cerca il authenticity_token, lo confronta con quello memorizzato nella sessione e se corrispondono alla richiesta è consentito continuare.

Perché succede

Poiché il token di autenticità è archiviato nella sessione, il client non può conoscerne il valore. Ciò impedisce alle persone di inviare moduli a un'app Rails senza visualizzare il modulo all'interno dell'app stessa. Immagina di utilizzare il servizio A, di aver effettuato l'accesso al servizio e che tutto sia a posto. Ora immagina di essere andato a utilizzare il servizio B, di aver visto un'immagine che ti piace e di aver premuto sull'immagine per visualizzarne una dimensione maggiore. Ora, se al servizio B fosse presente un codice maligno, potrebbe inviare una richiesta al servizio A (a cui si è effettuato l'accesso) e chiedere di eliminare il proprio account inviando una richiesta a http://serviceA.com/close_account. Questo è ciò che è noto come CSRF (Cross Site Request Forgery) .

Se il servizio A utilizza token di autenticità, questo vettore di attacco non è più applicabile, poiché la richiesta dal servizio B non conterrebbe il token di autenticità corretto e non potrà continuare.

I documenti API descrivono i dettagli sul meta tag:

La protezione CSRF è attivata con il protect_from_forgerymetodo, che controlla il token e reimposta la sessione se non corrisponde a quanto previsto. Per impostazione predefinita, viene generata una chiamata a questo metodo per le nuove applicazioni Rails. Il parametro token è denominato authenticity_tokenper impostazione predefinita. Il nome e il valore di questo token devono essere aggiunti a ogni layout che esegue il rendering dei moduli includendoli csrf_meta_tagsnell'intestazione HTML.

Appunti

Tieni presente che Rails verifica solo metodi non idempotenti (POST, PUT / PATCH e DELETE). Le richieste GET non vengono verificate per il token di autenticità. Perché? perché la specifica HTTP afferma che le richieste GET sono idempotenti e non devono creare, alterare o distruggere risorse sul server e la richiesta deve essere idempotente (se si esegue lo stesso comando più volte, si dovrebbe ottenere lo stesso risultato ogni volta).

Anche la vera implementazione è un po 'più complicata, come definita all'inizio, garantendo una migliore sicurezza. Rails non emette lo stesso token archiviato con ogni modulo. Né genera e memorizza un token diverso ogni volta. Genera e memorizza un hash crittografico in una sessione ed emette nuovi token crittografici, che possono essere abbinati a quelli memorizzati, ogni volta che viene eseguito il rendering di una pagina. Vedi request_forgery_protection.rb .

Lezioni

Utilizzare authenticity_tokenper proteggere i metodi non idempotenti (POST, PUT / PATCH e DELETE). Assicurarsi inoltre di non consentire richieste GET che potrebbero potenzialmente modificare le risorse sul server.


EDIT: controlla il commento di @erturne in merito alle richieste GET che sono idempotenti. Lo spiega in un modo migliore di quello che ho fatto qui.


25
@Faisal, è quindi possibile, per un utente malintenzionato, semplicemente leggere / acquisire l'elemento "nascosto" del modulo per il servizio A e ottenere quel token univoco generato per l'utente, dato che hanno ottenuto l'accesso alla sessione avviata dall'utente per il servizio A?
marcamillion,

11
@marcamillion: se qualcuno ha dirottato la sessione al servizio A, il token di autenticità non ti proteggerà. Il dirottatore sarà in grado di inviare una richiesta e potrà procedere.
Faisal,

12
@zabba: Rails genera un'eccezione ActionController :: InvalidAuthenticityToken se un modulo viene inviato senza il token appropriato. Puoi salvare_da l'eccezione e fare qualunque elaborazione tu voglia.
Faisal

5
re "Assicurati anche di non effettuare richieste GET che potrebbero potenzialmente modificare le risorse sul server." - questo include il non utilizzo di match () in rotte che potrebbero potenzialmente consentire richieste GET a controllori di azioni destinate a ricevere solo POST
Steven Soroka

102
"... e la richiesta dovrebbe essere idempotente (se si esegue lo stesso comando più volte, si dovrebbe ottenere lo stesso risultato ogni volta)." Solo un piccolo chiarimento qui. Sicuro significa senza effetti collaterali. Idempotente significa lo stesso effetto collaterale, indipendentemente da quante volte viene chiamato un servizio. Tutti i servizi sicuri sono intrinsecamente idempotenti perché non ci sono effetti collaterali. Chiamare GET su una risorsa corrente più volte restituirebbe ogni volta un risultato diverso, ma è sicuro (e quindi idempotente).
erturne,

137

Il token di autenticità è progettato in modo tale da sapere che il modulo viene inviato dal tuo sito Web. Viene generato dalla macchina su cui gira con un identificatore univoco che solo la tua macchina può conoscere, contribuendo così a prevenire attacchi di contraffazione di richieste tra siti.

Se hai semplicemente difficoltà a negare l'accesso al tuo script AJAX alle rotaie, puoi usarlo

<%= form_authenticity_token %>

per generare il token corretto durante la creazione del modulo.

Puoi leggere di più a riguardo nella documentazione .


88

Che cos'è CSRF?

Il token di autenticità è una contromisura alla falsificazione di richieste tra siti (CSRF). Cos'è CSRF, chiedi?

È un modo in cui un attaccante può potenzialmente dirottare sessioni senza nemmeno conoscere i token di sessione.

Scenario :

  • Visita il sito della tua banca, accedi.
  • Quindi visitare il sito dell'aggressore (ad es. Annuncio sponsorizzato da un'organizzazione non attendibile).
  • La pagina dell'attaccante include un modulo con gli stessi campi del modulo "Trasferisci fondi" della banca.
  • Attacker conosce le informazioni del tuo account e dispone di campi modulo pre-compilati per trasferire denaro dal tuo account al conto dell'aggressore.
  • La pagina dell'attaccante include Javascript che invia il modulo alla tua banca.
  • Quando viene inviato il modulo, il browser include i cookie per il sito della banca, incluso il token di sessione.
  • La banca trasferisce denaro sul conto dell'attaccante.
  • Il modulo può trovarsi in un iframe che è invisibile, quindi non si sa mai l'attacco.
  • Questo si chiama Cross-Site Request Forgery (CSRF).

Soluzione CSRF :

  • Il server può contrassegnare i moduli che provengono dal server stesso
  • Ogni modulo deve contenere un token di autenticazione aggiuntivo come campo nascosto.
  • Il token deve essere imprevedibile (l'attaccante non può indovinarlo).
  • Il server fornisce un token valido nei moduli nelle sue pagine.
  • Il server controlla il token quando il modulo viene pubblicato, rifiuta i moduli senza token corretto.
  • Token di esempio: identificatore di sessione crittografato con chiave segreta del server.
  • Rails genera automaticamente tali token: vedere il campo di input authenticity_token in ogni forma.

1
Ecco una versione di questa stessa spiegazione che è meno precisa ma anche meno astratta: stackoverflow.com/a/33829607/2810305
Lutz Prechelt

Non sono sicuro, ma i browser moderni consentono l'invio di richieste non idempotenti (POST / PUT / DELETE) a un altro dominio? Immagino che ci debba essere protezione contro tali cose nel browser stesso
divideByZero il

45

Esempio di attacco minimo che sarebbe possibile evitare: CSRF

Sul mio sito web evil.comti consiglio di inviare il seguente modulo:

<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>

Se accedi alla tua banca attraverso i cookie di sessione, i cookie verrebbero inviati e il trasferimento verrebbe effettuato senza che tu lo sapessi.

Cioè dove entra in gioco il token CSRF:

  • con la risposta GET che ha restituito il modulo, Rails invia un parametro nascosto casuale molto lungo
  • quando il browser effettua la richiesta POST, invierà il parametro insieme e il server lo accetterà solo se corrisponde

Quindi il modulo su un browser autentico sarebbe simile a:

<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>

Pertanto, il mio attacco fallirebbe, dal momento che non stava inviando il authenticity_tokenparametro, e non avrei potuto indovinarlo dato che è un numero casuale enorme.

Questa tecnica di prevenzione si chiama Synchronizer Token Pattern .

Stessa politica di origine

E se l'attaccante avesse fatto due richieste con JavaScript, una per leggere il token e la seconda per effettuare il trasferimento?

Il modello di token del sincronizzatore da solo non è sufficiente per impedirlo!

È qui che la stessa politica di origine viene in soccorso, come ho spiegato in: /security/8264/why-is-the-same-origin-policy-so-important/72569# 72.569

Come Rails invia i token

Coperto in: Rails: come funziona csrf_meta_tag?

Fondamentalmente:

  • Gli helper HTML come form_tagaggiungere un campo nascosto al modulo per te se non è un modulo GET

  • AJAX viene gestito automaticamente da jquery-ujs , che legge il token dagli metaelementi aggiunti all'intestazione csrf_meta_tags(presente nel modello predefinito) e lo aggiunge a qualsiasi richiesta effettuata.

    uJS tenta anche di aggiornare il token nei moduli in frammenti memorizzati nella cache obsoleti.

Altri approcci di prevenzione


Grazie, ma il tuo punto di affidarsi alla stessa politica di origine per non riuscire a leggere prima il token CSRF sembra difettoso. Quindi prima dici che puoi POSTARE a un'origine diversa ma non puoi leggere da essa, sembra strano ma credo sia corretto, ma potresti iniettare un'immagine o un tag di script con un get alla pagina e collegare un gestore per analizzare la risposta e capito sì?
bjm88,

@ bjm88 iniettare lo script dove? Sul tuo sito o sul sito attaccato? Se il sito viene attaccato, consentire l'iniezione di script è un noto difetto di sicurezza e attiva in modo efficace il sito Web. Ogni sito web deve combatterlo attraverso la sanificazione degli input. Per le immagini, non vedo come possano essere usate per un attacco. Su un sito di attacco: puoi modificare il tuo browser per consentire la lettura, e quindi autoimpegnarti a piacimento :-) ma i browser decenti lo impediscono di default, provalo.
Ciro Santilli 28 冠状 病 六四 事件 法轮功

43

Il token di autenticità viene utilizzato per prevenire attacchi di falsificazione delle richieste tra siti (CSRF). Per comprendere il token di autenticità, è necessario innanzitutto comprendere gli attacchi CSRF.

CSRF

Supponi di essere l'autore di bank.com. Sul tuo sito è presente un modulo utilizzato per trasferire denaro su un altro account con una richiesta GET:

inserisci qui la descrizione dell'immagine

Un hacker potrebbe semplicemente inviare una richiesta HTTP al server dicendo GET /transfer?amount=$1000000&account-to=999999, giusto?

inserisci qui la descrizione dell'immagine

Sbagliato. L'attacco degli hacker non funzionerà. Il server fondamentalmente penserà?

Eh? Chi è questo ragazzo che cerca di avviare un trasferimento. Non è il proprietario dell'account, questo è certo.

Come fa il server a saperlo? Perché non ci sono session_idcookie per autenticare il richiedente.

Quando accedi con il tuo nome utente e password, il server imposta un session_idcookie sul tuo browser. In questo modo, non è necessario autenticare ogni richiesta con nome utente e password. Quando il browser invia il session_idcookie, il server sa:

Oh, quello è John Doe. Ha effettuato l'accesso con successo 2,5 minuti fa. È bravo ad andare.

Un hacker potrebbe pensare:

Hmm. Una normale richiesta HTTP non funzionerà, ma se potessi mettere la mano su quel session_idcookie, sarei dorato.

Il browser degli utenti ha un gruppo di cookie impostati per il bank.comdominio. Ogni volta che l'utente bank.cominvia una richiesta al dominio, vengono inviati tutti i cookie. Incluso il session_idcookie.

Quindi, se un hacker potrebbe ottenere voi di fare la richiesta GET che i trasferimenti di denaro sul suo conto, che sarebbe successo. Come potrebbe indurti a farlo? Con falsificazione richiesta su più siti.

È abbastanza semplicemente, in realtà. L'hacker potrebbe farti visitare il suo sito Web. Sul suo sito Web, potrebbe avere il seguente tag immagine:

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

Quando il browser degli utenti incontra quel tag immagine, farà una richiesta GET a quell'URL. E poiché la richiesta proviene dal suo browser, invierà con essa tutti i cookie associati bank.com. Se l'utente ha recentemente effettuato l'accesso a bank.com... il session_idcookie verrà impostato e il server penserà che l'utente intendesse trasferire $ 1.000.000 all'account 999999!

inserisci qui la descrizione dell'immagine

Bene, non visitare siti pericolosi e andrà tutto bene.

Questo non è abbastanza. E se qualcuno pubblica quell'immagine su Facebook e appare sulla tua bacheca? Cosa succede se viene iniettato in un sito che stai visitando con un attacco XSS?

Non è così male. Solo le richieste GET sono vulnerabili.

Non vero. Un modulo che invia una richiesta POST può essere generato dinamicamente. Ecco l'esempio della Guida di Rails sulla sicurezza :

<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 di autenticità

Quando hai ApplicationControllerquesto:

protect_from_forgery with: :exception

Questo:

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

È compilato in questo:

<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>

In particolare, viene generato:

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

Per proteggersi dagli attacchi CSRF, se Rails non vede il token di autenticità inviato insieme a una richiesta, non la considererà sicura.

In che modo un utente malintenzionato dovrebbe sapere cos'è questo token? Un valore diverso viene generato casualmente ogni volta che viene generato il modulo:

inserisci qui la descrizione dell'immagine

Un attacco Cross Site Scripting (XSS): ecco come. Ma questa è una vulnerabilità diversa per un giorno diverso.



34

poiché Authenticity Tokenè così importante e in Rails 3.0+ puoi usare

 <%= token_tag nil %>

creare

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

dovunque


Questo mi è stato utile. In realtà stavo cercando di fare XSSsulla pagina di accesso, non per scopi nefasti, ma per creare una nuova sessione con un nome utente precompilato. Ora so che posso solo usare value="token_value".
Michael - Dov'è Clay Shirky il

27

Attenzione al meccanismo di token di autenticità può comportare condizioni di competizione se si hanno più richieste simultanee dallo stesso client. In questa situazione, il tuo server può generare più token di autenticità quando ce ne dovrebbe essere uno solo e il client che riceve il token precedente in un modulo fallirà alla sua richiesta successiva perché il token del cookie di sessione è stato sovrascritto. C'è un commento su questo problema e una soluzione non del tutto banale qui: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/


11

Metodi Dove authenticity_tokenè richiesto

authenticity_token è richiesto in caso di metodi idempotenti come post, put ed eliminazione, poiché i metodi idempotent influiscono sui dati.

Perché è richiesto

È necessario per prevenire azioni malvagie. authenticity_token viene archiviato in sessione, ogni volta che viene creato un modulo su pagine Web per la creazione o l'aggiornamento delle risorse, un token di autenticità viene archiviato in un campo nascosto e inviato con un modulo sul server. Prima di eseguire l'azione, l'utente inviato authenticity_token viene verificato e authenticity_tokenarchiviato nella sessione. Se authenticity_tokenè lo stesso, il processo è continua, altrimenti non esegue azioni.


3
In realtà, non è il contrario? GET è idempotente poiché la sua chiamata non dovrebbe alterare lo stato del sistema, dove i verbi PUT POST e DELETE NON sono verbi idempotenti poiché alterano lo stato del sistema. IE: authenticity_token è richiesto in caso di metodi NON idempotenti.
Jean-Théo,

2
@ Jean-Daube, uma: idempotente significa che se fatto due volte, l'azione avviene una sola volta. GET, PUT e DELETE sono idempotenti: w3.org/Protocols/rfc2616/rfc2616-sec9.html La proprietà chiave qui non è idempotenza, ma se il metodo cambia o meno i dati, che si chiama "metodo sicuro" o no.
Ciro Santilli 12 冠状 病 六四 事件 法轮功

6

Che cos'è un token di autenticazione?

Questa è una stringa casuale utilizzata dall'applicazione rails per assicurarsi che l'utente stia richiedendo o eseguendo un'azione dalla pagina dell'app, non da un'altra app o sito.

Perché è necessario un token di autenticazione?

Per proteggere la tua app o il tuo sito da falsificazioni di richieste tra siti.

Come aggiungere un token di autenticazione a un modulo?

Se stai generando un modulo utilizzando form_for il tag viene automaticamente aggiunto un token di autenticazione, altrimenti puoi usarlo <%= csrf_meta_tag %>.

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.