Token di sicurezza per l'esposizione esterna: UUID o HMAC / JWT / hash?

1

Sto costruendo il back-end per un'app web. Quando un nuovo utente accede al sito e fa clic sul pulsante Iscriviti , verrà compilato un modulo super semplice chiedendo loro il nome utente + la password e invieranno. Questo richiede al server di inviare una email di verifica a quell'indirizzo email. Verranno quindi controllati tramite e-mail, facendo clic su un collegamento (che verifica la loro e-mail) e quindi indirizzati alla pagina di accesso in modo che possano accedere se lo desiderano.

Per verificare la loro posta elettronica, quando il server genera l'e-mail dovrà creare (e memorizzare) un token di verifica (probabilmente un UUID) e allegarlo a questo link nell'e-mail, in modo che il collegamento abbia un aspetto simile a:

"https://api.myapp.example.com/v1/users/verify?vt=12345"

Dove vt=12345 è il "token di verifica" (probabilmente un UUID). Quindi l'utente fa clic su questo link e il mio GET v1/users/verify endpoint esamina il token, in qualche modo conferma la sua validità e rende alcuni aggiornamenti DB per "attivare" l'utente. Ora possono accedere.

Scenari simili per quando un utente desidera annullare l'iscrizione alla posta elettronica o quando non è in grado di ricordare la propria password e deve ripristinarla in modo che possano accedere.

Cancella

L'utente vuole smettere di ricevere email ma vuole comunque utilizzare l'app. Fanno clic su un link " Annulla iscrizione " in una newsletter settimanale che inviamo loro. Questo link deve contenere una sorta di "token di annullamento sottoscrizione" simile che, come il token di verifica sopra, viene generato + memorizzato sul server e utilizzato per autenticare la richiesta dell'utente di annullare l'iscrizione via email.

Recupera password

Qui l'utente ha dimenticato la sua password e ha bisogno di recuperarla. Quindi, nella schermata di accesso, fanno clic sul collegamento " Hai dimenticato la password " e vengono presentati con un modulo in cui devono compilare il proprio indirizzo email. Il server invia una e-mail a quell'indirizzo. Controllano questa e-mail e contiene un link a un modulo dove possono inserire la loro nuova password. Questo link deve contenere un "token password di reimpostazione" che, come il token di verifica sopra, viene generato + memorizzato sul server e utilizzato per autenticare la richiesta dell'utente di cambiare la password.

Quindi qui abbiamo tre problemi molto simili da risolvere, tutti che richiedono l'uso di ciò che chiamo " token di sicurezza una tantum (OTO) ". Questi token OTO:

  • Deve essere generato lato server e persistente (forse in una tabella security_tokens )
  • Deve essere qualcosa che può essere allegato ai link che esporremo dall'interno delle email
  • Deve essere valido una sola volta: una volta cliccato, il token è "usato" e non può essere riutilizzato

La mia domanda

La soluzione che ho trovato era semplice ... quasi troppo semplice.

Per i token sto solo generando UUID casuali (36 caratteri) e li memorizzo in una tabella security_tokens con i seguenti campi:

[security_tokens]
---
id (PK)
user_id (FK to [users] table)
token (the token itself)
status (UNCLAIMED or CLAIMED)
generated_on (DATETIME when created)

Quando il server li crea sono "UNCLAIMED". Quando l'utente fa clic su un collegamento all'interno della tabella, viene "CLAIMATO". Un lavoro di background worker verrà eseguito periodicamente per ripulire eventuali token CLAIMED o per eliminare tutti i token UNCLAIMED che sono "scaduti" (in base ai relativi campi generated_on ). L'app ignorerà anche tutti i token che sono stati precedentemente accreditati (e non sono ancora stati puliti).

I penso questa soluzione potrebbe funzionare, ma non sono un super-ragazzo di sicurezza e sono preoccupato che questo approccio:

  1. Forse lascia aperta la mia app a qualche tipo di attacco / exploit; e
  2. Forse reinventa la ruota quando qualche altra soluzione potrebbe funzionare altrettanto bene

Come per il 2 ° in alto mi chiedo se dovrei usare un meccanismo hash / HMAC / JWT invece di un UUID morto. Forse ci sono alcuni esperti di criptazione / sicurezza che hanno trovato un modo per far sì che questi token contengano lo stato CLAIM e la data di scadenza stessi in modo sicuro / immutabile, ecc.

    
posta smeeb 09.01.2018 - 16:13
fonte

1 risposta

1

Vedo alcuni dubbi con ciò che hai descritto:

  1. A seconda di come generi gli UUID, potrebbero non essere così imprevedibili - sono non crittograficamente forti .
  2. Non descrivi uno "scopo" nella tua tabella security_tokens , che potrebbe (a seconda del tuo codice) consentire a qualcuno di utilizzare un token per uno scopo diverso da quello previsto.
  3. Il tuo sistema richiede un sacco di traffico nel database - questo potrebbe o potrebbe non essere un problema, a seconda della quantità di traffico che prevedi di ricevere.

Una soluzione alternativa sarebbe utilizzare (per esempio) JWT e includere nei dati firmati:

  1. L'ID utente per il quale è valido.
  2. Lo scopo per cui è valido.

JWT fornisce di per sé le funzionalità per la gestione delle scadenze. Nota che questo genera token che possono essere "double spesi", ma se sono per validare e-mail o annullare l'iscrizione, non è un problema. (Convalida la stessa e-mail due volte un problema?)

Per questi token, tutto ciò di cui hai bisogno è un segreto memorizzato sul server delle applicazioni e non comporta scritture di database aggiuntive per generare o spendere i token. (Che, ancora una volta, è un problema di ridimensionamento, quindi potrebbe applicarsi o meno alla tua applicazione.)

    
risposta data 09.01.2018 - 17:50
fonte

Leggi altre domande sui tag