"Seguir Conectado" - el mejor enfoque


Mi aplicación web utiliza sesiones para almacenar información sobre el usuario una vez que ha iniciado sesión, y para mantener esa información a medida que viajan de una página a otra dentro de la aplicación. En esta aplicación específica, estoy almacenando el user_id, first_name y last_name de la persona.

Me gustaría ofrecer una opción "Keep Me Logged In" al iniciar sesión que colocará una cookie en la máquina del usuario durante dos semanas, que reiniciará su sesión con los mismos detalles cuando regresen a la aplicación.

¿Qué es el mejor enfoque para hacer esto? No quiero almacenar su user_id en la cookie, ya que parece que eso haría que sea fácil para un usuario intentar falsificar la identidad de otro usuario.

Author: Jimbo, 2009-08-31

12 answers

OK, permítanme decir esto sin rodeos: si está poniendo datos de usuario, o cualquier cosa derivada de los datos de usuario en una cookie para este propósito, está haciendo algo mal.

Ahí. Lo dije. Ahora podemos pasar a la respuesta real.

¿Qué hay de malo con el hashing de datos de usuario, te preguntas? Bueno, todo se reduce a la superficie de exposición y la seguridad a través de la oscuridad.

Imagina por un segundo que eres un atacante. Ves una cookie criptográfica establecida para el remember-me en tu sesión. Tiene 32 caracteres de ancho. Gee. Eso puede ser un MD5...

Imaginemos también por un segundo que conocen el algoritmo que usaste. Por ejemplo:

md5(salt+username+ip+salt)

Ahora, todo lo que un atacante necesita hacer es la fuerza bruta de la "sal" (que no es realmente una sal, pero más sobre eso más adelante), y ahora puede generar todos los tokens falsos que quiera con cualquier nombre de usuario para su dirección IP! Pero forzar brutalmente una sal es difícil, ¿verdad? Absolutamente. Pero las GPU modernas son muy buenas en eso. Y a menos que uses suficiente aleatoriedad en él (que sea lo suficientemente grande), va a caer rápidamente, y con él las llaves de su castillo.

En resumen, lo único que te protege es la sal, que en realidad no te protege tanto como crees.

Pero Espera!

Todo eso fue predicado que el atacante conoce el algoritmo! Si es secreto y confuso, entonces estás a salvo, ¿verdad? INCORRECTO . Esa línea de pensamiento tiene un nombre: Seguridad a Través de Oscuridad, en la que NUNCA se debe confiar.

El Mejor Camino

La mejor manera es nunca dejar que la información de un usuario salga del servidor, excepto por el id.

Cuando el usuario inicie sesión, genere un token aleatorio grande (de 128 a 256 bits). Agregue eso a una tabla de base de datos que asigna el token al userid, y luego envíelo al cliente en la cookie.

¿Qué pasa si el atacante adivina el token aleatorio de otro usuario?

Bien, vamos a hacer algunas cuentas aquí. Estamos generando un token aleatorio de 128 bits. Eso significa que hay:

possibilities = 2^128
possibilities = 3.4 * 10^38

Ahora, para mostrar lo absurdamente grande que es ese número, imaginemos cada servidor en Internet (digamos 50,000,000 hoy) tratando de forzar brutalmente ese número a una tasa de 1,000,000,000 por segundo cada uno. En realidad sus servidores se fundirían bajo tal carga,pero vamos a jugar esto.

guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000

Entonces 50 cuatrillones de conjeturas por segundo. ¡Eso es rápido! ¿Verdad?

time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000

So 6.8 sextillón segundos...

Tratemos de reducir eso a números más amistosos.

215,626,585,489,599 years

O incluso mejor:

47917 times the age of the universe

Sí, eso es 47917 veces la edad del universo...

, Básicamente, no va a ser roto.

Así que para resumir:

El mejor enfoque que recomiendo es almacenar la cookie con tres partes.

function onLogin($user) {
    $token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
    storeTokenForUser($user, $token);
    $cookie = $user . ':' . $token;
    $mac = hash_hmac('sha256', $cookie, SECRET_KEY);
    $cookie .= ':' . $mac;
    setcookie('rememberme', $cookie);
}

Entonces, para validar:

function rememberMe() {
    $cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
    if ($cookie) {
        list ($user, $token, $mac) = explode(':', $cookie);
        if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (hash_equals($usertoken, $token)) {
            logUserIn($user);
        }
    }
}

