http spring - Come fare l'autenticazione con l'API REST giusto?(Browser+client nativi)



session authentication (1)

Questo ha funzionato bene per me, usando inoltre l'autenticazione BASIC:

curl -v --proxy '' --basic -u Administrator:password -X POST -H "Content-Type: application/json"
        --data-binary '{"value":"30","type":"Tip 3","targetModule":"Target 3","configurationGroup":null,"name":"Configuration Deneme 3","description":null,"identity":"Configuration Deneme 3","version":0,"systemId":3,"active":true}'
        http://httpbin.org/post

Ovviamente, non si dovrebbe mai usare l'autenticazione BASIC senza SSL e un certificato controllato.

Oggi mi sono imbattuto in questo, usando CURL 7.49.1 di Cygwin per Windows ... E quando --data o --data-binary con un argomento JSON, cURL si confondeva e interpretava il {} nel JSON come un URL modello. Aggiungendo un argomento -g per disattivare globbing cURL risolto.

Vedi anche Passare un URL con parentesi per arricciare .

Sto costruendo un'applicazione web usando Rails. Al momento sto utilizzando Devise con le sessioni HTTP che è stato abbastanza facile da configurare e funziona bene.

L'applicazione consiste in un URL che fornisce un'applicazione Web AJAX. Il resto degli URL disponibili appartiene all'API REST. Quindi tutto e ogni piccola richiesta di dati viene effettuata tramite AJAX.

Ora mi piacerebbe estendere il tutto per supportare i client nativi. Ho letto molto su auth stateless, http basic e digest auth, http session, cookies, xsrf, ecc ... E ora mi sento come se non potessi avere un'app sicura, perché c'è sempre un modo per dirottarne alcune parti .

1 .: Vs. sessione HTTP token di autenticazione stateless

Qual è la differenza? Non capisco

  • Sessione HTTP:

    1. Il client richiede un URL (prima richiesta al server)
    2. Il server fornisce la risposta normale più una stringa univoca (== ID sessione)
    3. Il cliente deve inviare questa stringa ad ogni richiesta (che viene eseguita automaticamente usando l'intestazione HTTP)
    4. Il client accede -> Il server memorizza che questo particolare ID sessione è ora connesso
    5. Il client visita una pagina che richiede auth -> Niente di speciale da fare, poiché l'ID di sessione verrà automaticamente inviato al server tramite l'intestazione HTTP
  • token di autenticazione stateless:

    1. URL richiesta client (prima richiesta al server)
    2. Server fornisce semplicemente la risposta normale senza alcuna chiave o token o id
    3. (niente di speciale qui)
    4. Il client esegue l'accesso -> Server crea un token di autenticazione e invia questo token al client all'interno della risposta
    5. La pagina delle visite del cliente che richiede auth -> Il client deve inviare il token di autenticazione

Per me entrambi i modi sembrano abbastanza simili. Con Rails posso anche scegliere di memorizzare la sessione all'interno del database ... Devise farebbe lo stesso con il token auth stateless.

2 .: il metodo di autenticazione

In questo momento sto usando POST /users/sign_in con {"user":{"email":"[email protected]","password":"p455w0rd"}} .

Ma ci sono altre possibilità come HTTP basic auth e HTTP digest auth, ma anche soluzioni come oAuth (troppo grandi per il mio scopo).

Da quello che ho letto:

  • Per quanto riguarda la sicurezza di sign_in non c'è differenza tra l'attuale POST /users/sign_in e l'autenticazione di base HTTP. Entrambi usano testo chiaro.
  • Per sign_out HTTP l'autenticazione di base ha uno svantaggio: la disconnessione è possibile solo chiudendo la finestra del browser
  • L'auth di digest HTTP ha un enorme vantaggio: non trasmette la password (solo un hash di password più una stringa generata casualmente)
  • (Tedesco) Wikipedia dice: HTTP digest auth non è supportato da tutti i browser. Forse questa informazione è troppo vecchia ?!

Ciò che di cui ho bisogno:

  • nomi utente e password hash (bcrypt) memorizzate in un database.
  • l'utente può cambiare la sua password e la password non deve essere inviata in chiaro. (Lo stesso problema si verifica quando si tratta di sign_up dell'utente). Possibili soluzioni?
    1. ovviamente: usando SSL / TLS
    2. il client richiede un want_to_change_password_salt e lo usa per crittografare la password sul lato client. ma (?!) in questo modo ho inviato una parte essenziale della password hash sul cavo più la password con hash. Mi sembra insicuro ?!

