2010-03-06 11 views
66

Cross Site Request Forgery (CSRF) è in genere prevenire con uno dei seguenti metodi:Come prevenire CSRF in un'applicazione RESTful?

  • check referer - RESTful ma inaffidabile gettone
  • inserto in forma e memorizzare il token nella sessione di server - non proprio RESTful
  • criptici URI una sola volta - non RESTful per la stessa ragione come token
  • inviare password manualmente per questa richiesta (non la password memorizzata nella cache utilizzata con autenticazione HTTP) - RESTful ma non conveniente

La mia idea è quella di utilizzare un utente segreto, un form enigmatico ma statico e JavaScript per generare token.

<form method="POST" action="/someresource" id="7099879082361234103"> 
    <input type="hidden" name="token" value="generateToken(...)"> 
    ... 
</form> 
  1. GET /usersecret/john_doe inverosimile dal JavaScript dal utente autenticato.
  2. Risposta: OK 89070135420357234586534346 Questo segreto è concepzionalmente statico, ma può essere modificato ogni giorno/ora ... per migliorare la sicurezza. Questa è l'unica cosa confidenziale.
  3. Leggere l'ID modulo criptico (ma statico per tutti gli utenti!) Con JavaScript, elaborarlo insieme al segreto utente: generateToken(7099879082361234103, 89070135420357234586534346)
  4. Inviare il modulo insieme al token generato sul server.
  5. Poiché il server conosce il segreto dell'utente e l'id del modulo, è possibile eseguire la stessa funzione generateToken eseguita dal client prima dell'invio e confrontare entrambi i risultati. Solo quando entrambi i valori sono uguali l'azione sarà autorizzata.

C'è qualcosa di sbagliato in questo approccio, nonostante il fatto che non funzioni senza JavaScript?

addendum:

+0

Il tuo usersecret non è univoco per l'utente, un utente malintenzionato deve semplicemente ottenere quel numero e modificare i propri script per utilizzare il nuovo calcolo. Come stai autenticando gli utenti se non hai nessuno stato? – Mike

+0

Il segreto utente è univoco per utente e può essere recuperato solo dopo l'autenticazione (autenticazione di base o digest o autenticazione del certificato HTTP) – deamon

risposta

21

Ci sono molte risposte qui e problemi con alcuni di loro.

cose che non dovrebbe fare:

  1. Se avete bisogno di leggere il token di sessione da JavaScript, si sta facendo qualcosa di terribilmente sbagliato. Il tuo cookie identificatore di sessione dovrebbe SEMPRE avere impostato HTTPOnly su di esso in modo che non sia disponibile per gli script.

    Questa protezione fa sì che l'impatto di XSS sia notevolmente ridotto, poiché un utente malintenzionato non sarà più in grado di ottenere un token di sessione degli utenti registrati, che a tutti gli effetti equivale a credenziali nell'applicazione . Non vuoi che un errore dia le chiavi al regno.

  2. L'identificativo di sessione non deve essere scritto sul contenuto della pagina. Questo è per gli stessi motivi per cui hai impostato HTTPOnly. Ciò significa che il tuo token di sessione non può essere il tuo ID di sessione. Devono essere valori diversi.

cose che dovreste fare:

  1. Seguire OWASP's guidance:

  2. In particolare, se si tratta di un'applicazione REST è possibile require double-submission of CSRF tokens:

basta creare qualcosa di crittografia casuale, memorizzarlo in encode ASCII o Base64, a e aggiungilo come cookie e ai tuoi moduli quando il server restituisce la pagina.Sul lato server assicurarsi che il valore del cookie corrisponda al valore del modulo. Voilà, hai ucciso CSRF, evitato richieste extra per i tuoi utenti e non ti sei aperto a più vulnerabilità.

+0

XSS con un cookie di sessione è altrettanto vulnerabile di un XSS con un token che può essere letto da JavaScript. Se è ancora possibile creare una richiesta AJAX che trasferisce denaro dall'account utente al mio account, e il server lo accetterà volentieri. – ghayes

+2

@ghayes Non sono d'accordo. Il token di sessione è molto più sensibile del token CSRF. Con il tuo token di sessione posso accedere all'applicazione completamente, come te, dalla mia macchina. Con il token CSRF, posso potenzialmente avere un elenco di azioni sensibili pre-scripted che vengono eseguite nel tuo browser. Il secondo scenario è molto più difficile da eseguire, richiede la conoscenza dell'app, richiede una finestra di tempo più ampia per l'esecuzione e le azioni sono limitate a ciò che hai pianificato in anticipo. Il primo scenario richiede una riga di codice per qualsiasi sito Web e un'app Cookie Manager che l'utente malintenzionato può utilizzare sulla sua macchina. – Doug

