Come implementare in modo sicuro una funzione "Ricordami"?

55

Supponendo che tu abbia già un sito web che implementa tutti gli elementi di accesso standard, qual è il modo corretto e più sicuro per consentire agli utenti di accedere automaticamente per un certo periodo di tempo (diciamo 30 giorni)? Questo periodo dovrebbe essere rigorosamente applicato; cioè, non penso che una soluzione valida possa dare a un cookie una data di scadenza e sperare che il browser dell'utente la cancelli in modo tempestivo.

Dettagli del sistema di accesso esistente:

  • Un utente deve inserire un nome utente e una password
  • Il database memorizza i nomi utente e strong_hash_function (password + user_specific_random_salt)
  • I moduli di accesso vengono caricati e inoltrati su SSL, come tutte le pagine successive

Ci sono molti esempi là fuori e molti con evidenti difetti di sicurezza. Usiamo PHP come lingua di arrivo, ma i concetti dovrebbero essere applicabili a qualsiasi lingua.

    
posta colithium 11.11.2010 - 23:32
fonte

6 risposte

30

Ci sono due parti per la mia risposta:

Innanzitutto, supponendo che il tuo modello di minaccia non si preoccupi dell'esposizione dei cookie lato client , puoi generare e archiviare un nonce sul lato server, l'hash con il nome utente e altre informazioni (es. , computername, timestamp, roba simile) e inviarlo nel cookie. Il nonce deve essere memorizzato nel database, insieme alla data di scadenza, ed entrambi vengono controllati quando il cookie ritorna.
Questo dovrebbe essere solo un cookie di "memorizzazione", NON un cookie di sessione - cioè quando si ottiene quel cookie senza una sessione, riemettere un nuovo cookie di sessione regolare. Si noti inoltre che, come il cookie di sessione, questo dovrebbe essere distribuito solo su https, e usando gli attributi secure e httpOnly , e ovviamente avere il cookie corretto correttamente ecc.

In secondo luogo, per gli utenti che sono stati ricordati, si dovrebbe (probabilmente, in base alla sensibilità) invocare un processo di riautenticazione per alcune operazioni sensibili. Cioè, a volte avrebbero bisogno di inserire nuovamente la password in ogni caso, ma raramente, e solo per operazioni sensibili. Questo serve a prevenire attacchi come CSRF e l'esposizione desktop condivisa.

    
risposta data 11.11.2010 - 23:56
fonte
7

Ho scritto a lungo su sicuro caselle di accesso e "ricordami" . La risposta accettata non è sbagliata, ma direi che è un po 'più complicato da un punto di vista rispetto a quello che deve essere, e trascura un'area in cui è necessaria un po' più di complessità.

generate and store a nonce on the server side, hash that with the username and other info (e.g. client ip, computername, timestamp, similar stuff), and send that in the cookie. The nonce should be stored in the database, together with expiry date, and both checked when the cookie comes back.

Quindi un'implementazione che non espone alcuna informazione al client potrebbe sembrare ...

// Storage:
setcookie(
    'rememberme',
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce),
    time() + 8640000 // 100 days, for example
);
// Validation:
$valid = hash_equals(
    $_COOKIE['rememberme'],
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce)
);

La ovvia limitazione qui è che, se il tuo indirizzo IP (o altre informazioni) cambia, il tuo cookie è inutile. Alcuni potrebbero vedere questo come una buona cosa, lo vedo come inutilmente ostile verso l'usabilità per gli utenti Tor.

Puoi certamente interpretare la citazione sopra per indicare qualcosa di diverso, come il token Web JSON di un povero uomo. Tuttavia, i cookie HTTP sono limitati a 4 KiB per dominio. Lo spazio è prezioso.

Un altro problema: quante informazioni perde la query del database sulla tua applicazione? (Sì, sto parlando di canali secondari.)

Strategia "Remember Me" sicura di Paragon Initiative

