2012-05-05 18 views
32

Quindi sto leggendo in giro ed ero davvero confuso dall'avere un token CSRF, mentre dovrei generare un nuovo token per ogni richiesta, o solo all'ora o qualcosa del genere?Nuovo token CSRF per richiesta o NOT?

$data['token'] = md5(uniqid(rand(), true)); 
$_SESSION['token'] = $data['token']; 

Ma diciamo che è meglio per generare un token ogni ora, quindi avrei bisogno di due sessioni: motivo, la scadenza,

E come faccio a passare al modulo? Basta mettere echo $ _SESSION ['token'] sul modulo del valore nascosto e quindi confrontare su submit?

+2

Attenzione che la generazione di un nuovo token per richiesta può causare problemi se un utente ha l'applicazione aperta in due finestre di browser diverse contemporaneamente. I token non saranno sincronizzati. –

+0

@ Michael Questo problema potrebbe essere risolto con la tecnologia push, html 5 standard di comunicazione. –

+1

Se non è di alta sicurezza, basta prendere un hash, ad es. dell'ip utente e del suo hash della password. Questo potrebbe non cambiare mai o solo raramente, ma serve comunque al suo scopo poiché un utente malintenzionato non conoscerà la password dell'utente (e se lo fa, non ha bisogno di eseguire un attacco CSRF su di lui) – ThiefMaster

risposta

34

Se lo fai per ogni richiesta modulo - quindi si rimuove sostanzialmente la possibilità per gli attacchi CSRF a verificarsi & è possibile risolvere un altro problema comune: forma multipla presentazione

In termini semplici - la vostra applicazione accetterà solo modulo di input se l'utente ha richiesto il modulo prima dell'invio.

scenario normale: utente A va al tuo sito web, e chiede Forma A, viene data forma A più un codice univoco per il modulo A. Quando l'utente invia Forma A, lui/lei deve includere il codice unico che era solo per modulo A.

attacco CSRF scenario: utente a va al tuo sito web, e chiede modulo A. Nel frattempo si visita un altro sito "cattivo", che tenta un attacco CSRF su di loro, inducendole a invia per un falso Modulo B.

Ma il tuo sito web sa che l'Utente A non ha mai richiesto il Modulo B - e quindi anche Se hanno il codice univoco per il modulo A, il modulo B verrà rifiutato perché non hanno un codice univoco di tipo B, solo un codice di forma A. Il tuo utente è al sicuro e puoi dormire sonni tranquilli durante la notte.

Ma se lo fai come token generico, della durata di un'ora (come hai postato sopra), allora l'attacco sopra potrebbe funzionare, nel qual caso non hai ottenuto molto con la tua protezione CSRF. Questo perché l'applicazione non sa che il modulo B non è mai stato richiesto in primo luogo. È un token generico. Il PUNTO TUTTO della prevenzione CSRF è quello di rendere ogni modulo token univoco a quella forma

Edit: perché lei ha chiesto per ulteriori informazioni: 1 - non dovete farlo per ogni modulo di richiesta, si può fare per ora/sessione ecc. Il punto è un valore segreto che viene fornito all'utente e rinominato sul reso.Questo valore non è conosciuto da un altro sito Web e pertanto non può presentare una forma falsa.

Così si sia generare il token per ogni richiesta, o per ogni sessione:

// Before rendering the page: 
$data['my_token'] = md5(uniqid(rand(), true)); 
$_SESSION['my_token'] = $data['my_token']; 

// During page rendering: 
<input type="hidden" name="my_token" id="my_token" value="<? php echo $_SESSION['my_token']?>" /> 

// After they click submit, when checking form: 
if ($_POST['my_token'] === $_SESSION['my_token']) 
{ 
     // was ok 
} 
else 
{ 
      // was bad!!! 
} 

e perché è "per forma" - che si voleva ottenere doppie invio di moduli - perché si può pulire il token dopo il primo modulo sottomissione!

+13

"quindi l'attacco sopra potrebbe funzionare" --- come? Elaborare per favore (questa ** risposta errata ** non dovrebbe essere upvoted, perché il rispondente non ha idea di cosa sia usato csrf-token) – zerkms

+1