Nota: No utilice el token o la combinación de usuario y token para busca un registro en tu base de datos. Siempre asegúrese de obtener un registro basado en el usuario y use una función de comparación segura para comparar el token obtenido después. Más sobre ataques de tiempo.

Ahora, es muy importante que el SECRET_KEY sea un secreto criptográfico (generado por algo como /dev/urandom y/o derivado de una entrada de alta entropía). Además, GenerateRandomToken() necesita ser una fuente aleatoria fuerte (mt_rand() no es lo suficientemente fuerte. Utilice una biblioteca, como RandomLib o random_compat, o mcrypt_create_iv() con DEV_URANDOM)...

El hash_equals() es para prevenir ataques de tiempo. Si utiliza una versión de PHP por debajo de PHP 5.6 la función hash_equals() no es compatible. En este caso se puede reemplazar hash_equals() con la función timingSafeCompare:

/**
 * A timing safe equals comparison
 *
 * To prevent leaking length information, it is important
 * that user input is always used as the second parameter.
 *
 * @param string $safe The internal (safe) value to be checked
 * @param string $user The user submitted (unsafe) value
 *
 * @return boolean True if the two strings are identical.
 */
function timingSafeCompare($safe, $user) {
    if (function_exists('hash_equals')) {
        return hash_equals($safe, $user); // PHP 5.6
    }
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    // mbstring.func_overload can make strlen() return invalid numbers
    // when operating on raw binary strings; force an 8bit charset here:
    if (function_exists('mb_strlen')) {
        $safeLen = mb_strlen($safe, '8bit');
        $userLen = mb_strlen($user, '8bit');
    } else {
        $safeLen = strlen($safe);
        $userLen = strlen($user);
    }

    // Set the result to the difference between the lengths
    $result = $safeLen - $userLen;

    // Note that we ALWAYS iterate over the user-supplied length
    // This is to prevent leaking length information
    for ($i = 0; $i < $userLen; $i++) {
        // Using % here is a trick to prevent notices
        // It's safe, since if the lengths are different
        // $result is already non-0
        $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
    }

    // They are only identical strings if $result is exactly 0...
    return $result === 0;
}
 627
Author: ircmaxell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-27 08:48:43

Aviso de seguridad : Basar la cookie en un hash MD5 de datos deterministas es una mala idea; es mejor usar un token aleatorio derivado de un CSPRNG. Ver respuesta de ircmaxell a esta pregunta para un enfoque más seguro.

Normalmente hago algo como esto:

  1. El usuario inicia sesión con 'keep me logged in'
  2. Crear sesión
  3. Crear una cookie llamada ALGO que contenga: md5 (salt + nombre de usuario + ip + salt) y una cookie llamada otracosa que contiene el identificador
  4. Almacenar cookie en la base de datos
  5. El usuario hace cosas y se va ----
  6. El usuario regresa, compruebe si hay algo cookie, si existe, obtenga el hash antiguo de la base de datos para ese usuario, verifique el contenido de cookie ALGO que coincida con el hash de la base de datos, que también debe coincidir con un hash recién calculado (para la ip) así: cookieHash==databaseHash==md5(salt+username+ip+salt), si lo hacen, goto 2, si no 1

Por supuesto, puede usar diferentes nombres de cookies, etc. también puede cambiar un poco el contenido de la cookie, solo asegúrese de que no se cree fácilmente. Por ejemplo, también puede crear un user_salt cuando se crea el usuario y también ponerlo en la cookie.

También puedes usar sha1 en lugar de md5 (o casi cualquier algoritmo)

 94
Author: Pim Jager,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:32

Introducción

Su título "Keep Me Logged In" - el mejor enfoque me hace difícil saber por dónde empezar porque si está buscando el mejor enfoque, entonces tendría que considerar lo siguiente:

  • Identificación
  • Seguridad

Cookies

Las cookies son vulnerables, entre las vulnerabilidades comunes de robo de cookies del navegador y los ataques de scripting entre sitios, debemos aceptar que las cookies no son seguros. Para ayudar a mejorar la seguridad, debe tener en cuenta que php setcookies tiene funcionalidad adicional como