3 .: Token CSRF

Come detto sopra, adesso ho solo un normale sito Web AJAX che utilizza l'API REST. Ha una protezione XSRF: il sito web viene consegnato da binari e quindi ha incorporato il token XSRF. L'ho letto usando AJAX e lo trasmetto quando faccio un POST . Rails restituisce quindi i dati richiesti e un nuovo token XSRF, che quindi utilizzerò per il prossimo POST .

Ora voglio cambiare la mia applicazione server per lavorare con i client nativi. Un client nativo non caricherà la pagina HTML e quindi non recupererà un token CSRF. Quindi mi sono venute in mente le seguenti opzioni:

  • Creare una risorsa REST token XSRF. Quindi il client (nativo) deve richiedere un token XSRF da questa risorsa prima di poter eseguire il primo POST .
  • Disattiva completamente la protezione XSRF.

Domande:

  • Come funziona la protezione XSRF (in Rails)? Come fa il server a sapere quale token appartiene a quale client? L'unico modo in cui riesco a pensare sono le sessioni. Questa assunzione porta a:
  • Se disabilito la sessione per creare un'API REST completamente stateless, la protezione XSRF non funzionerà più. Destra?

4: token di autenticazione stateless

Qui ho per lo più un sacco di domande:

  • Ha gli stessi problemi di sicurezza delle sessioni HTTP? Cosa intendo: rubare l'ID di sessione ha lo stesso effetto di rubare il token di autenticazione. Destra?
  • La scadenza del token di autenticazione dovrebbe funzionare come con le sessioni HTTP: il server deve memorizzare da qualche parte (database e sessione) un timestamp e controllarlo.
  • sign_out funziona allo stesso modo?
    • Sessione: distruggere la sessione sul server
    • Token di autenticazione: distruggi il token sul server
  • Da quello che ho letto dovrebbe essere più sicuro conservare il token di autenticazione all'interno dell'intestazione HTTP (proprio come l'ID di sessione), perché i log del server possono contenere i parametri GET e quindi potrebbero contenere il token.
  • Dovrebbe essere un semplice token di autenticazione o sarebbe meglio se il client trasmettesse anche il suo user_id o anche la password con hash? HERE ho letto che il client dovrebbe inviare:
    1. user_id
    2. expiration_date
    3. un hash (o cos'è l'HMAC?) di [ user_id , expiration_date , SECRET_KEY ]. Dove SECRET_KEY è fondamentalmente una stringa casuale generata dal server.

Ci scusiamo per il post huuuge, ma la sicurezza è essenziale! E non voglio fare errori di progettazione che potrebbero probabilmente esporre dati privati.

Grazie :)

Ecco un po 'di nuove informazioni e nuove domande ;-) :

5 .: clienti nativi

