Proteggi un'applicazione CodeIgniter contro CSRF

Nel tutorial di oggi, impareremo come proteggere in modo indolore l'applicazione CodeIgniter (pre 2.0) contro gli attacchi di contraffazione di richieste tra siti. La libreria che creeremo oggi automatizzerà tutti i meccanismi di protezione, rendendo il tuo sito più forte e più sicuro.


Passo 1 - Capire il vettore di attacco

Richiesta cross-site Gli attacchi di contraffazione sono basati su moduli non protetti sui tuoi siti.

Un utente malintenzionato potrebbe creare un modulo falso sul suo sito, ad esempio un modulo di ricerca. Questo modulo potrebbe contenere input nascosti contenenti dati dannosi. Ora il modulo non viene effettivamente inviato al sito dell'utente malintenzionato per eseguire la ricerca; in realtà, la forma punta a il tuo posto! Dal momento che il tuo sito Web si fida che il modulo è autentico, passa attraverso e esegue le azioni richieste (e forse anche quelle dannose).

Immagina che un utente abbia effettuato l'accesso al tuo sito e venga reindirizzato al sito dell'utente malintenzionato per qualche motivo (phishing, XSS, tu lo chiami). Il modulo del malintenzionato potrebbe indicare il modulo di eliminazione dell'account sul tuo sito. Se l'utente esegue una "ricerca" sul sito degli attaccanti, il suo account verrà eliminato senza che lui lo sappia!

Esistono numerosi modi per prevenire questo tipo di attacchi.

  • Controlla l'intestazione HTTP Referer e verifica se appartiene al tuo sito. Il problema con questo metodo è che non tutti i browser inviano questa intestazione (personalmente ho avuto questo problema una volta con IE7); potrebbe essere forgiato comunque.
  • Un altro metodo (quello che useremo), è quello di includere una stringa casuale (un "token") su ciascun modulo e memorizzare quel token nella sessione dell'utente. Su ciascun INVIARE richiesta, confronta il token inviato con quello in negozio e, se sono diversi, rifiuta la richiesta. Il tuo sito deve comunque essere protetto contro XSS, perché se non lo è, questo metodo diventa inutile.

Passaggio 2: pianificazione

Dovremo fare tre cose per ogni richiesta:

  • Se la richiesta è a INVIARE richiesta, convalidare il token inviato.
  • Genera un token nel caso in cui non ce ne sia uno.
  • Inietti il ​​token in tutte le forme. Ciò rende il metodo semplice e indolore, dal momento che non sono necessarie modifiche alle tue opinioni.

Per farlo automaticamente, utilizzeremo gli hook CodeIgniter. Gli hook ci permettono di eseguire tutte le azioni su diverse parti della richiesta. Ne avremo bisogno tre:

  • post_controller_constructor - Per controllare il token inviato, avremo bisogno di un post_controller_constructor gancio. Agganciare questa azione prima di questo evento non ci consente di accedere correttamente all'istanza di CodeIgniter.
  • Genera il token - Per generare il token, utilizzeremo lo stesso hook di prima. Questo ci permette di avere accesso ad esso nel caso in cui avessimo bisogno di stamparlo manualmente nelle nostre visualizzazioni.
  • display_override - Per iniettare automaticamente il token nelle nostre visualizzazioni, dovremo utilizzare il display_override gancio. Questo è un aggancio difficile, dato che, come suggerisce il nome, sovrascrive la visualizzazione delle viste. Abbiamo bisogno di generare i contenuti da soli se usiamo questo hook (controlla la documentazione di CodeIgniter per maggiori informazioni).

Passaggio 3: generazione di token

Iniziamo. Andremo passo dopo passo per spiegare tutto nel modo più completo possibile. Creeremo il metodo che genera prima il token, quindi possiamo testare tutto correttamente in seguito. Crea un file nel tuo sistema / applicazione / ganci cartella chiamata "csrf.php"e incollare il seguente codice:

CI = & get_instance (); 