Bool setcookie ( string $nombre [, string $value [, int $expire = 0 [, string $path [, string $dominio [, bool $secure = false [, bool $httponly = false ]]]]]] )

  • seguro (Usando conexión HTTPS)
  • httponly (Reduce identity theft through XSS attack)

Definiciones

  • Token ( cadena aleatoria impredecible de n longitud, por ejemplo. / dev / urandom)
  • Referencia (Cadena aleatoria impredecible de n longitud eg. / dev / urandom)
  • Firma (Generar un valor hash con clave utilizando el método HMAC)

Enfoque simple

Una solución simple sería:

  • El usuario ha iniciado sesión con Remember Me
  • Cookie de inicio de sesión emitida con token y firma
  • Cuando está regresando, la firma es [19]]}
  • Si la firma está bien .. luego username & token se busca en la base de datos
  • si no es válido .. volver a la página de inicio de sesión
  • Si es válido, inicie sesión automáticamente

El estudio de caso anterior resume todos los ejemplos dados en esta página, pero las desventajas son que

  • No hay manera de saber si las cookies fueron robadas
  • El atacante puede acceder a operaciones confidenciales como el cambio de contraseña o datos como personal y horneado información, etc.
  • La cookie comprometida seguiría siendo válida durante la vida útil de la cookie

Mejor Solución

Una solución mejor sería

  • El usuario ha iniciado sesión y remember me está seleccionado
  • Generar Token y firma y almacenar en cookie
  • Los tokens son aleatorios y solo son válidos para autenticación única
  • El token se reemplaza en cada visita al sitio
  • Cuando un usuario no registrado visita sitio la firma, el token y el nombre de usuario se verifican
  • Remember me login debe tener acceso limitado y no permitir la modificación de contraseña , información personal, etc.

Código de ejemplo

// Set privateKey
// This should be saved securely 
$key = 'fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282';
$key = pack("H*", $key); // They key is used in binary form

// Am Using Memecahe as Sample Database
$db = new Memcache();
$db->addserver("127.0.0.1");

try {
    // Start Remember Me
    $rememberMe = new RememberMe($key);
    $rememberMe->setDB($db); // set example database

    // Check if remember me is present
    if ($data = $rememberMe->auth()) {
        printf("Returning User %s\n", $data['user']);

        // Limit Acces Level
        // Disable Change of password and private information etc

    } else {
        // Sample user
        $user = "baba";

        // Do normal login
        $rememberMe->remember($user);
        printf("New Account %s\n", $user);
    }
} catch (Exception $e) {
    printf("#Error  %s\n", $e->getMessage());
}

Clase Utilizada

class RememberMe {
    private $key = null;
    private $db;

    function __construct($privatekey) {
        $this->key = $privatekey;
    }

    public function setDB($db) {
        $this->db = $db;
    }

    public function auth() {

        // Check if remeber me cookie is present
        if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) {
            return false;
        }

        // Decode cookie value
        if (! $cookie = @json_decode($_COOKIE["auto"], true)) {
            return false;
        }

        // Check all parameters
        if (! (isset($cookie['user']) || isset($cookie['token']) || isset($cookie['signature']))) {
            return false;
        }

        $var = $cookie['user'] . $cookie['token'];

        // Check Signature
        if (! $this->verify($var, $cookie['signature'])) {
            throw new Exception("Cokies has been tampared with");
        }

        // Check Database
        $info = $this->db->get($cookie['user']);
        if (! $info) {
            return false; // User must have deleted accout
        }

        // Check User Data
        if (! $info = json_decode($info, true)) {
            throw new Exception("User Data corrupted");
        }

        // Verify Token
        if ($info['token'] !== $cookie['token']) {
            throw new Exception("System Hijacked or User use another browser");
        }

        /**
         * Important
         * To make sure the cookie is always change
         * reset the Token information
         */

