it-swarm-es.com

¿Cómo implementar de forma segura una función "Recordarme"?

Suponiendo que ya tiene un sitio web que implementa todos los elementos de inicio de sesión estándar, ¿cuál es la forma correcta y más segura de permitir que los usuarios inicien sesión automáticamente durante un cierto período de tiempo (digamos 30 días)? Este período de tiempo debe cumplirse estrictamente; es decir, no creo que una solución válida sea darle una fecha de vencimiento a una cookie y esperar que el navegador del usuario la elimine de manera oportuna.

Detalles del sistema de inicio de sesión existente:

  • Un usuario debe ingresar un nombre de usuario y contraseña
  • La base de datos almacena nombres de usuario, así como strong_hash_function (contraseña + user_specific_random_salt)
  • Los formularios de inicio de sesión se cargan y envían a través de SSL, al igual que todas las páginas posteriores

Existen muchos ejemplos y muchos con obvios defectos de seguridad. Usemos PHP como idioma de destino pero los conceptos deberían ser aplicables a cualquier idioma.

57
colithium

Mi respuesta es incompleta y tiene un defecto no trivial, en algunas situaciones, por favor vea @ Respuesta de Scott en su lugar.


Hay dos partes en mi respuesta:

Primero, suponiendo que su modelo de amenaza no se preocupe por la exposición de las cookies del lado del cliente , puede generar y almacenar un nonce en el lado del servidor, hash eso con el nombre de usuario y otra información (por ejemplo, IP del cliente, nombre del equipo, marca de tiempo, cosas similares) y envíelo en la cookie. El nonce debe almacenarse en la base de datos, junto con la fecha de caducidad, y ambos deben verificarse cuando vuelva la cookie.
Esto debería ser solo una cookie de "recuerdo", NO una cookie de sesión, es decir, cuando obtenga esa cookie sin una sesión, vuelva a emitir una nueva cookie de sesión regular. También tenga en cuenta que, al igual que la cookie de sesión, esta debe entregarse solo a través de https, y utilizando los atributos secure y httpOnly, y, por supuesto, tener la cookie con el alcance adecuado, etc.

En segundo lugar, para los usuarios que fueron recordados, debería (probablemente, dependiendo de la sensibilidad) invocar un proceso de reautenticación para ciertas operaciones sensibles. Es decir, a veces tendrían que volver a ingresar su contraseña de todos modos, pero rara vez, y solo para operaciones confidenciales. Esto es para evitar ataques como CSRF y la exposición de escritorio compartida.

31
AviD

He escrito extensamente sobre inicio de sesión seguro y casillas de verificación "recordarme" . La respuesta aceptada no es incorrecta, pero diría que es un poco más complicado en un aspecto de lo que debe ser, y descuida un área donde se necesita un poco más de complejidad.

generar y almacenar un nonce en el lado del servidor, mezclarlo con el nombre de usuario y otra información (por ejemplo, IP del cliente, nombre del equipo, marca de tiempo, cosas similares) y enviarlo en la cookie. El nonce debe almacenarse en la base de datos, junto con la fecha de caducidad, y ambos deben verificarse cuando vuelva la cookie.

Entonces, una implementación que no exponga ninguna información al cliente podría verse así ...

// 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 limitación obvia aquí es que, si su dirección IP (u otra información) cambia, su cookie es inútil. Algunos podrían ver esto como algo bueno, lo veo innecesariamente hostil hacia la usabilidad para los usuarios de Tor.

Ciertamente, puede interpretar que la cita anterior significa algo diferente, como el token web JSON de un hombre pobre, también. Sin embargo, las cookies HTTP están limitadas a 4 KiB por dominio. El espacio es escaso.

Otro problema: ¿cuánta información pierde su consulta de base de datos sobre su aplicación? (Sí, estoy hablando de canales laterales).


Estrategia segura de "Recordarme" de la Iniciativa Paragon

