it-swarm.dev

Come implementare in modo sicuro una funzione "Ricordami"?

Supponendo che abbiate 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 determinato periodo di tempo (diciamo 30 giorni)? Questo periodo di tempo dovrebbe essere rigorosamente applicato; vale a dire, non penso che una soluzione valida significhi 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 e strong_hash_function (password + user_specific_random_salt)
  • I moduli di accesso vengono caricati e inviati su SSL, così come tutte le 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.

57
colithium

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


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, hash quello con il nome utente e altre informazioni (ad es. ip client, nomecomputer, data/ora, cose simili) e inviarlo nel cookie. Il nonce deve essere archiviato nel database, insieme alla data di scadenza, ed entrambi devono essere controllati al ritorno del cookie.
Dovrebbe essere solo un cookie di "ricordo", NON un cookie di sessione, ovvero quando ricevi quel cookie senza una sessione, riemetti un nuovo cookie di sessione normale. Si noti inoltre che, come il cookie di sessione, questo dovrebbe essere consegnato solo su https e utilizzando gli attributi secure e httpOnly e, naturalmente, il cookie ha l'ambito corretto, ecc.

In secondo luogo, per gli utenti che sono stati ricordati, è necessario (probabilmente, a seconda della sensibilità) invocare un processo di riautenticazione per determinate operazioni sensibili. Cioè, a volte avrebbero bisogno di reinserire la password comunque, ma raramente e solo per operazioni sensibili. Questo per prevenire attacchi come CSRF ed esposizione desktop condivisa.

31
AviD

Ho scritto molto 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 debba essere, e trascura un'area in cui è necessaria un po' più di complessità.

generare e memorizzare un nonce sul lato server, l'hash con il nome utente e altre informazioni (ad es. ip client, nomecomputer, timestamp, cose simili) e inviarlo nel cookie. Il nonce deve essere archiviato nel database, insieme alla data di scadenza, ed entrambi devono essere controllati al ritorno del cookie.

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

// 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)
);

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, lo vedo inutilmente ostile verso l'usabilità per gli utenti Tor.

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

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


La strategia sicura "Ricordami" di Paragon Initiative

Conservazione:

  1. Generare una stringa casuale di 9 byte da random_bytes() (PHP 7.0+ o via random_compat ), base64 codificarla su 12. Verrà utilizzata per le 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. 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 di sicurezza:

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

    Se hai implementato un'autenticazione token casuale ingenua, 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.)

  • Cosa succede se un utente malintenzionato può perdere 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 ha un valore entropico elevato, è improbabile che la ricerca della forza bruta produca risultati. (Ciò mitiga anche la necessità, ad esempio, di bcrypt.)

Questa strategia è implementata in Gatekeeper .

10

Questa è una domanda interessante. Inizierò dicendo che non sono sicuro che possa essere fatto senza un certo indebolimento della 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 l'ora in cui un token è impostato e la durata che deve essere ricordata in modo da poter 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 che l'utente può selezionare)

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 il periodo di scadenza della funzione Ricordami non è stato raggiunto). Se il token è ancora valido, l'utente viene trattato 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 (sia attraverso una vulnerabilità nel sito, un problema del browser o attraverso malware installato sulla macchina) sarà in grado di ottenere un accesso non autorizzato al sito. Un suggerimento comune per mitigare questo è provare a legare il cookie a un indirizzo IP di origine (crittografato o memorizzato sul server ovviamente) che viene verificato quando l'utente presenta il cookie, tuttavia ciò causa problemi con ambienti aziendali di grandi dimensioni in cui un utente può venire da un numero di indirizzi IP e può anche essere suscettibile di spoofing.

5
Rory McCune

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

In effetti, affinché ciò sia sicuro, direi che probabilmente è quello che dovrai fare comunque, perché vorresti impostare la data di scadenza come parte del valore del cookie e crittografarlo anziché fare affidamento sul proprietà di scadenza in chiaro del cookie stesso.

E vorresti davvero contrassegnare il cookie come sicuro e utilizzare 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 venga rubato via cavo e utilizzato da una parte non autorizzata.

4
Xander

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

  • Un cookie "Ricordami" è costituito dall'ID utente, un token (grande numero casuale) e un token di sequenza (un altro grande numero casuale).
  • Quando un utente presenta il cookie, vengono ricercate nel database 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 era 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 ID utente e 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 devono essere archiviati come hash perché funzionano sostanzialmente come password e consentirebbero a un utente malintenzionato, che ha ottenuto il database, di accedere.

Ho creato un implementazione di esempio per PHP.

0
chiborg