        $this->remember($info['user']);
        return $info;
    }

    public function remember($user) {
        $cookie = [
                "user" => $user,
                "token" => $this->getRand(64),
                "signature" => null
        ];
        $cookie['signature'] = $this->hash($cookie['user'] . $cookie['token']);
        $encoded = json_encode($cookie);

        // Add User to database
        $this->db->set($user, $encoded);

        /**
         * Set Cookies
         * In production enviroment Use
         * setcookie("auto", $encoded, time() + $expiration, "/~root/",
         * "example.com", 1, 1);
         */
        setcookie("auto", $encoded); // Sample
    }

    public function verify($data, $hash) {
        $rand = substr($hash, 0, 4);
        return $this->hash($data, $rand) === $hash;
    }

    private function hash($value, $rand = null) {
        $rand = $rand === null ? $this->getRand(4) : $rand;
        return $rand . bin2hex(hash_hmac('sha256', $value . $rand, $this->key, true));
    }

    private function getRand($length) {
        switch (true) {
            case function_exists("mcrypt_create_iv") :
                $r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
                break;
            case function_exists("openssl_random_pseudo_bytes") :
                $r = openssl_random_pseudo_bytes($length);
                break;
            case is_readable('/dev/urandom') : // deceze
                $r = file_get_contents('/dev/urandom', false, null, 0, $length);
                break;
            default :
                $i = 0;
                $r = "";
                while($i ++ < $length) {
                    $r .= chr(mt_rand(0, 255));
                }
                break;
        }
        return substr(bin2hex($r), 0, $length);
    }
}

Pruebas en Firefox y Chrome

introduzca la descripción de la imagen aquí

Ventaja

  • Mejor seguridad
  • Acceso limitado para el atacante
  • Cuando la cookie es robado solo es válido para acceso único
  • La próxima vez que el usuario original acceda al sitio, puede detectar y notificar automáticamente al usuario de robo

Desventaja

  • No admite la conexión persistente a través de múltiples navegadores (Móvil y Web)
  • La cookie todavía puede ser robada porque el usuario solo recibe la notificación después del siguiente inicio de sesión.

Solución rápida

  • Introducción de la aprobación sistema para cada sistema que debe tener conexión persistente
  • Utilice múltiples cookies para la autenticación

Enfoque de cookies múltiples

Cuando un atacante está a punto de robar cookes, el único enfoque es en un sitio web o dominio en particular, por ejemplo. example.com

Pero realmente se puede autenticar un usuario de 2 dominios diferentes ( example.com & fakeaddsite.com ) y hacer que parezca " Cookie de anuncio"

  • Usuario conectado a example.com con recuérdame
  • Almacenar nombre de usuario , token , referencia en cookie
  • Almacenar nombre de usuario, token, referencia en la base de datos, por ejemplo. Memcache
  • Enviar refrence id vía get e iframe a fakeaddsite.com
  • fakeaddsite.com utiliza la referencia para obtener el usuario y el token de la base de datos
  • fakeaddsite.com almacena la firma
  • Cuando un usuario devuelve obtener información de firma con iframe desde fakeaddsite.com
  • Combine los datos y haga la validación
  • ..... usted sabe el resto

Algunas personas podrían preguntarse cómo se puede utilizar 2 cookies diferentes ? Bueno, es posible, imaginen example.com = localhost y fakeaddsite.com = 192.168.1.120. Si inspecciona las cookies se vería así

introduzca la descripción de la imagen aquí

De la imagen de arriba

  • El sitio actual visitado es localhost
  • También contiene cookies establecidas desde 192.168.1.120

192.168.1.120

  • Solo acepta definido HTTP_REFERER
  • Solo acepta conexión desde REMOTE_ADDR
  • Sin JavaScript, sin contenido, pero no consisten en nada más que firmar información y agregarla o recuperarla de cookie

Ventaja

  • El 99% % de las veces que has engañado al atacante
  • Puede bloquear fácilmente la cuenta en el primer intento del atacante
  • El ataque puede ser prevenido incluso antes del siguiente inicio de sesión como los otros métodos

Desventaja

  • Solicitud múltiple al servidor solo para un solo inicio de sesión

Mejora

  • Hecho use iframe use ajax
 67
Author: Baba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-12 12:49:03

Hay dos artículos muy interesantes, que encontré mientras buscaba una solución perfecta para el problema"remember-me":

 24
Author: Stefan Gehrig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-31 06:38:23

Hice un ángulo de esta pregunta aquí, y las respuestas lo llevarán a todos los enlaces de cookies de tiempo de espera basados en tokens que necesita.

Básicamente, no almacena el ID de usuario en la cookie. Usted almacena un token de una sola vez (cadena enorme) que el usuario utiliza para recoger su antigua sesión de inicio de sesión. Luego, para que sea realmente seguro, solicita una contraseña para operaciones pesadas (como cambiar la contraseña en sí).

 6
Author: Dan Rosenstark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:33