Speriamo che ciò che abbiamo aggiunto sopra dovrebbe sembrare piuttosto di base per te. Stiamo creando una classe, chiamata csrf_protection, una variabile di istanza per contenere l'istanza CodeIgniter, due variabili statiche per contenere il nome del parametro che memorizzerà il token e una per memorizzare il token stesso per un facile accesso all'intera classe. All'interno del costruttore della classe (__costruire), recuperiamo semplicemente l'istanza CodeIgniter e la memorizziamo nella nostra variabile di istanza corrispondente.

Nota: Il "nome del parametro" è il nome del campo che contiene il token. Quindi sulle forme, è il nome dell'input nascosto.

Dopo il costruttore della classe, incolla il seguente codice:

/ ** * Genera un token CSRF e lo memorizza durante la sessione. Viene generato un solo token per sessione. * Questo deve essere legato a un hook post-controller e prima del hook * che chiama il metodo inject_tokens (). * * @return void * @author Ian Murray * / public function generare_token () // Carica la libreria di sessione se non caricata $ this-> CI-> load-> library ('session'); if ($ this-> CI-> session-> userdata (self :: $ token_name) === FALSE) // Genera un token e lo memorizza nella sessione, dal momento che quello vecchio sembra essere scaduto. self :: $ token = md5 (uniqid (). microtime (). rand ()); $ this-> CI-> session-> set_userdata (self :: $ token_name, self :: $ token);  else // Impostalo sulla variabile locale per auto di facile accesso :: $ token = $ this-> CI-> session-> userdata (self :: $ token_name); 

Passo dopo passo:

  • Carichiamo la libreria di sessione, nel caso non venga caricata automaticamente. Abbiamo bisogno di questo per memorizzare il token.
  • Determiniamo se il token è già stato generato. Se la dati utente metodo restituisce FALSE, quindi il token non è ancora presente.
  • Generiamo un token e lo memorizziamo nella variabile di classe per un facile accesso. Il token potrebbe essere praticamente qualsiasi cosa. Ho usato queste tre funzioni per assicurarmi che sia così molto casuale.
  • Quindi lo memorizziamo nella variabile di sessione con il nome precedentemente configurato.
  • Se il token era già presente, noi non generarlo e invece memorizzarlo nella variabile di classe per un facile accesso.

Passaggio 4: convalida del token

Dobbiamo assicurarci che il token sia stato inviato ed è valido nel caso in cui la richiesta sia a INVIARE richiesta. Vai avanti e incolla il seguente codice nel tuo csrf.php file:

/ ** * Convalida un token inviato quando viene effettuata la richiesta POST. * * @return void * @author Ian Murray * / public function validate_tokens () // Questa è una richiesta? if ($ _SERVER ['REQUEST_METHOD'] == 'POST') // Il campo token è impostato e valido? $ posted_token = $ this-> CI-> input-> post (self :: $ token_name); if ($ posted_token === FALSE || $ posted_token! = $ this-> CI-> session-> userdata (self :: $ token_name)) // Richiesta non valida, invio errore 400. show_error ('Richiesta non valida. I token non corrispondevano. ', 400); 
  • In alto, convalidiamo la richiesta, ma solo se è un INVIARE richiesta, il che significa che un modulo è stato, di fatto, presentato. Controlliamo questo dando un'occhiata al REQUEST_METHOD all'interno del $ _SERVER super globale.
  • Controlla se il token è stato effettivamente pubblicato. Se l'output di $ This-> CI-> input-> Post (self :: $ nome_token) è FALSE, quindi il token non è mai stato pubblicato.
  • Se il token non è stato pubblicato o se non è uguale a quello che abbiamo generato, quindi negare la richiesta con un errore "Bad Request".

Passaggio 5: iniettare token nelle viste

Questa è la parte divertente! Abbiamo bisogno di iniettare i token in tutte le forme. Per semplificarci la vita, inseriremo due meta tag all'interno del nostro (Rails-like). In questo modo, possiamo includere anche il token nelle richieste AJAX.

Aggiungi il seguente codice al tuo csrf.php file:

