Rilascio di token sicuri che non richiedono alcun stato lato server

2

Il problema

Attualmente sto progettando il backend per una SPA (Single Page Application), che sto progettando di costruire in modo abbastanza RESTful. Il back-end sarà idealmente solo un sottile strato tra il client e il database. Quasi tutti i dati nel database saranno adattati a un utente specifico, motivo per cui ho bisogno di qualche forma di sistema di autenticazione.

Dato che viviamo in un universo in cui gli esseri umani sono abbastanza prevedibili e le macchine possono essere veramente molto veloci, dobbiamo ovviamente rendere lento l'hashing della password e la verifica della password (normalmente usando schemi di stretching chiave come bcrypt o whatnot ). Tutto questo è bello e dandy, ma complica la vita per noi poveri che vogliono progettare applicazioni veloci e scattanti usando un backend RESTful, perché idealmente un back-end non avrebbe bisogno di memorizzare alcun dato di sessione, ma solo autenticare ogni singola richiesta individualmente ( utilizzando ad esempio l'autenticazione di accesso di base).

Tuttavia ciò significherebbe hashing della password degli utenti per ogni singola richiesta, che aggiungerebbe una penalità dolorosa per ogni richiesta (e probabilmente rendendo più facili gli attacchi DDoS). Ovviamente questo problema può essere risolto memorizzando nella cache le credenziali dell'utente nel back-end, ma questa soluzione non sembra molto pulita e solleva alcuni altri problemi, come la gestione del ritiro della cache e la costrizione del client a memorizzare le credenziali in testo semplice. / p>

Quindi, in breve, il mio problema è che ho bisogno di qualche forma di sistema per gestire l'autenticazione degli utenti in modo rapido, ma idealmente posso ancora evitare qualsiasi stato lato server.

La mia soluzione proposta

Prima di tutto il traffico tra il client e il server verrà crittografato usando TSL standard di boog, quindi non dovremmo essere vulnerabili agli intercettatori né agli attacchi man-in-the-middle.

La soluzione che ho pensato è di rilasciare un token al client, dopo l'autenticazione iniziale riuscita utilizzando le credenziali inviate in testo normale su TSL, che contiene le informazioni necessarie necessarie al server per autenticare un utente. Questo token verrebbe calcolato nel modo seguente:

key = a random key generated when the server starts, never to be shared
nonce = just some random bytes grabbed out of the air

token = nonce + timestamp + user_id + HMAC(key, nonce + timestamp + user_id)

Ciò consentirebbe al server di verificare se il token è valido semplicemente convalidando l'HMAC per ogni richiesta, il che è molto economico da fare (economico quanto fare una ricerca in una tabella hash, ma eliminando del tutto la necessità di tabella hash in primo luogo). Se il token è valido e non è scaduto (non è consentito che il timestamp sia troppo vecchio) lascio che la richiesta proceda e consenta al database di gestire il problema dell'autorizzazione.

Questo sembra un modo per autenticare in modo efficiente e sicuro gli utenti, oltre a richiedere sorprendentemente pochi LoC da implementare oltre a evitare totalmente qualsiasi tipo di stato nel back-end (l'applicazione server userebbe una quantità costante di memoria per tutta la sua durata ).

La mia vera domanda

Ora questo schema può sembrare totalmente distruttivo a prima vista, ma dopo averlo osservato un po 'vedo due potenziali problemi che fanno sorgere delle domande:

  1. Non c'è modo di ritirare un token, tranne aspettare che scada. Questo è davvero un problema o sono io essere paranoico? Dal momento che deve essere memorizzato sul lato client, la sicurezza viene immediatamente compromessa se una parte malintenzionata accede a un client prima che il token scada. Certo, il pericolo è contenuto nel periodo di tempo in cui il token è valido, ma per questo SPA che il tempo potrebbe essere contato in giorni.

  2. Come si scala? La chiave del lato server segreto dovrebbe essere condivisa tra tutti i server del cluster, ma come dovremmo farlo in modo sicuro e quando dovremmo ritirare una chiave lato server?

Potrebbero esserci anche altri problemi, ma questi sono i due evidenti che si sono distinti per me. Cosa ne pensi di questo schema e di questi problemi?

Si noti che questa domanda non è una domanda generale sulla generazione sicura di token o sulla gestione delle sessioni (come è stato risposto molte volte qui), ma sui problemi periferici riguardanti questo schema specifico, problemi che non ho visto discusso.

    
posta Fors 01.06.2015 - 15:04
fonte

2 risposte

1

L'implementazione di una crittografia avanzata aggiunge una penalizzazione delle prestazioni, ma dovrebbe essere trascurabile per un utente umano, considerare un round trip dal browser Web al database con 600ms senza crittografia e 700 ms. Credo che l'incremento di 100 ms sia esagerato ma illustra il punto che cerco di fare, un umano non è in grado di dire la differenza in 100 ms. Potresti anche implementare tecniche di performance migliorate come mostrare un'immagine di spinner per mostrare che qualcosa sta effettivamente accadendo.

Come per attacchi specifici:

  • Forza brutale: il ritardo effettivamente ti aiuta, un utente malintenzionato avrebbe bisogno di un secolo extra per completare l'elaborazione della maschera
  • DoS e DDoS: gli analizzatori di pacchetti e il meccanismo di controllo delle inondazioni dovrebbero essere posizionati comunque, e queste misure sono metodi mitigativi ma non di elusione, in questi casi devi accettare un certo livello di rischio.

Non l'hai menzionato ma cosa succede quando scade un token? l'utente è obbligato a riconnettersi ?. In caso contrario, l'incapacità di scadere di un token pone un rischio maggiore, poiché chiunque sia in grado di rubare quel token può prevalere sulla "sessione" per tutto il tempo necessario. Tuttavia, questo è il modo in cui la maggior parte del software funziona, offrono all'utente la possibilità di disconnettersi e presuppongono che rimarranno connessi per un determinato periodo di inattività, rinnovando il tempo di scadenza ad ogni richiesta. Quindi, per il tuo caso, eseguirò un paio di casi d'uso e determinerò la scadenza della sessione ideale, regolando di conseguenza il timeout. (Nota a margine: assicurati di verificare che il token e le altre credenziali temporanee siano distrutti sia nel client che nel server).

Un'altra opzione è forzare un rinnovo del token ogni tanto, possibilmente trasparente dall'utente (non richiede l'autenticazione manuale) ma semplicemente aggiornando il token. Ciò invaliderebbe qualsiasi sessione duplicata con lo svantaggio che gli utenti sono in grado di aprire più di una scheda e potrebbe ostacolare l'esperienza dell'utente.

Per quanto riguarda il ridimensionamento, puoi incollare una sessione a un server specifico, questo ti permetterebbe di usare chiavi diverse nel tuo sistema

    
risposta data 01.06.2015 - 15:53
fonte
0

Penso che non sia una buona idea progettare il proprio algoritmo di generazione di token a meno che tu non sappia davvero cosa stai facendo. Potresti voler controllare questa libreria: link

  1. In genere, le applicazioni Web funzionano comunque oggi, ma puoi comunque avere un dizionario di token invalidati manualmente nella memoria da controllare in un servizio centralizzato prima di inoltrare le richieste alla tua app.

  2. Perché non utilizzare sessioni persistenti per evitare la condivisione?

risposta data 01.06.2015 - 16:34
fonte

Leggi altre domande sui tag