Recomendaría el enfoque mencionado por Stefan (es decir, seguir las pautas en Mejora de las Mejores Prácticas de Cookies de Inicio de sesión Persistentes) y también recomiendo que se asegure de que sus cookies son HttpOnly cookies para que no sean accesibles para JavaScript potencialmente malicioso.

 5
Author: Walter Rumsby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-20 22:40:25

Genere un hash, tal vez con un secreto que solo usted conoce, luego guárdelo en su base de datos para que pueda ser asociado con el usuario. Debería funcionar bastante bien.

 4
Author: Jani Hartikainen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-30 21:52:54

Hilo antiguo, pero sigue siendo una preocupación válida. Me di cuenta de algunas buenas respuestas sobre la seguridad, y evitar el uso de la "seguridad a través de la oscuridad", pero los métodos técnicos reales dados no eran suficientes en mis ojos. Cosas que debo decir antes de contribuir mi método:

  • NUNCA almacene una contraseña en texto claro...¡NUNCA!
  • NUNCA almacene la contraseña con hash de un usuario en más de una ubicación en su base de datos. Su servidor backend siempre es capaz de tirar de la hash contraseña de la tabla usuarios. No es más eficiente almacenar datos redundantes en lugar de transacciones de base de datos adicionales, lo inverso es cierto.
  • Su ID de sesión debe ser único, por lo que no dos usuarios podrían nunca compartir un ID, de ahí el propósito de un ID (¿podría su número de identificación de licencia de Conducir coincidir alguna vez con otras personas? No.) Esto genera una combinación única de dos piezas, basada en 2 cuerdas únicas. La tabla de sesiones debe usar el ID como PK. Para permitir que se confíe en varios dispositivos para la firma automática, use otra tabla para dispositivos de confianza que contenga la lista de todos los dispositivos validados (consulte mi ejemplo a continuación) y se asigne con el nombre de usuario.
  • No sirve para nada hash datos conocidos en una cookie, la cookie puede ser copiada. Lo que estamos buscando es un dispositivo de usuario que cumpla con la normativa para proporcionar información auténtica que no se puede obtener sin que un atacante comprometa la máquina del usuario (de nuevo, ver mi ejemplo). Esto significaría, sin embargo, que un usuario legítimo que prohíbe la información estática de su máquina (es decir, dirección MAC, nombre de host del dispositivo, agente de usuario si está restringido por el navegador, etc.) de ser coherente (o parodias en primer lugar) no será capaz de utilizar esta función. Pero si esto es una preocupación, considere el hecho de que está ofreciendo auto-signin a los usuarios que identificarse de manera única, así que si se niegan a ser conocidos al suplantar su MAC, suplantar su agente de usuario, suplantar/cambiar su nombre de host, esconderse detrás de proxies, etc., entonces no son identificables, y nunca deben autenticarse para un servicio automático. Si desea esto, debe buscar el acceso a la tarjeta inteligente incluido con el software del lado del cliente que establece la identidad del dispositivo que se está utilizando.

Dicho todo, hay dos grandes maneras de tener auto-signin en su sistema.

Primero, la manera barata y fácil que pone todo en alguien más. Si haces que tu sitio soporte iniciar sesión con, digamos, tu cuenta de Google+, probablemente tener un botón optimizado de Google + que se conectará al usuario si ya ha iniciado sesión en Google (lo hice aquí para responder a esta pregunta, ya que siempre estoy conectado a Google). Si desea que el usuario inicie sesión automáticamente si ya ha iniciado sesión con un autenticador de confianza y compatible, y marcó la casilla para hacerlo, haga que sus scripts del lado del cliente realicen el código detrás del botón correspondiente 'iniciar sesión con' antes de cargar, solo asegúrese de que el servidor almacene un ID único en un tabla de firma automática que tiene el nombre de usuario, el ID de sesión y el autenticador utilizado para el usuario. Dado que estos métodos de inicio de sesión utilizan AJAX, está esperando una respuesta de todos modos, y esa respuesta es una respuesta validada o un rechazo. Si obtiene una respuesta validada, utilícela de forma normal y, a continuación, continúe cargando el usuario que ha iniciado sesión de forma normal. De lo contrario, el inicio de sesión falló, pero no se lo diga al usuario, simplemente continúe como no ha iniciado sesión, se dará cuenta. Esto es para evitar que un atacante que robó cookies (o los falsificó en un intento de escalar privilegios) de aprender que el usuario inicia sesión automáticamente en el sitio.