Per quanto riguarda i client nativi, non esiste un modo ( facile ) di utilizzare le sessioni:

  • Un client nativo non è un browser

  • Quindi non gestirà facilmente i cookie (e senza i cookie non c'è una gestione tipica delle sessioni)

Quindi ci sono 3 scelte possibili:

  1. Implementare la gestione delle sessioni per i client nativi. Questo sarebbe come:

    1. Accesso
    2. leggi l'intestazione HTTP della risposta per ottenere i cookie
    3. salva tutti i dati dei cookie di cui hai bisogno (specialmente quello con le informazioni di sessione) localmente
    4. invia questo id di sessione ad ogni richiesta che fai
  2. Non usare affatto le sessioni. Dal punto di vista di un client nativo è praticamente lo stesso di 1 .:

    1. Accesso
    2. Ottieni un token di autenticazione dall'intestazione HTTP o dal corpo della risposta (è la tua app, anche se dipende da te)
    3. salva questo token localmente
    4. invia questo token ad ogni richiesta
  3. L'approccio ibrido. Ciò significa fondamentalmente che il server deve distinguere tra browser e client nativo e quindi controllare l'ID di sessione fornito ei dati di sessione o (per i client nativi) controllare il token di autenticazione fornito.

6 .: Autorizzazione CSRF Token con stateless (= nessuna sessione / nessun cookie)

La protezione CSRF protegge i tuoi utenti da siti Web dannosi, che cercano di fare qualche richiesta sulla tua API nel nome dell'utente che ha effettuato l'accesso, ma senza che l'utente lo sappia. È piuttosto semplice quando si usano le sessioni:

  1. L'utente accede alla tua API
  2. La sessione viene creata
  3. Il tuo browser utenti avrà un cookie impostato con questo ID di sessione
  4. Ogni richiesta effettuata dall'utente viene automaticamente autenticata, poiché il browser invierà tutti i cookie (incluso l'id di sessione) insieme a ciascuna richiesta alla tua API

E quindi il sito web che attacca semplicemente deve fare quanto segue:

  1. Scrivi un <form> HTML personalizzato che punta alla tua API
  2. Lascia che l'utente in qualche modo faccia clic sul pulsante Submit

Ovviamente questo modulo sarà qualcosa del tipo:

<form action="http://your.api.com/transferMoney" method="post">
  <input type="hidden" name="receiver" value="ownerOfTheEvilSite" />
  <input type="hidden" name="amount" value="1000.00" />
  <input type="submit" value="WIN MONEY!!" />
</form>

Ciò porta alle seguenti ipotesi :

  1. La protezione CSRF è necessaria solo perché i browser inviano automaticamente i cookie.

  2. I clienti nativi non hanno bisogno della protezione CSRF (ovviamente: il tuo browser non può accedere ai dati di autenticazione (token, cookie, qualunque cosa) della tua app nativa e l'app nativa non utilizzerà un browser per comunicare con l'API)

  3. Se hai un design API che non usa i cookie per autenticare l'utente, non c'è possibilità di fare CSRF. Poiché l'utente malintenzionato deve conoscere il token di autenticazione e inviarlo esplicitamente insieme alla richiesta dannosa.

Ovviamente, se si desidera sovraccaricare la propria app, è possibile utilizzare i token CSRF insieme al proprio meccanismo di autenticazione stateless, ma sono piuttosto sicuro che non vi siano ulteriori vantaggi in termini di sicurezza.

7 .: I giusti metodi HTTP da scegliere

Login / Accedi e Logout / Esci:

