2010-06-27 16 views
34

vorrei aggiungere un'opzione casella di controllo "ricordati di me" prima di accederePHP: Ricordati di me (cookie permanenti)

Qual è il modo migliore per archiviare in modo sicuro un cookie nel browser dell'utente.?

Per esempio, Facebook hanno il loro casella di controllo "ricordati di me", in modo che ogni volta che si entra facebook.com si è già connessi a.

mio login corrente utilizza le sessioni semplici.

+0

Si può dare un'occhiata a https://github.com/delight-im/PHP-Auth e la sua fonte per vedere come implementare una funzione * secure * "ricordami". Fondamentalmente, basta memorizzare una stringa * molto lunga * (cioè molta entropia) di dati casuali in un cookie. Quando l'utente visita la tua pagina, controlla quel "token" sul tuo database dove tieni traccia di questi token. Se il token è valido, autenticare l'utente. – caw

risposta

34

Questa domanda viene posta molto, ecco alcuni link per voi.

Ci

anche alcuni grandi risorse raccolte insieme nella risposta a questa domanda: The Definitive Guide To Website Authentication

+2

Soluzione migliore: [Implementazione dell'autenticazione utente sicura nelle applicazioni PHP con persistenza a lungo termine] (https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2) (2015) –

32

Aggiornamento (2017/08/13): Per capire il motivo per cui ci stiamo separando selector e token, invece di utilizzare un token, si prega di leggere this article about splitting tokens to prevent timing attacks su query SELECT.

ho intenzione di estrarre la strategia delineata in questo post del blog about secure long-term authentication dal momento che copre un sacco di terra e noi siamo interessati solo alla parte "remember me".

Preambolo - Struttura del database

Vogliamo un tavolo separato dalla tavola dei nostri utenti che assomiglia a questo (MySQL):

CREATE TABLE `auth_tokens` (
    `id` integer(11) not null UNSIGNED AUTO_INCREMENT, 
    `selector` char(12), 
    `token` char(64), 
    `userid` integer(11) not null UNSIGNED, 
    `expires` datetime, 
    PRIMARY KEY (`id`) 
); 

Le cose importanti sono che selector e token sono campi separati.

dopo l'accesso

Se non si dispone di random_bytes(), basta prendere una copia di random_compat.

if ($login->success && $login->rememberMe) { // However you implement it 
    $selector = base64_encode(random_bytes(9)); 
    $authenticator = random_bytes(33); 

    setcookie(
     'remember', 
     $selector.':'.base64_encode($authenticator), 
     time() + 864000, 
     '/', 
     'yourdomain.com', 
     true, // TLS-only 
     true // http-only 
    ); 

    $database->exec(
     "INSERT INTO auth_tokens (selector, token, userid, expires) VALUES (?, ?, ?, ?)", 
     [ 
      $selector, 
      hash('sha256', $authenticator), 
      $login->userId, 
      date('Y-m-d\TH:i:s', time() + 864000) 
     ] 
    ); 
} 

Ri-Autenticazione On caricamento della pagina

if (empty($_SESSION['userid']) && !empty($_COOKIE['remember'])) { 
    list($selector, $authenticator) = explode(':', $_COOKIE['remember']); 

    $row = $database->selectRow(
     "SELECT * FROM auth_tokens WHERE selector = ?", 
     [ 
      $selector 
     ] 
    ); 

    if (hash_equals($row['token'], hash('sha256', base64_decode($authenticator)))) { 
     $_SESSION['userid'] = $row['userid']; 
     // Then regenerate login token as above 
    } 
} 

dettagli

Usiamo 9 byte di dati casuali (codifica Base64 a 12 caratteri) per il nostro selettore. Questo fornisce 72 bit di spazio delle chiavi e quindi 2 bit di resistenza collisione (attacco del compleanno), che è più grande della nostra capacità di stoccaggio (integer(11) UNSIGNED) per un fattore di 16.

Usiamo 33 byte (264 bit) di casualità per il nostro vero autenticatore.Questo dovrebbe essere imprevedibile in tutti gli scenari pratici.

Memorizziamo un hash SHA256 dell'autenticatore nel database. Ciò riduce il rischio di impersonificazione degli utenti in seguito a perdite di informazioni.

Ricalcolo dell'hash SHA256 del valore di autenticazione memorizzato nel cookie dell'utente, quindi confrontarlo con l'hash SHA256 memorizzato utilizzando hash_equals() per prevenire attacchi di temporizzazione.

Abbiamo separato il selettore dall'autenticatore perché le ricerche DB non sono costanti. Ciò elimina il potenziale impatto delle perdite temporali sulle ricerche senza causare un drastico calo delle prestazioni.

+1

Ottimo lavoro. Vale la pena aggiungere il controllo di scadenza nel codice? Rigenera il token altrove nel tuo codice (ad esempio, caricamento di 1 su 10 pagine)? – rybo111

+0

Argh! Questo sembra fantastico e tutto, ma non posso ottenere 'hash_equals' per restituire true usando una di queste funzioni personalizzate: https://php.net/hash_equals#115664 – rybo111

+0

Se hai bisogno di un'implementazione in-PHP: https: // github. com/sarciszewski/php-future –

Problemi correlati