Esto es barato, y también podría ser considerado sucio por algunos porque trata de validar su potencial ya firmado en sí mismo con lugares como Google y Facebook, sin siquiera decirle. Sin embargo, no se debe usar en usuarios que no hayan solicitado iniciar sesión automáticamente en su sitio, y este método en particular es solo para autenticación externa, como con Google+ o FB.

Debido a que se usó un autenticador externo para decirle al servidor detrás de escena si un usuario fue validado o no, un atacante no puede obtener nada más que un ID único, que es inútil por sí solo. Voy a elaborar:

  • El usuario 'joe' visita el sitio por primera vez, ID de sesión colocado en la cookie 'sesión'.
  • El usuario 'joe' inicia sesión, aumenta los privilegios, obtiene un nuevo ID de sesión y renueva la cookie 'session'.
  • El usuario 'joe' elige auto-signin usando google+, obtiene un ID único colocado en la cookie 'keepmesignedin'.
  • El usuario 'joe' hace que Google los mantenga conectados, lo que permite que tu sitio firme automáticamente al usuario usando Google en tu backend.
  • El atacante intenta sistemáticamente ID únicos para 'keepmesignedin' (esto es conocimiento público entregado a cada usuario), y no se inicia sesión en ningún otro lugar; intenta ID único dado a 'joe'.
  • Servidor recibe ID único para 'joe', saca partido en la base de datos para una cuenta de Google+.
  • El servidor envía al Atacante a iniciar sesión página que ejecuta una solicitud AJAX a Google para iniciar sesión.
  • El servidor de Google recibe una solicitud, usa su API para ver que el atacante no está conectado actualmente.
  • Google envía la respuesta de que no hay ningún usuario registrado actualmente a través de esta conexión.
  • La página del atacante recibe respuesta, script redirige automáticamente a la página de inicio de sesión con un valor POST codificado en la url.
  • La página de inicio de sesión obtiene el valor POST, envía la cookie para 'keepmesignedin' a un valor vacío y una fecha válida hasta 1-1-1970 para disuadir un intento automático, haciendo que el navegador del atacante simplemente elimine la cookie.
  • El atacante recibe una página de inicio de sesión normal por primera vez.

Pase lo que pase, incluso si un atacante usa un ID que no existe, el intento debe fallar en todos los intentos excepto cuando se recibe una respuesta validada.

Este método puede y debe usarse junto con su autenticador interno para aquellos que inician sesión en su sitio utilizando un autenticador.

=========

Ahora, para su propio sistema de autenticación que puede firmar automáticamente a los usuarios, así es como lo hago:

DB tiene algunas tablas:

TABLE users:
UID - auto increment, PK
username - varchar(255), unique, indexed, NOT NULL
password_hash - varchar(255), NOT NULL
...

Tenga en cuenta que el nombre de usuario es capaz de tener 255 caracteres de largo. Mi programa de servidor limita los nombres de usuario en mi sistema a 32 caracteres, pero los autenticadores externos pueden tener nombres de usuario con su @dominio.tld ser más grande que eso, por lo que solo apoyo la longitud máxima de una dirección de correo electrónico para el máximo compatibilidad.

TABLE sessions:
session_id - varchar(?), PK
session_token - varchar(?), NOT NULL
session_data - MediumText, NOT NULL

Tenga en cuenta que no hay ningún campo de usuario en esta tabla, porque el nombre de usuario, cuando se inicia sesión, está en los datos de la sesión, y el programa no permite datos nulos. El session_id y el session_token se pueden generar utilizando hashes md5 aleatorios, hashes sha1/128/256, sellos de fecha y hora con cadenas aleatorias agregadas a ellos y luego hasheados, o lo que desee, pero la entropía de su salida debe permanecer tan alta como tolerable para mitigar los ataques de fuerza bruta el suelo y todos los hashes generados por su clase de sesión deben verificarse en busca de coincidencias en la tabla de sesiones antes de intentar agregarlos.

TABLE autologin:
UID - auto increment, PK
username - varchar(255), NOT NULL, allow duplicates
hostname - varchar(255), NOT NULL, allow duplicates
mac_address - char(23), NOT NULL, unique
token - varchar(?), NOT NULL, allow duplicates
expires - datetime code