Conservazione:

  1. Genera una stringa casuale da 9 byte da random_bytes() (PHP 7.0+ o tramite random_compat ), base64 lo codifica su 12. Questo sarà usato per ricerche nel database.
  2. Genera un'altra stringa casuale, preferibilmente lunga almeno 18 byte, e ancora una volta base64 la codifica (a 24+). Questo verrà effettivamente utilizzato per l'autenticazione.
  3. Memorizza $lookup e hash('sha256, $validator) nel database; ovviamente associato a un account utente specifico.
  4. Memorizza $lookup . $validator nel cookie HTTP dell'utente (ad esempio rememberme ).

Convalida (accesso automatico):

  1. Dividi il cookie in $lookup e $validator .
  2. Esegui una ricerca nel database basata su $lookup ; va bene se c'è un canale laterale temporizzato qui.
  3. Controlla hash_equals($row['hashedValdator'], hash('sha256', $validator)) .
  4. Se il passaggio 3 restituisce TRUE , associare la sessione corrente con l'account utente appropriato.

Analisi della sicurezza:

  • Quali canali secondari sono mitigati?
    Il più significativo: attenua l'impatto delle perdite di informazioni temporali sull'operazione di confronto delle stringhe utilizzata nella ricerca del database.

    Se hai implementato un'autentica autenticazione token casuale, un utente malintenzionato potrebbe inviare molte richieste finché non troveranno un token di autenticazione valido per un utente. (Probabilmente non sarebbero in grado di selezionare la vittima che stanno impersonando.)

  • Che cosa succede se un utente malintenzionato può divulgare la tabella del database di autenticazione a lungo termine? Abbiamo archiviato un hash SHA256 di $validator nel database, ma il testo in chiaro per l'hash è memorizzato nel cookie. Poiché l'input è un valore di entropia elevato, è improbabile che la ricerca di forza bruta produca risultati. (Questo riduce anche la necessità di ad esempio bcrypt.)

Questa strategia è implementata in Gatekeeper .

    
risposta data 01.01.2016 - 08:57
fonte
5

Questa è una domanda interessante. Inizierò dicendo che non sono sicuro che possa essere fatto senza un certo indebolimento della sicurezza dell'applicazione, ma a seconda del sito che può essere considerato meritevole del compromesso.

Quindi un approccio potrebbe essere

Il database dello stato della sessione dovrà registrare l'ora in cui è impostato un token e la durata che deve essere ricordata in modo che possa confrontare il token presentato.

Quando l'utente accede all'applicazione ha una casella di controllo che consente loro di rimanere connessi (idealmente con un intervallo di periodi di tempo che l'utente può selezionare)

Quando viene impostato il token di sessione, tale periodo viene utilizzato alla scadenza del cookie. Nelle visite successive, il cookie verrà presentato all'applicazione e il server dovrà verificare se è ancora valido (ovvero il periodo di scadenza per la funzione remember me non è stato raggiunto). Se il token è ancora valido, l'utente viene considerato come autenticato, in caso contrario il token viene cancellato e l'utente viene reindirizzato alla schermata di accesso.

Problemi con questo approccio. Browser condivisi significherebbe che l'accesso non autorizzato è possibile.

Anche chiunque possa accedere al cookie (tramite una vulnerabilità nel sito, un problema del browser o malware installato nella macchina) sarà in grado di ottenere l'accesso non autorizzato al sito. Un suggerimento comune per attenuare questo è il tentativo di legare il cookie a un indirizzo IP di origine (crittografato o memorizzato sul server ovviamente) che viene controllato quando l'utente presenta il cookie, tuttavia questo causa problemi con ambienti aziendali di grandi dimensioni in cui un utente può venire da un numero di indirizzi IP, e potrebbe anche essere suscettibile allo spoofing.

    
risposta data 11.11.2010 - 23:52
fonte
4

Non devi dipendere dal browser per eliminare il cookie, hai il sito controlla la data di scadenza sul cookie.

In effetti, per essere sicuro, direi che probabilmente è quello che dovrai fare comunque, perché vorresti impostare la data di scadenza come parte del valore del cookie e crittografarla piuttosto che fare affidamento sulla proprietà di scadenza del solo testo del cookie stesso.

E vorresti davvero che il cookie fosse contrassegnato come sicuro, e utilizzi un cookie secondario per la durata di una singola sessione, oppure proteggere l'intera sessione (non solo il processo di accesso) con SSL per impedire che il cookie venisse rubato fuori dal filo e utilizzato da una parte non autorizzata.

    
risposta data 11.11.2010 - 23:53
fonte
0

Questo articolo descrive un approccio che difende dal furto di cookie e dall'ipotesi di ID di sessione:

  • Un cookie "ricordami" è composto dall'ID utente, un token (numero casuale grande) e un token sequenza (un altro grande numero casuale).
  • Quando un utente presenta il cookie, il database viene ricercato per queste tre informazioni.
    • Se trovato, il token di sequenza viene rigenerato, archiviato nel database e inviato all'utente
    • Se vengono trovati solo il nome utente e il token, si presume che qualcun altro abbia rubato il cookie perché il token di sequenza è già stato utilizzato. Il sistema può quindi avvisare l'utente e / o eliminare tutti i token memorizzati da questo utente dal database.

Come ulteriore misura di sicurezza si consiglia che azioni critiche come la modifica dei dati di accesso o il pagamento di cose richiedano la password, se l'utente è stato autenticato solo con il cookie "ricordami".

La tabella del database che contiene l'id utente e i token contiene anche la data di scadenza dei token. Potrebbe anche contenere l'agente utente e l'indirizzo IP, quindi un utente malintenzionato deve replicare anche questi. Tutti i token dovrebbero essere archiviati come hash perché fondamentalmente funzionano come password e consentirebbero a un utente malintenzionato, che ha ottenuto il database, di accedere.

Ho creato una implementazione di esempio per PHP.

    
risposta data 04.01.2013 - 13:55
fonte
-1

Un approccio comune sarebbe utilizzare un algoritmo di crittografia strong per memorizzare un cookie contenente le seguenti informazioni sul computer dell'utente:

AES ([Data di scadenza] + [sale saltuario] + [Nome utente] + [Password])

Ciò consentirebbe all'utente di invalidare il cookie (se fosse stato compromesso) semplicemente cambiando la password. La crittografia consente all'operatore del sito di verificare che non sia stato manomesso (ad esempio prolungando la data di scadenza) e di proteggere la password di testo non crittografato. Includere la password significa che un utente malintenzionato che ottiene l'accesso alla chiave AES non può ancora "modificare" i token per l'autenticazione come utenti arbitrari.

Lo svantaggio è che il sito deve proteggere un tasto AES.

Un approccio alternativo sarebbe semplicemente quello di aggiungere due campi al database dell'utente, per un valore del cookie (valore generato da long / strong / randomly) e una data di scadenza associata. Qualsiasi utente che presenti quel cookie sarà autenticato fino alla data di scadenza. Uno dovrebbe solo assicurarsi sul lato server che operazioni come la modifica di una password rimuovano i cookie "ricordami" memorizzati nel database.

I vantaggi di questo approccio sono che non esiste una chiave di crittografia da gestire (o che si preoccupa di andare avanti) e che non vi è alcuna possibilità che la password dell'utente venga compromessa. Gli svantaggi sono che hai bisogno di spazio di archiviazione aggiuntivo per i tuoi utenti.

Sull'ulteriore aspetto positivo, questo approccio consente di fornire diversi token "ricordami" su dispositivi diversi (come l'account manager di Google), semplicemente memorizzando i token in una tabella separata e consentendo più di una voce per utente. In questo modo è possibile tracciare da dove viene utilizzato ciascuno e invalidarli singolarmente.

    
risposta data 19.11.2010 - 08:21
fonte