Almacenamiento:

  1. Genere una cadena aleatoria de 9 bytes a partir de random_bytes() (PHP 7.0+ o vía random_compat ), codifique en base64 a 12. Esto se usará para búsquedas en la base de datos.
  2. Genere otra cadena aleatoria, preferiblemente de al menos 18 bytes de longitud, y una vez más codifique en base64 (a 24+). Esto se usará realmente para la autenticación.
  3. Almacenar $lookup Y hash('sha256, $validator) en la base de datos; por supuesto asociado con una cuenta de usuario específica.
  4. Almacene $lookup . $validator En la cookie HTTP del usuario (por ejemplo, rememberme).

Validación (inicio de sesión automático):

  1. Divida la cookie en $lookup Y $validator.
  2. Realice una búsqueda en la base de datos basada en $lookup; está bien si hay un canal lateral de sincronización aquí.
  3. Marque hash_equals($row['hashedValdator'], hash('sha256', $validator)).
  4. Si el paso 3 devuelve TRUE, asocie la sesión actual con la cuenta de usuario adecuada.

Análisis de seguridad:

  • ¿Qué canales laterales se mitigan?
    Lo más significativo: mitiga el impacto de las fugas de información de tiempo en la operación de comparación de cadenas utilizada en la búsqueda de la base de datos.

    Si implementó una autenticación de token aleatoria ingenua, un atacante podría enviar muchas solicitudes hasta que encuentre un token de autenticación válido para un usuario. (Probablemente no podrían seleccionar a qué víctima se están haciendo pasar).

  • ¿Qué sucede si un atacante puede filtrar la tabla de la base de datos de autenticación a largo plazo?
    Almacenamos un hash SHA256 del $validator En la base de datos, pero el texto sin formato para el hash se almacena en la cookie. Dado que la entrada es un valor de entropía alto, es poco probable que la búsqueda de fuerza bruta produzca algún resultado. (Esto también mitiga la necesidad de, por ejemplo, bcrypt).

Esta estrategia se implementa en Gatekeeper .

10

Esa es una pregunta interesante. Comenzaré diciendo que no estoy seguro de que se pueda hacer sin un debilitamiento de la seguridad de la aplicación, pero luego, dependiendo del sitio que pueda considerarse que vale la pena.

Entonces, un enfoque podría ser

La base de datos del estado de la sesión necesitará registrar el tiempo en que se establece un token y la duración en que debe recordarse para poder comparar el token presentado.

Cuando el usuario inicia sesión en la aplicación, tiene una casilla de verificación que le permite permanecer conectado (idealmente con un rango de períodos de tiempo para que el usuario seleccione)

Cuando se establece el token de sesión, ese período de tiempo se utiliza como vencimiento de la cookie. En las visitas posteriores, la cookie se presentará a la aplicación y el servidor deberá verificar si aún es válida (es decir, no se ha alcanzado el período de vencimiento de la función recordarme). Si el token sigue siendo válido, el usuario se trata como autenticado, de lo contrario, el token se borra y el usuario se redirige a la pantalla de inicio de sesión.

Problemas con este enfoque. Los navegadores compartidos significarían que es posible el acceso no autorizado.

Además, cualquier persona que pueda acceder a la cookie (ya sea a través de una vulnerabilidad en el sitio, un problema del navegador o mediante malware instalado en la máquina) podrá obtener acceso no autorizado al sitio. Una sugerencia común para mitigar esto es tratar de vincular la cookie a una dirección IP de origen (encriptada o almacenada en el servidor, por supuesto) que se verifica cuando el usuario presenta la cookie, sin embargo, esto causa problemas con entornos corporativos grandes donde puede venir un usuario de varias direcciones IP, y también pueden ser susceptibles de falsificación.

5
Rory McCune

No tiene que depender del navegador para eliminar la cookie, debe hacer que el sitio verifique la fecha de vencimiento de la cookie.

De hecho, para que esto sea seguro, diría que probablemente sea lo que tendrá que hacer de todos modos, ya que desearía establecer la fecha de vencimiento como parte del valor de la cookie y cifrarla en lugar de confiar en propiedad de caducidad de texto sin formato de la propia cookie.

Y realmente querrá marcar la cookie como segura y usar una cookie secundaria durante la duración de una sesión individual, o proteger toda la sesión (no solo el proceso de inicio de sesión) con SSL para evitar que la cookie sea robada del cable y utilizado por una parte no autorizada.

4
Xander

Este artículo describe un enfoque que defiende contra el robo de cookies y adivinar las ID de sesión:

  • Una cookie "recordarme" consiste en la identificación de usuario, un token (gran número aleatorio) y un token de secuencia (otro gran número aleatorio).
  • Cuando un usuario presenta la cookie, se busca en la base de datos estos tres datos.
    • Si se encuentra, el token de secuencia se regenera, se almacena en la base de datos y se envía al usuario
    • Si solo se encuentran el nombre de usuario y el token, se presume que otra persona ha robado la cookie porque el token de secuencia ya se usó. El sistema puede advertir al usuario y/o eliminar todos los tokens almacenados de este usuario de la base de datos.

Como medida de seguridad adicional, se aconseja que acciones críticas como cambiar los datos de inicio de sesión o pagar por las cosas soliciten la contraseña, si el usuario solo se autenticó con la cookie "recordarme".

La tabla de la base de datos que contiene el identificador de usuario y los tokens también contiene la fecha de vencimiento de los tokens. También podría contener el agente de usuario y la dirección IP, por lo que un atacante también debe replicarlos. Todos los tokens solo deben almacenarse como hashes porque básicamente funcionan como contraseñas y permitirían que un atacante, que obtuvo la base de datos, inicie sesión.

He creado un implementación de muestra para PHP.

0
chiborg