Las direcciones MAC por su naturaleza se supone que son ÚNICAS, por lo tanto tiene sentido que cada entrada tenga un valor único. Los nombres de host, por otro lado, podrían duplicarse en redes separadas legítimamente. ¿Cuántas personas usan "Home-PC" como uno de sus nombres de computadora? El nombre de usuario se toma de los datos de la sesión por el servidor, por lo que manipularlo es imposible. En cuanto al token, el mismo método para generar tokens de sesión para páginas debe usarse para generar tokens en cookies para el auto-signin del usuario. Por último, se agrega el código datetime para cuando el usuario necesitaría revalidar sus credenciales. Actualice esta fecha y hora en el inicio de sesión del usuario manteniéndola dentro de unos pocos días, o fuerce que expire independientemente del último inicio de sesión manteniéndola solo durante un mes más o menos, lo que dicte su diseño.

Esto evita que alguien falsificar sistemáticamente el MAC y el nombre de host para un usuario que conocen inicia sesión automáticamente. NUNCA hacer que el usuario guarde una cookie con su contraseña, texto claro o de otra manera. Haga que el token se regenere en cada navegación de página, tal como lo haría con el token de sesión. Esto reduce enormemente la probabilidad de que un atacante pueda obtener una cookie token válida y usarla para iniciar sesión. Algunas personas tratarán de decir que un atacante podría robar las cookies de la víctima y hacer una repetición de la sesión ataque para iniciar sesión. Si un atacante pudiera robar las cookies (lo cual es posible), ciertamente habría comprometido todo el dispositivo, lo que significa que podría usar el dispositivo para iniciar sesión de todos modos, lo que frustra el propósito de robar cookies por completo. Siempre y cuando su sitio se ejecute sobre HTTPS (que debería cuando se trata de contraseñas, números CC u otros sistemas de inicio de sesión), ha brindado toda la protección al usuario que puede dentro de un navegador.

Una cosa a tener en cuenta: los datos de la sesión no debe caducar si utiliza auto-signin. Puede caducar la capacidad de continuar la sesión falsamente, pero la validación en el sistema debe reanudar los datos de la sesión si se trata de datos persistentes que se espera que continúen entre sesiones. Si desea datos de sesión persistentes y no persistentes, use otra tabla para datos de sesión persistentes con el nombre de usuario como PK y haga que el servidor los recupere como lo haría con los datos de sesión normales, simplemente use otra variable.

Una vez que un login tiene se ha logrado de esta manera, el servidor aún debe validar la sesión. Aquí es donde puede codificar las expectativas de los sistemas robados o comprometidos; los patrones y otros resultados esperados de los inicios de sesión a los datos de la sesión a menudo pueden llevar a la conclusión de que un sistema fue secuestrado o se falsificaron cookies para obtener acceso. Aquí es donde su tecnología ISS puede poner reglas que activarían un bloqueo de cuenta o la eliminación automática de un usuario del sistema de auto-signin, manteniendo a los atacantes fuera el tiempo suficiente para el usuario para determinar cómo el atacante tuvo éxito y cómo cortarlos.

Como nota de cierre, asegúrese de que cualquier intento de recuperación, cambios de contraseña o errores de inicio de sesión más allá del umbral den como resultado que el inicio de sesión automático se deshabilite hasta que el usuario valide correctamente y reconozca que esto ha ocurrido.

Me disculpo si alguien esperaba que el código fuera dado en mi respuesta, eso no va a suceder aquí. Diré que uso PHP, jQuery y AJAX para ejecutar mis sitios, y NUNCA uso Windows como un servidor... nunca.

 3
Author: user253780,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-07 22:31:29

Mi solución es así. No es 100% a prueba de balas pero creo que te ahorrará para la mayoría de los casos.

Cuando el usuario haya iniciado sesión correctamente, cree una cadena con esta información:

$data = (SALT + ":" + hash(User Agent) + ":" + username 
                     + ":" + LoginTimestamp + ":"+ SALT)

Cifrar $data, establecer tipo a HttpOnly y establecer cookie.