perché se si hanno token CSRF generici, finché l'hacker ha l'utente A invia il modulo B, ci riuscirà, perché l'applicazione non sa che il modulo B non è mai stato richiesto in primo luogo. È un token generico. L'INTERO PUNTO della prevenzione CSRF è rendere ciascun token modulo unico per quella forma. – Laurence

+13

in che modo l'utente malintenzionato conosce il valore del token csrf? "L'INTERO PUNTO della prevenzione CSRF è di rendere ciascun token di forma unico per quella forma" --- no, ti sbagli. – zerkms

1

La risposta alla tua domanda è: dipende.

E non è necessario utilizzare la sessione per i token a tempo, è sufficiente utilizzare il server-time e una chiave segreta sul server.

Ma diciamo che è meglio per generare un token ogni ora, quindi avrei bisogno di due sessioni: a gettone, di scadenza,

No, avete bisogno di una routine che è in grado di generare un token per un lasso di tempo. Diciamo che dividi il tempo ogni 30 minuti. Si crea un token per gli attuali 30 minuti nel modulo.

Quando viene inviato il modulo e si verifica il token contro per ora e contro il periodo precedente di 30 minuti. Pertanto un token è valido per 30 minuti fino a un'ora.

$token = function($tick = 0) use($secret, $hash) { 
    $segment = ((int) ($_SERVER['REQUEST_TIME']/1800)) + $tick; 
    return $hash($secret . $segment); 
}; 
+0

Bene dipende da cosa? – John

+0

@John In base alle tue esigenze, naturalmente, se il metodo basato su '$ _SESSION' lavora per te, fallo. L'ho usato per una protezione hotlink per i download una volta e ha funzionato bene. – hakre

+0

@john: ho aggiunto una funzione esemplare su come generare token validi per un determinato periodo di tempo senza utilizzare le sessioni. – hakre

12

In genere, è sufficiente disporre di un singolo token per utente o per sessione. È importante che il token sia associato a un solo utente/sessione e non utilizzato globalmente.

Se si ha paura che il token possa essere trapelato o ottenuto da un sito attaccante (ad esempio tramite XSS), è possibile limitare la validità del token solo a un determinato intervallo di tempo, a un determinato modulo/URL o a certa quantità di usi.

Ma limitare la validità ha lo svantaggio che potrebbe causare falsi positivi e quindi restrizioni nell'usabilità, e. g. una richiesta legittima potrebbe utilizzare un token che è stato appena invalidato poiché la richiesta di token è troppo remota o il token è già stato utilizzato troppo spesso.

Quindi la mia raccomandazione è di usare solo un token per utente/sessione. E se vuoi maggiore sicurezza, usa un token per modulo/URL per utente/sessione in modo che se un token per un modulo/URL viene trapelato gli altri sono ancora al sicuro.

+0

Si prega di definire "per utente". Cosa significa? Cosa si qualifica come utente? Ti riferisci al monitoraggio degli utenti basato su IP? – hakre

+1

@hakre Normalmente si utilizzerà una sessione per associare il token alla sessione. Ma ci sono account utente, puoi anche associare direttamente il token a un utente. Questo è ciò che intendevo per "utente". – Gumbo

+0

E la sessione? Identificato dai cookie? Non lascerebbe un buco in quel contesto? Intendo dire che finché l'identificatore di sessione può essere passato all'interno della richiesta, questo non funzionerebbe, giusto? Anche se le informazioni fornite sono utili per la guida, cerco solo di girarci intorno. – hakre

0

È possibile utilizzare entrambi i metodi.

1) Un token casuale per ogni richiesta. E per risolvere il problema dei token non sincronizzati, è possibile utilizzare gli eventi inviati dal server http://www.w3.org/TR/eventsource/ o la tecnologia di comunicazione Websockets http://www.w3.org/TR/websockets/ per aggiornare i token in tempo reale per ogni pagina.

2) È possibile utilizzare un token per sessione utente (non è necessario farlo all'ora) che è più facile da implementare. Non avrai problemi con la sincronizzazione, ma se il token non è troppo forte può essere indovinato. Ofcorse se il token è molto casuale sarà molto difficile per la persona in stato di male fare una falsificazione di richiesta.

P.S. il primo metodo è il più sicuro ma è più difficile da implementare e utilizza più risorse.

Problemi correlati