sesiones - Sistema de inicio de sesión de PHP: Recordarme(cookie persistente)
pasar variables de session entre paginas php (2)
Esta pregunta ya tiene una respuesta aquí:
- "Mantenerme conectado": el mejor enfoque 12 respuestas
Me gustaría agregar una opción de casilla de verificación "recordarme" antes de iniciar sesión.
¿Cuál es la mejor forma de almacenar de forma segura una cookie en el navegador del usuario?
Por ejemplo, Facebook tiene su casilla de "recordarme" para que cada vez que ingrese a Facebook.com ya esté conectado.
Mi inicio de sesión actual usa sesiones simples.
Actualización (13/08/2017) : para entender por qué estamos separando
selector
ytoken
, en lugar de simplemente usar untoken
, lea este artículo sobre cómo dividir tokens para evitar ataques de tiempo en consultas SELECT.
Voy a extraer la estrategia descrita en esta publicación de blog sobre la autenticación segura a largo plazo, ya que abarca mucho terreno y solo nos interesa la parte "recordarme" .
Preámbulo - Estructura de la base de datos
Queremos una tabla separada de la tabla de nuestros usuarios que se vea así (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`)
);
Lo importante aquí es que el selector
y el token
son campos separados.
Después de iniciar sesión
Si no tiene random_bytes()
, simplemente tome una copia de 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)
]
);
}
Re-autenticando en la carga de la página
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
}
}
Detalles
Usamos 9 bytes de datos aleatorios (base64 codificados para 12 caracteres) para nuestro selector. Esto proporciona 72 bits de espacio de teclado y, por lo tanto, 2 36 bits de resistencia a la colisión (ataques de cumpleaños), que es mayor que nuestra capacidad de almacenamiento ( integer(11) UNSIGNED
) por un factor de 16.
Usamos 33 bytes (264 bits) de aleatoriedad para nuestro autenticador real. Esto debería ser impredecible en todos los escenarios prácticos.
Almacenamos un hash SHA256 del autenticador en la base de datos. Esto mitiga el riesgo de suplantación de usuario después de filtraciones de información.
Volvemos a calcular el hash SHA256 del valor del autenticador almacenado en la cookie del usuario y luego lo comparamos con el hash SHA256 almacenado utilizando hash_equals()
para evitar ataques de temporización.
Separamos el selector del autenticador porque las búsquedas DB no son de tiempo constante. Esto elimina el impacto potencial de las filtraciones de tiempo en las búsquedas sin causar un golpe de rendimiento drástico.
Esta pregunta se hace mucho, aquí hay algunos enlaces para ti.
- La mejor práctica para implementar Secure "Remember Me"
- "Recordarme en esta computadora" - ¿Cómo debería funcionar?
- "Mantenerme conectado": el mejor enfoque
También hay algunos recursos geniales reunidos en la respuesta a esta pregunta: La guía definitiva para la autenticación del sitio web