+0

Vale la pena menzionare qui. La presenza di rigorosi controlli di "richiesta HTTP incrociata" sul server e di intestazioni di ritorno HTTP dall'API può limitare un gran numero di danni automatici che un utente malintenzionato potrebbe eseguire su un utente che ha effettuato l'accesso. – LessQuesar

6

Il modulo ID statico fornisce alcuna protezione; un attaccante può prenderlo da solo. Ricorda, l'utente malintenzionato non è vincolato all'utilizzo di JavaScript sul client; può recuperare l'ID del modulo statico sul lato server.

Non sono sicuro di comprendere appieno la difesa proposta; da dove viene il GET /usersecret/john_doe? Questa parte della pagina è JavaScript? È l'URL letterale proposto? Se è così, suppongo che username non sia un segreto, il che significa che evil.ru può recuperare i segreti degli utenti se un bug del browser o del plugin consente richieste GET tra domini. Perché non archiviare il segreto dell'utente in un cookie al momento dell'autenticazione piuttosto che consentire a chiunque sia in grado di eseguire GET tra domini per recuperarlo?

Leggerei "Robust Defenses for Cross-Site Forgery" molto attentamente prima di implementare il mio sistema di autenticazione che volevo essere resistente a CSRF. In effetti, riconsidererei l'implementazione del mio sistema di autenticazione.

+1

L'ID modulo è qualcosa come una chiave pubblica. Hai ragione, 'GET/usersecret/john_doe' fa parte del codice JavaScript. Lo stesso nome utente non è il segreto, ma l'ID recuperato con questa richiesta da un utente autenticato (!). Grazie per il collegamento. – deamon

9

Hai sicuramente bisogno di qualche stato sul server per autenticare/autorizzare. Tuttavia, non è necessario che la sessione http sia memorizzata in una cache distribuita (come memcached) o in un database.

Se si utilizzano i cookie per l'autenticazione, la soluzione più semplice consiste nel duplicare il valore del cookie. Prima di inviare il modulo, leggere l'id di sessione dal cookie, memorizzarlo in un campo nascosto e quindi inviarlo. Sul lato server, conferma che il valore nella richiesta è uguale all'ID di sessione (che hai ricevuto dal cookie). Lo script malevolo di un altro dominio non sarà in grado di leggere l'id della sessione dal cookie, impedendo così CSRF.

Questo schema utilizza un identificativo singolo attraverso la sessione.

Se si desidera maggiore protezione, generare un ID per sessione univoco per modulo.

Inoltre, NON generare token in JS. Chiunque può copiare il codice ed eseguirlo da un dominio diverso per attaccare il tuo sito.

+1

Le sessioni non sono necessarie per l'autenticazione, come dimostrato dall'autenticazione HTTP. Il codice JavaScript per generare il token non è segreto: solo il segreto dell'utente deve essere segreto. – deamon

+3

@Sri anche se sono d'accordo che una sessione è il modo migliore per gestire questo da una sicurezza e prestazioni rispettive. Questo non è RESTful perché richiede al server di tenere traccia dello stato per utente che può causare problemi di scalabilità. – rook

+1

È corretto dire che l'invio doppio dei cookie non funzionerebbe se la pagina è vulnerabile agli attacchi XSS? Perché in questo caso si potrebbe inviare un modulo direttamente dall'interno del dominio stesso e il valore verrà inviato sia tramite cookie sia tramite modulo. –

3

Esistono alcuni metodi nello CSRF Prevention Cheat Sheet che possono essere utilizzati dal servizio di assistenza. La mitigazione CSRF stateless più RESTful utilizza l'origine o HTTP referer per assicurarsi che le richieste provengano da un dominio di cui ti fidi.

+2

Questo è un consiglio pericoloso, mentre è difficile falsificare il referer HTTP non è impossibile, anche l'intestazione del referer non è garantita (e non inviare un'intestazione referer romperà la tua app). – RelaXNow

+3

@RelaXNow Dai un'occhiata a questo framework di exploit CSRF che ho scritto: https://github.com/TheRook/CSRF-Request-Builder. Permette di specificare intestazioni HTTP arbitrarie e corpo. Tuttavia, non è possibile modificare il riferimento http **, perché questo è vietato da Flash. Il foglio dei trucchi di prevenzione CSRF è molto buono, dovresti leggere il link nel mio post. – rook

+0