/ ** * Inietta i tag nascosti su tutte le forme POST con il token csrf. * Inietta anche meta intestazioni in  di output (se esiste) per un facile accesso * da framework JS. * * @return void * @author Ian Murray * / public function inject_tokens () $ output = $ this-> CI-> output-> get_output (); // Iniettare in forma $ output = preg_replace ('/ (<(form|FORM)[^>] * (metodo | METHOD) = "(post | POST)" [^>] *>) / ',' $ 0', $ output); // Iniettare in  $ output = preg_replace ('/ (<\/head>) / ',''. "\ n". ''. "\ n". '$ 0', $ output); $ This-> CI-> uscita -> _ display ($ output); 
  • Poiché questo è un display_override hook, dobbiamo recuperare l'output generato da CodeIgniter. Lo facciamo usando il $ This-> CI-> output-> get_output () metodo.
  • Per iniettare i tag nei nostri moduli, useremo le espressioni regolari. L'espressione garantisce di iniettare un tag di input nascosto (che contiene il nostro token generato) solo in moduli con un metodo di tipo INVIARE.
  • Dobbiamo anche inserire i nostri meta tag nel intestazione (se presente). Questo è semplice, dal momento che il tag head di chiusura dovrebbe essere presente solo una volta per file.
  • Infine, dal momento che stiamo usando il display_override hook, il metodo predefinito per visualizzare la vista non verrà chiamato. Questo metodo include tutti i tipi di cose, che non dovremmo - solo allo scopo di iniettare del codice. Chiamarlo noi stessi risolve questo.

Passo 6 - Ganci

Ultimo, ma non meno importante, abbiamo bisogno di creare gli stessi hook - così vengono chiamati i nostri metodi. Incolla il seguente codice nel tuo system / application / config / hooks.php file:

// // Hook di protezione CSRF, non toccarli a meno che tu non sappia cosa stai facendo //. // // L'ORDINE DI QUESTI GANCI È ESTREMAMENTE IMPORTANTE !! // // QUESTO DEVE ANDARE PRIMA NELLA LISTA DELL'ELENCO post_controller_constructor. $ hook ['post_controller_constructor'] [] = array (// Ricorda "[]", questo non è l'unico post_controller_constructor hook 'class' => 'CSRF_Protection', 'function' => 'validate_tokens', 'filename' = > 'csrf.php', 'filepath' => 'ganci'); // Genera il token (DEVE ACCADERE DOPO CHE LA VALIDAZIONE È STATA EFFETTUATA, MA PRIMA CHE IL CONTROLLER // È ESEGUITO, ALTRIMENTI L'UTENTE NON HA ACCESSO A UN GESTORE VALIDO PER LE FORME PERSONALIZZATE). $ hook ['post_controller_constructor'] [] = array (// Mind the "[]", questo non è l'unico post_controller_constructor hook 'class' => 'CSRF_Protection', 'function' => 'generate_token', 'filename' = > 'csrf.php', 'filepath' => 'ganci'); // Inietta token su tutte le forme $ hook ['display_override'] = array ('class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filename' => 'csrf.php', 'filepath' => 'ami');
  • Il primo hook chiama il validate_tokens metodo. Questo non è l'unico post_controller_constructor gancio, quindi abbiamo bisogno di aggiungere quelle parentesi ("[]") Per ulteriori informazioni, consultare la documentazione sui ganci CodeIgniter.
  • Il secondo gancio, che è anche un post_controller_constructor, genera il token nel caso in cui non sia stato ancora generato.
  • Il terzo è l'override del display. Questo gancio inserirà i nostri token nel modulo e il intestazione.

Avvolgendo

Con il minimo sforzo, abbiamo creato una libreria piuttosto bella per noi stessi.

Puoi utilizzare questa libreria in qualsiasi progetto e proteggerà automaticamente il tuo sito contro CSRF.

Se desideri contribuire a questo piccolo progetto, ti preghiamo di lasciare un commento qui sotto, oppure inserire il progetto su GitHub. In alternativa, a partire da CodeIgniter v2.0, la protezione dagli attacchi CSRF è ora integrata nel framework!