Cuando el usuario regrese a su sitio, Realice estos pasos:

  1. Obtener datos de cookies. Eliminar los personajes peligrosos dentro de cookie. Explótalo con el carácter :.
  2. Comprobar la validez. Si la cookie es más de X días luego redirigir al usuario a la página de inicio de sesión.
  3. Si la cookie no es antigua; Obtenga la última hora de cambio de contraseña de la base de datos. Si la contraseña se cambia después del último inicio de sesión del usuario, redirija al usuario a la página de inicio de sesión.
  4. Si pass no se ha cambiado recientemente; Obtenga el agente del navegador actual del usuario. Compruebe si (currentUserAgentHash = = cookieUserAgentHash). SI los agentes son los mismos ir al siguiente paso, de lo contrario redirigir a la página de inicio de sesión.
  5. Si todos los pasos pasados autorizan con éxito nombre de usuario.

Si el usuario cierra sesión, elimine esta cookie. Cree una nueva cookie si el usuario vuelve a iniciar sesión.

 2
Author: trante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-04 00:06:21

No entiendo el concepto de almacenar cosas cifradas en una cookie cuando es la versión cifrada de la misma la que necesita para hacer su piratería. Si me estoy perdiendo algo, por favor comente.

Estoy pensando en tomar este enfoque para 'Recordarme'. Si puede ver algún problema, por favor comente.

  1. Cree una tabla para almacenar los datos de "Recordarme" por separado de la tabla de usuarios para que pueda iniciar sesión desde varios dispositivos.

  2. Al iniciar sesión correctamente (con Recuérdame marcado):

    A) Generar una cadena aleatoria única para ser utilizada como ID de usuario en esta máquina: bigUserID

    B) Generar una cadena aleatoria única: bigKey

    C) Almacenar una cookie: bigUserID: bigKey

    D) En la tabla" Remember Me", agregue un registro con: userId, IP Address, bigUserID, bigKey

  3. Si intenta acceder a algo que requiere inicio de sesión:

    A) Compruebe la cookie y busque bigUserID & bigKey con una IP coincidente dirección

    B) Si lo encuentra, ingrese a la persona pero establezca una bandera en la tabla de usuarios "soft login" para que para cualquier operación peligrosa, pueda solicitar un inicio de sesión completo.

  4. Al cerrar sesión, Marque todos los registros de" Recordarme " para ese usuario como caducados.

Las únicas vulnerabilidades que puedo ver son;

  • podría obtener el portátil de alguien y falsificar su dirección IP con la cookie.
  • podría falsificar una dirección IP diferente cada vez y supongo que todo el asunto-pero con dos grandes cuerdas para que coincida, eso sería...haciendo un cálculo similar al anterior...No tengo idea...enormes probabilidades?
 2
Author: Enigma Plus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-21 11:07:24

Leí todas las respuestas y todavía me resultaba difícil extraer lo que se suponía que debía hacer. Si una imagen vale 1k palabras espero que esto ayude a otros a implementar un almacenamiento persistente seguro basado en las Mejores Prácticas de Barry Jaspan Cookie de Inicio de Sesión Persistente Mejorada

introduzca la descripción de la imagen aquí

Si tiene preguntas, comentarios o sugerencias, intentaré actualizar el diagrama para reflejar al novato que intenta implementar un inicio de sesión seguro y persistente.

 2
Author: Josh Woodcock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-28 13:29:30

La implementación de una función "Keep Me Logged In" significa que necesita definir exactamente lo que eso significará para el usuario. En el caso más simple, usaría eso para significar que la sesión tiene un tiempo de espera mucho más largo: 2 días (digamos) en lugar de 2 horas. Para hacer eso, necesitará su propio almacenamiento de sesión, probablemente en una base de datos, para que pueda establecer tiempos de caducidad personalizados para los datos de la sesión. Luego, debe asegurarse de establecer una cookie que se quede durante unos días (o más), en lugar de caducar cuando se cierren navegador.

Puedo oírte preguntar "¿ por qué 2 días? ¿por qué no 2 semanas?". Esto se debe a que el uso de una sesión en PHP automáticamente empujará la caducidad hacia atrás. Esto se debe a que la expiración de una sesión en PHP es en realidad un tiempo de espera inactivo.

Ahora, dicho esto, probablemente implementaría un valor de tiempo de espera más difícil que almaceno en la sesión en sí, y a las 2 semanas más o menos, y agregaría código para ver eso y para invalidar por la fuerza la sesión. O al menos cerrarlos. Esto significará que el usuario se le pedirá que inicie sesión periódicamente. Yahoo! hace esto.

 0
Author: staticsan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-31 06:00:10