Non utilizzare mai GET per (almeno) tre motivi:

  1. La protezione CSRF nella maggior parte dei casi protegge solo POST, PUT, PATCH e DELETE e quindi un CSRF può accedere a un utente a sua insaputa quando usa una richiesta GET

  2. Le richieste GET non dovrebbero mai cambiare lo stato dell'applicazione. Ma quando si usa Sessioni, lo stato dell'applicazione cambia al login / logout, perché una sessione viene creata o distrutta.

  3. Quando si utilizza una richiesta GET e si trasmettono le informazioni di autenticazione come parametri URL (ad es. http://your.api.com/login?username=foo&password=bar ) c'è un altro problema: registri del server! La maggior parte dei server registra semplicemente ogni richiesta HTTP inclusi tutti i parametri URL. Ciò significa: se il tuo server viene hackerato, non c'è bisogno di decifrare gli hash delle password dal tuo DB, devono solo dare un'occhiata ai file di log del server. Inoltre, un amministratore malintenzionato potrebbe anche leggere le informazioni di accesso per ogni utente. soluzioni:

    • Usa POST (o qualsiasi altro metodo ti piaccia) e invia le informazioni di autenticazione all'interno del corpo della richiesta. O:
    • Invia le informazioni di autenticazione all'interno delle intestazioni HTTP. Perché tali informazioni normalmente non compaiono nei file di registro del server. O:
    • Dai un'occhiata alla configurazione del server, e digli di rimuovere ogni parametro URL che è chiamato "password" (o è offuscato, quindi l'URL diventa login?username=foo&password=*** all'interno dei log). Ma suggerisco di usare semplicemente il corpo della richiesta per questo tipo di informazioni insieme al metodo POST.

Quindi potresti usare per esempio:

POST http://your.api.com/authentication per il login

DELETE http://your.api.com/authentication per il logout

8: password e hashing

L'autenticazione funziona solo con alcune chiavi segrete. E ovviamente questa chiave dovrebbe essere tenuta segreta. Questo significa:

  • Non memorizzare mai una password in chiaro nel database. Ci sono diverse librerie disponibili per renderlo sicuro. Secondo me l'opzione migliore è bcrypt .

  • bcrypt : è stato ottimizzato per le password hash. Genera automaticamente un sale e blocca la password più volte (round). Inoltre la stringa hash generata contiene tutto il necessario: numero di giri, sale e hash. Anche se hai solo bisogno di memorizzare questa stringa e non c'è bisogno di scrivere nulla a mano.

  • ovviamente puoi anche usare qualsiasi altra libreria di hashing forte. Ma per la maggior parte di essi, devi implementare la salatura e utilizzare più di 1 round. Inoltre non ti danno solo una singola stringa come fa bcrypt, anche se devi gestire te stesso per immagazzinare giri, sale e hash e ricomporli in seguito.

  • rounds : questa è semplicemente la frequenza con cui la password deve essere sottoposta a hash. Quando si usano 5000 colpi, la funzione di hashing restituirà l'hash dell'hash dell'hash dell'hash della password . C'è fondamentalmente un solo motivo per farlo: costa CPU Power! Ciò significa che quando qualcuno cerca di potenziare il tuo hash, ci vogliono 5000 volte più a lungo quando usi 5000 round. Per l'applicazione in sé non importa più di tanto: se l'utente conosce la sua password, non riconoscerà, se il server ha impiegato 0,0004ms o 2ms per convalidarlo.

  • buone password : la migliore funzione di hashing è inutile, se la password è troppo semplice. Se può essere rotto, usando un dizionario, non importa se lo hai con 5000 round: ci vorranno alcune ore in più, ma quali sono alcune ore, se potrebbero essere mesi o anni? Anche se assicurati che le password degli utenti contengano i soliti consigli (in basso + maiuscolo + numeri + caratteri speciali, ecc. Pp.)

9 .: Invio di password crittografate via cavo

Se non puoi (o non vuoi) fare affidamento su HTTPS, ma non vuoi inviare password in chiaro quando si effettua l'accesso, è possibile utilizzare la crittografia asimmetrica ( http://en.wikipedia.org/wiki/Public-key_cryptography ).

Questo server crea una coppia di chiavi (chiave pubblica e chiave privata). La chiave pubblica è resa disponibile ai clienti, la chiave privata deve essere mantenuta privata!

Il client può ora crittografare i dati utilizzando la chiave pubblica e questi dati possono essere decodificati solo dal proprietario della chiave privata (= il server).

Questo non dovrebbe (!) Essere usato per memorizzare le password nel database, perché se il tuo server viene violato, l'hacker avrà le password crittografate e la chiave privata per la decrittografia. Sebbene continui a utilizzare un algoritmo di hashing (come bcrypt) per l'archiviazione delle password nel tuo database. Un altro motivo è che puoi facilmente generare una nuova coppia di chiavi, se pensi che qualcuno abbia violato la crittografia.

HTTPS funziona fondamentalmente allo stesso modo. Tuttavia, se la tua applicazione utilizza HTTPS (che è consigliato), non ci sarebbe alcun grande vantaggio in termini di sicurezza. Ma come detto sopra, se non puoi usare HTTPS per qualsiasi motivo o non ti fidi, è un modo per creare la tua connessione sicura.

E tieni presente che una vera connessione HTTPS crittografa l'intera (!) Connessione e tutti i dati, non solo i dati della password. E lo cripta in entrambi i modi, da client a server e da server a client.





http session rest authentication stateless