Domanda:
Come implementare in modo sicuro una funzione "Ricordami"?
colithium
2010-11-12 04:32:08 UTC
view on stackexchange narkive permalink

Supponendo che tu abbia già un sito web che implementa tutte le funzioni 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 di tempo dovrebbe essere applicato rigorosamente; cioè, non credo che una soluzione valida sarebbe dare a un cookie una data di scadenza e sperare che il browser dell'utente lo elimini 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 così come strong_hash_function (password + user_specific_random_salt)
  • I moduli di accesso vengono caricati e inviati tramite SSL, come sono tutte pagine successive

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

possibile duplicato di [Buone pratiche di sessione] (http://security.stackexchange.com/questions/41/good-session-practices)
di interesse: [È sicuro memorizzare l'hash della password in un cookie e utilizzarlo per l'accesso "ricordami"?] (http://security.stackexchange.com/questions/9455/is-it-safe-to- memorizzare-la-password-hash-in-un-cookie-e-usarla-per-ricordarmi-l / 9456 # 9456)
Sei risposte:
#1
+32
AviD
2010-11-12 04:56:36 UTC
view on stackexchange narkive permalink

La mia risposta è incompleta e presenta un difetto non banale, in alcune situazioni - per favore vedi invece la risposta di @ Scott.


Ci sono due parti nella mia risposta:

Innanzitutto, supponendo che il tuo modello di minaccia non si preoccupi dell'esposizione dei cookie lato client , puoi generare e memorizzare un nonce sul lato server, hash che con il nome utente e altre informazioni ( es. ip client, nomecomputer, timestamp, cose simili) e inviarlo nel cookie. Il nonce dovrebbe essere memorizzato nel database, insieme alla data di scadenza, ed entrambi controllati quando il cookie ritorna.
Questo dovrebbe essere solo un cookie di "ricordo", NON un cookie di sessione, ovvero quando ottieni quel cookie senza una sessione , riemetti un nuovo cookie di sessione regolare. Tieni inoltre presente che, come il cookie di sessione, questo dovrebbe essere consegnato solo su https e utilizzando gli attributi secure e httpOnly e, naturalmente, il cookie deve essere adeguatamente definito come ambito ecc.

Secondo, per gli utenti che sono stati ricordati, dovresti (probabilmente, a seconda della sensibilità) invocare un processo di riautenticazione per alcune operazioni sensibili. Cioè, a volte avrebbero bisogno di inserire di nuovo la password comunque, ma raramente e solo per operazioni sensibili. Questo serve per prevenire attacchi come CSRF e esposizione di desktop condivisi.

Vedi Amazon per un esempio della seconda parte!
@adamse, gentile, di solito mi riferisco a LinkedIn. Amazon ancora meglio :)
Solo l'hash del nonce dovrebbe essere archiviato nel database.
@Jacco è necessario memorizzare la data di scadenza, in modo da sapere fino a quando è valida. Supponendo che tu voglia che scada dopo un tempo limitato ...
Intendevo dire che, poiché il nonce è una password equivalente, invece del nonce, solo l'hash del nonce dovrebbe essere memorizzato nel database. Ovviamente devi anche memorizzare cose come la data di scadenza e / o l'ID utente, ecc.
Ah, ora capisco, ma è sbagliato, il nonce non è l'equivalente di una password, deve essere sottoposto a hashing * insieme * ai dettagli dell'utente. Altrimenti dovresti memorizzare IP, nome del computer, ecc. Sul server.
Penso che entrambi abbiamo un approccio diverso. dati come il nome del computer possono essere falsificati, gli indirizzi IP possono cambiare per molte ragioni. Creo un numero casuale a 256 bit, memorizzo (hash di) questo numero nel database e imposto un cookie con la chiave primaria e il numero casuale. Si presume che chi presenta il cookie sia l'utente associato al record DB. (vedi anche la risposta che ho collegato sopra).
La mia risposta è incompleta e presenta un difetto non banale, in alcune situazioni - vedere la risposta di @Scott's, http://security.stackexchange.com/a/109439/33.
#2
+13
Scott Arciszewski
2016-01-01 13:57:41 UTC
view on stackexchange narkive permalink

Ho scritto a lungo su accessi sicuri e caselle di controllo "ricordami". La risposta accettata non è sbagliata, ma direi che è un po 'più complicata da un punto di vista di quanto dovrebbe essere e trascura un'area in cui è necessaria un po' più di complessità.

genera e archivia un nonce sul lato server, lo hash con il nome utente e altre informazioni (es. ip client, nomecomputer, timestamp, cose simili) e invialo nel cookie. Il nonce dovrebbe essere memorizzato nel database, insieme alla data di scadenza, ed entrambi controllati quando il cookie torna.

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

  // Memoria: setcookie ('rememberme', hash_hmac ('sha256', $ username. $ _SERVER ['REMOTE_ADDR'], $ storedNonce), time () + 8640000 / / 100 giorni, ad esempio); // Convalida: $ valid = hash_equals ($ _COOKIE ['rememberme'], hash_hmac ('sha256', $ username. $ _SERVER ['REMOTE_ADDR'], $ storedNonce));  

L'ovvia limitazione qui è che, se il tuo indirizzo IP (o altre informazioni) cambia, il tuo cookie è inutile. Alcuni potrebbero vederlo come una buona cosa, io lo vedo come inutilmente ostile nei confronti dell'usabilità per gli utenti Tor.

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

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


Strategia "Ricordami" sicura di Paragon Initiative

Archiviazione:

  1. Genera una stringa casuale di 9 byte da random_bytes () (PHP 7.0+ o tramite random_compat), codificala in base64 a 12. Questo sarà usato per le ricerche nel database.
  2. Genera un'altra stringa casuale, preferibilmente lunga almeno 18 byte, e ancora una volta codificala in base64 (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. Store $ lookup. $ validator nel cookie HTTP dell'utente (ad es. rememberme).

Convalida (accesso automatico):

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

Analisi della sicurezza:

  • Quali canali laterali sono mitigati?
    Più significativamente: mitiga l'impatto delle perdite di informazioni sulla tempistica sull'operazione di confronto delle stringhe utilizzata nella ricerca nel database.

    Se hai implementato un'autenticazione casuale del token ingenuo, un utente malintenzionato potrebbe inviare molte richieste fino a quando non trova un token di autenticazione valido per un utente. (Probabilmente non sarebbero in grado di selezionare quale vittima stanno impersonando.)

  • E se un utente malintenzionato potesse divulgare la tabella del database di autenticazione a lungo termine?
    Abbiamo memorizzato un hash SHA256 del $ 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 con la forza bruta produca alcun risultato. (Questo mitiga anche la necessità, ad esempio, di bcrypt.)

Questa strategia è implementata in Gatekeeper.

Grazie @scott - mi ci è voluto un po 'per capire l'importanza del timing attack qui, ma ora vedo che hai ragione! Concordo anche sugli altri punti per la maggior parte, ma non sono sicuro che sia così che intendevo comunque :-). Ma sì, l'attacco a tempo ha davvero senso, e tutta la differenza!
Questa è un'ottima risposta.L'aggiunta di un'impronta digitale del dispositivo oltre a questo aumenterà la sicurezza (ad esempio, `HTTP_USER_AGENT`)?
Non credo che aiuterebbe molto.Qui ti affidi alla sicurezza di un valore casuale per l'autenticazione challenge-response.Tutto ciò che un controllo dell'agente utente farà è far sì che le persone vengano disconnesse ogni volta che il loro browser si aggiorna automaticamente, il che li renderebbe avversi agli aggiornamenti automatici e li manterrebbe insicuri.
Puoi gentilmente spiegare perché hai bisogno di usare `base64_encode ()` invece di una funzione hash come SHA *?
9 byte casuali -> base64 -> stringa di 12 caratteri;9 byte casuali -> SHA256 -> stringa di 64 caratteri
Lo scopo è solo quello di ottenere una durata di archiviazione più breve e non necessariamente una maggiore sicurezza?
9 byte casuali sono sufficienti per questo caso d'uso, quindi non è necessario gonfiarlo fino a 64 caratteri esadecimali.Quindi sì, ma qui non c'è riduzione della sicurezza.12 caratteri nell'alfabeto base64 (o base64url) vanno bene.
@ScottArciszewski Facendo riferimento al passaggio 4, cosa succede se il passaggio 3 restituisce false?L'ho configurato per eliminare le sessioni degli utenti dal database, ma poi corri il rischio che qualcuno lo dossa.
Questo è un vettore di attacco DoS inutilmente costoso da sfruttare.È meglio eliminarlo se viene trovato un selettore valido per un prefisso non valido.
Puoi approfondire di più sull'attacco a tempo?Non penso che "$ Lookup" sia necessario.Diciamo che il cookie assomiglia a `rememberme = WBWgm2oMFxiR` e nel database viene memorizzato l'hash crittografico del valore del cookie.Capisco che le ricerche nel database con due valori dissimili impiegheranno meno tempo a fallire rispetto a due valori simili.Supponiamo che un utente malintenzionato abbia indovinato un valore di cookie il cui hash corrisponde quasi a una riga nel database.Modificare la loro ipotesi in ogni caso risulterà in un hash completamente diverso secondo en.wikipedia.org/wiki/Cryptographic_hash_function
Quindi, attraverso le informazioni sulla tempistica, non c'è modo per un utente malintenzionato di sapere che si sta avvicinando gradualmente alla ricerca di un token di autenticazione valido?
"viene memorizzato l'hash crittografico del valore del cookie" - L'attaccante non conosce anche l'hash del valore che invia?
Sì, se conoscono l'algoritmo utilizzato per eseguire l'hashing del token da archiviare nel database.È un problema però?"$ validator" in questa risposta è anche memorizzato chiaramente nel cookie, con hash nel database
Anche se l'attaccante conosce l'hash del valore che sta inviando, una "quasi corrispondenza" determinata dalle informazioni sui tempi non lo aiuta in alcun modo a ottenere una corrispondenza esatta poiché le operazioni di hash non sono reversibili no?
Se ci vuole più tempo per restituire false, allora conoscono gli N byte più a sinistra di H (spazzatura) = cosa è memorizzato nel database, che è la stessa condizione di un attacco a tempo. Certo, questo richiede ancora un lavoro di calcolo per trovare pezzi che corrispondono a più hash, ma rivela informazioni su ciò che è archiviato sul server.Un po 'di separazione qui significa ** non avere affatto questa perdita **, che è una proposta di sicurezza migliore perché gli aggressori non imparano nulla.
Quale vantaggio ha la divisione del token in due parti rispetto al controllo dell'intero hash del token nel database E POI l'esecuzione di un confronto hash sicuro con temporizzazione ridondante su due hash generati in modo sicuro creati da random_bytes e buttando via il risultato?
@ScottArciszewski Questo ha senso per me.Se l'aggressore è in grado di individuare un hash corretto nel database, può calcolarne il valore forzando il VS offline se l'hash non può essere derivato, è costretto a eseguire la bruteforce online che è facilmente rilevabile.Grazie!
#3
+5
Rory McCune
2010-11-12 04:52:52 UTC
view on stackexchange narkive permalink