Punto giusto, nel contesto di CSRF un utente malintenzionato non sarà in grado (per quanto ne so) di spoofare l'intestazione di Referer della vittima, tuttavia l'intestazione non è ancora garantita e la sua richiesta per la tua API dovrebbe essere eseguita solo se può garantire che sarà sempre inviato (come per un'applicazione interna per un'azienda). – RelaXNow

7

mi appare questo diritto:

  • si desidera la protezione contro CSRF per gli utenti registrati tramite i cookie.
  • E allo stesso tempo si desidera l'interfaccia RESTful per le richieste autenticate Basic, OAuth e Digest dalle app.

Allora, perché non controllare se gli utenti vengono registrati in via biscotto e applicano CSRF solo allora?

Non sono sicuro ma è possibile che un altro sito crei elementi come l'autenticazione di base o le intestazioni?

Per quanto ne so, CSRF è tutto sui cookie? L'autenticazione RESTful non avviene con i cookie.

+0

Mi stavo chiedendo anche questo! Secondo questo articolo https: //mathieu.fenniak.net/is-your-web-api-suscettibile-a-a-csrf-exploit/Dovrebbe essere possibile attivare i controlli CSRF se qualcuno sta arrivando tramite un cookie/sessione e spegnerlo, se la richiesta sta arrivando una sorta di schema di autenticazione stateless di base .. ecc. – CMCDragonkai

+2

Fai attenzione all'autenticazione di base: equivale effettivamente a un utente che ha effettuato l'accesso tramite un cookie poiché i browser invieranno l'intestazione di autorizzazione fornita alle richieste successive come praticità dell'utente. –

0

C'è qualcosa di sbagliato in questo approccio, nonostante il fatto che sia non funziona senza JavaScript?

Il tuo segreto utente non è un segreto se lo invii al cliente. Di solito usiamo questi segreti per generare hash e inviarli con il modulo, e aspettarli per il confronto.

Se si desidera essere RESTful, la richiesta deve contenere tutte le informazioni su come elaborarlo. I modi si può fare questo:

  • Aggiungere un cookie token CSRF con il client REST e inviare lo stesso motivo in input nascosto con le vostre forme. Se il servizio e il client si trovano in domini diversi, è necessario condividere le credenziali. Sul servizio devi confrontare i 2 token e se sono uguali, la richiesta è valida ...

  • È possibile aggiungere il cookie token csrf con il servizio REST e inviare lo stesso token con le rappresentazioni di le tue risorse (input nascosti, ecc ...). Tutto il resto è uguale alla fine della soluzione precedente. Questa soluzione è al limite di RESTfulness.(Va bene fino a quando il client non chiama il servizio per modificare il cookie.Se il cookie è solo http, il client non dovrebbe saperlo, se non lo è, il client dovrebbe impostarlo.) Puoi fare un altro soluzione complessa se aggiungi diversi token a ciascun modulo e aggiungi tempo di scadenza ai cookie. Puoi anche inviare il tempo di scadenza con i moduli, in modo che tu sappia il motivo del fallimento di una convalida del token.

  • È possibile avere un utente segreto (diverso da ciascun utente) nello stato risorsa sul servizio. Creando rappresentazioni, è possibile generare un token (e un tempo di scadenza) per ogni modulo. È possibile generare un hash dal token effettivo (e il tempo di scadenza, il metodo, l'url, ecc.) E il segreto dell'utente e inviare anche l'hash con il modulo. Tenete il "segreto dell'utente" in segreto, ovviamente, quindi non lo invierete mai con il modulo. Successivamente, se il servizio riceve una richiesta, è possibile generare nuovamente l'hash dai parametri della richiesta e il segreto dell'utente e confrontarli. Se il non corrispondono, la richiesta non è valida ...

Nessuno di loro vi proteggerà se il client REST è javascript iniettabili, in modo da avere per controllare tutti i tuoi contenuti dell'utente contro entità HTML, e rimuovere tutti, oppure utilizzare sempre TextNode anziché innerHTML. È necessario proteggersi contro l'iniezione SQL e l'iniezione dell'intestazione HTTP. Non utilizzare mai un semplice FTP per aggiornare il tuo sito. E così via ... Ci sono molti modi per iniettare codice malvagio nel tuo sito ...

Ho quasi dimenticato di menzionare che le richieste GET sono sempre per la lettura da parte del servizio e del cliente. Dal servizio questo è ovvio, dal client che imposta qualsiasi url nel browser deve risultare una rappresentazione di una risorsa o di più risorse, non dovrebbe mai chiamare un metodo POST/PUT/DELETE su una risorsa. Ad esempio GET http://my.client.com/resource/delete -> DELETE http://my.api.com/resource è una soluzione molto pessima. Ma questa è un'abilità di base se vuoi ostacolare CSRF.

Problemi correlati