Questa è una domanda interessante. Inizierò dicendo che non sono sicuro che possa essere fatto senza indebolire la sicurezza dell'applicazione, ma poi a seconda del sito che può essere considerato degno del compromesso.

Quindi un approccio potrebbe essere

Il database dello stato della sessione dovrà registrare il tempo in cui un token è impostato e la durata in cui dovrebbe essere ricordato in modo che possa confrontare il token presentato.

Quando l'utente accede all'applicazione, ha una casella di controllo che gli consente di rimanere connesso (idealmente con un intervallo di periodi di tempo selezionabili dall'utente)

Quando il token di sessione è impostato, quel periodo di tempo viene utilizzato come scadenza del cookie. Nelle visite successive il cookie verrà presentato all'applicazione e il server dovrà verificare se è ancora valido (ovvero non è stato raggiunto il periodo di scadenza per la funzione ricordami). Se il token è ancora valido, l'utente viene considerato 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 è possibile l'accesso non autorizzato.

Anche chiunque possa accedere al cookie (sia attraverso una vulnerabilità nel sito, un problema con il browser o tramite malware piantato sulla macchina) potrà per ottenere l'accesso non autorizzato al sito. Un suggerimento comune per mitigare questo problema è cercare 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 ciò causa problemi con grandi ambienti aziendali in cui un utente può entrare da un certo numero di indirizzi IP e può anche essere soggetto a spoofing.

#4
+4
Xander
2010-11-12 04:53:22 UTC
view on stackexchange narkive permalink

Non è necessario dipendere dal browser per eliminare il cookie, è necessario che il sito controlli la data di scadenza del cookie.

In effetti, perché questo sia sicuro, direi che probabilmente è quello che dovrai fare comunque, perché vorresti impostare la data di scadenza come parte del valore del cookie e crittografalo piuttosto che fare affidamento sulla proprietà di scadenza in testo normale del cookie stesso.

E vorresti davvero contrassegnare il cookie come sicuro e utilizzare un cookie secondario per la durata di una singola sessione, o proteggere l'intera sessione (non solo il processo di accesso) con SSL per impedire il cookie per evitare che vengano rubati e utilizzati da una parte non autorizzata.

#5
  0
chiborg
2013-01-04 18:55:53 UTC
view on stackexchange narkive permalink

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

  • Un cookie "ricordami" è costituito dall'ID utente, un token (grande numero casuale) e un segnalino sequenza (un altro grande numero casuale).
  • Quando un utente presenta il cookie, nel database vengono cercate queste tre informazioni.
    • Se trovato, il token della sequenza viene rigenerato, memorizzato 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 della sequenza era già stato utilizzato. Il sistema può quindi avvisare l'utente e / o eliminare tutti i token memorizzati da questo utente dal database.

Come misura di sicurezza aggiuntiva si consiglia di azioni come la modifica dei dati di accesso o il pagamento di cose richiedono 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 gettoni. Potrebbe anche contenere l'agente utente e l'indirizzo IP in modo che un utente malintenzionato debba replicarli. Tutti i token devono essere archiviati solo come hash perché funzionano fondamentalmente come password e consentirebbero a un utente malintenzionato, che ha ottenuto il database, di accedere.

Ho creato un implementazione di esempio per PHP .

Quell'implementazione di esempio non usa un CSPRNG. :(
#6
-1
Rogan Dawes
2010-11-19 13:21:42 UTC
view on stackexchange narkive permalink

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

AES ([Data di scadenza] + [salt casuale] + [Nome utente] + [Password])

Ciò consentirebbe all'utente di invalidare il cookie (se è stato compromesso) semplicemente cambiando la propria password. La crittografia consente all'operatore del sito di verificare che non sia stato manomesso (estendendo la data di scadenza, ad esempio), oltre a proteggere la password in chiaro da compromissione. Includere la password significa che un utente malintenzionato che ottiene l'accesso alla chiave AES non può ancora "coniare" token per autenticarsi come utenti arbitrari.

Lo svantaggio è quindi che il sito deve proteggere una chiave AES.

Un approccio alternativo sarebbe semplicemente quello di aggiungere due campi al database degli utenti, per un valore del cookie (valore lungo / forte / generato in modo casuale) e una data di scadenza associata. Qualsiasi utente che presenta quel cookie verrà autenticato, fino alla data di scadenza. È sufficiente assicurarsi sul lato server che operazioni come la modifica di una password rimuovano i cookie "ricordami" archiviati nel database.

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

Inoltre, questo approccio consente di fornire token "ricordami" diversi su dispositivi diversi (come l'account manager di Google), semplicemente archiviando i token in una tabella separata e consentendo più di uno ingresso per utente. Ciò rende quindi possibile tenere traccia da dove viene utilizzato ciascuno e invalidarli singolarmente.

Questa risposta non è corretta. La crittografia non consente a un operatore del sito di verificare che non sia stato manomesso. Questa è una confusione fondamentale e di base sulla distinzione tra riservatezza e autenticità / integrità.
-1 Stai proponendo di trasferire la password degli utenti finali via cavo e di salvarla sul suo disco rigido? ** No, non farlo, ** è assolutamente sbagliato anche se utilizzi la crittografia AES. Se un utente malintenzionato può ottenere un cookie, gli stai offrendo tentativi illimitati per ottenere la password degli utenti finali in testo non crittografato, ovvero ottenere le informazioni più critiche per la sicurezza. Presumo che tu intenda un hash della password all'interno dell'AES (che non è chiaro dalla tua scrittura), e anche questo non è l'ideale.
@D.W. La crittografia consente a un operatore del sito di verificare che non sia stato manomesso. Se la decrittografia risulta in spazzatura, è stata manomessa. Se risulta in dati validi, non è stato manomesso.
@Jesper Esiste una differenza insignificante tra l'archiviazione di una versione crittografata AES della password e una versione con hash della password. In entrambi i casi, l'attaccante può tentare di forzare una chiave AES casuale. L'aggiunta del passaggio aggiuntivo della forzatura bruta della password con hash in realtà non aggiunge molto, dato quanto le password siano facili da usare con la forza bruta in questi giorni. Almeno una chiave AES adeguatamente lunga dovrebbe tenerli occupati per un po '.
@Rogan, [ti sbagli] (http://security.stackexchange.com/questions/2202/lessons-learned-and-misconceptions-regarding-encryption-and-cryptology/2206#2206). Usare correttamente la crittografia è più sottile di quanto tu possa immaginare.
Attacchi di testo cifrato scelti. Ho detto abbastanza, davvero.
@JesperMortensen Per curiosità, quali inconvenienti vedresti se `ricordami-cookie = timestamp | user_id | firma` con timestamp = data di emissione, user_id = ID interno dell'utente (numero), firma =` base64 (sign (hash ('ricorda', timestamp, user_id, password))) `, password = password crittografata dell'utente recuperata dal tuo DB (già salata e con hash), sign = Elliptic Curve Digital Signature Algorithm (o qualsiasi altro algoritmo di firma forte), hash = SHA-256.Per convalidare il cookie: `verify (hash ('Remember', timestamp from cookie, user_id from cookie, password from DB), unbase64 (signature))`.


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 2.0 con cui è distribuito.
Loading...