web-dev-qa-db-fra.com

"Keep Me Logged In" - la meilleure approche

Mon application Web utilise des sessions pour stocker des informations sur l'utilisateur une fois qu'il est connecté et pour conserver ces informations lorsqu'il se déplace de page en page dans l'application. Dans cette application spécifique, je stocke les user_id, first_name et last_name de la personne. 

J'aimerais proposer une option "Keep Me Logged In" lors de la connexion, qui placera un cookie sur la machine de l'utilisateur pendant deux semaines, qui redémarrera sa session avec les mêmes informations à son retour à l'application.

Quelle est la meilleure approche pour faire cela? Je ne souhaite pas stocker leur user_id dans le cookie, car il semblerait que cela faciliterait la tâche à un utilisateur qui tenterait de forger l'identité d'un autre utilisateur.

229
Matthew

OK, permettez-moi de dire ceci sans ambages: si vous insérez des données utilisateur ou tout élément dérivé de données utilisateur dans un cookie à cette fin, vous faites quelque chose de mal.

Là. Je l'ai dit. Nous pouvons maintenant passer à la réponse réelle.

Quel est le problème avec le hachage des données utilisateur, vous demandez? Eh bien, cela dépend de la surface d'exposition et de la sécurité par l'obscurité.

Imaginez une seconde que vous êtes un attaquant. Vous voyez un cookie cryptographique défini pour le souvenir de moi lors de votre session. C'est 32 caractères de large. Gee. C'est peut-être un MD5 ...

Imaginons aussi pendant une seconde qu'ils connaissent l'algorithme que vous avez utilisé. Par exemple:

md5(salt+username+ip+salt)

Maintenant, tout ce qu'un attaquant doit faire est de forcer brutalement le "sel" (ce qui n'est pas vraiment un sel, mais plus sur cela plus tard), et il peut maintenant générer tous les faux jetons qu'il veut avec n'importe quel nom d'utilisateur pour son adresse IP! Mais forcer un sel est difficile, non? Absolument. Mais les GPU des temps modernes y sont extrêmement bons. Et à moins que vous n'utilisiez suffisamment d'aléatoire dans celui-ci (rendez-le suffisamment grand), il va tomber rapidement et avec lui les clés de votre château.

En bref, la seule chose qui vous protège est le sel, qui ne vous protège pas vraiment autant que vous ne le pensez.

Mais attendez!

Tout cela était fondé sur le fait que l'attaquant connaissait l'algorithme! Si c'est secret et déroutant, alors vous êtes en sécurité, non? FAUX. Cette ligne de pensée a un nom: La sécurité par l'obscurité , sur laquelle on devrait NE JAMAIS .

La meilleure façon

Le meilleur moyen est de ne jamais laisser les informations d'un utilisateur quitter le serveur, à l'exception de l'id.

Lorsque l'utilisateur se connecte, générez un jeton aléatoire volumineux (128 à 256 bits). Ajoutez cela à une table de base de données qui mappe le jeton sur l'ID utilisateur, puis envoyez-le au client dans le cookie.

Et si l'attaquant devine le jeton aléatoire d'un autre utilisateur?

Bien, faisons des calculs ici. Nous générons un jeton aléatoire de 128 bits. Cela signifie qu'il y a:

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

Maintenant, pour montrer à quel point ce nombre est absurdement grand, imaginons chaque serveur sur Internet (disons 50 000 000 aujourd'hui) essayant de forcer ce nombre à un rythme de 1 000 000 000 par seconde chacun. En réalité, vos serveurs fondraient sous une telle charge, mais jouons-en.

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

Donc, 50 quadrillions de suppositions par seconde. C'est rapide! Droite?

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

Donc 6,8 sextillions de secondes ...

Essayons de ramener cela à un nombre plus convivial.

215,626,585,489,599 years

Ou même mieux:

47917 times the age of the universe

Oui, c'est 47917 fois l'âge de l'univers ...

En gros, ça ne va pas être craqué.

Pour résumer:

La meilleure approche que je recommande est de stocker le cookie en trois parties.

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

Ensuite, pour valider:

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

Remarque: N'utilisez pas le jeton ni la combinaison utilisateur et jeton pour rechercher un enregistrement dans votre base de données. Assurez-vous toujours d'extraire un enregistrement basé sur l'utilisateur et utilisez une fonction de comparaison sécurisée pour comparer le jeton récupéré par la suite. Plus sur les attaques de synchronisation .

Maintenant, il est très important que le SECRET_KEY soit un secret cryptographique (généré par quelque chose comme /dev/urandom et/ou dérivé d'un entrée à entropie élevée). De plus, GenerateRandomToken() doit être une source aléatoire forte (mt_Rand() n'est pas assez puissant. Utilisez une bibliothèque, telle que RandomLib ou random_compat , ou mcrypt_create_iv() avec DEV_URANDOM ) ...

Le hash_equals() est d'empêcher attaques temporelles . Si vous utilisez une version PHP inférieure à PHP 5.6, la fonction hash_equals() n'est pas prise en charge. Dans ce cas, vous pouvez remplacer hash_equals() par la fonction 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;
}
701
ircmaxell

Avis de sécurité: Baser le cookie sur un hachage MD5 de données déterministes est une mauvaise idée. il est préférable d'utiliser un jeton aléatoire dérivé d'un CSPRNG. Voir la réponse de ircmaxell à cette question pour une approche plus sécurisée.

D'habitude je fais quelque chose comme ça:

  1. L'utilisateur se connecte avec 'garder moi connecté' 
  2. Créer une session 
  3. Créez un cookie appelé SOMETHING contenant: md5 (sel + nom d'utilisateur + ip + sel) et un cookie appelé quelquechose contenant le même identifiant 
  4. Store cookie dans la base de données 
  5. L'utilisateur fait des choses et laisse ---- 
  6. L’utilisateur revient, cherche un cookie, s’il existe, récupère l’ancien hachage de la base de données de cet utilisateur, vérifie si le contenu du cookie correspond à celui de la base de données, qui ip) donc: cookieHash == databaseHash == md5 (sel + nom d'utilisateur + ip + sel), s'ils le font, passez à 2, s'ils ne passent pas à 1

Bien sûr, vous pouvez utiliser différents noms de cookies, etc. Vous pouvez également modifier un peu le contenu du cookie. Assurez-vous simplement qu'il ne soit pas facile à créer. Vous pouvez par exemple également créer un user_salt lors de la création de l'utilisateur et le placer également dans le cookie. 

Vous pouvez aussi utiliser sha1 au lieu de md5 (ou à peu près n'importe quel algorithme)

95
Pim Jager

Introduction

Votre titre «Gardez-moi connecté» - la meilleure approche rend difficile pour moi de savoir par où commencer car, si vous recherchez la meilleure approche, vous devrez tenir compte des éléments suivants:

  • Identification
  • Sécurité 

Biscuits

Les cookies sont vulnérables. Entre les vulnérabilités courantes de vol de cookies dans les navigateurs et les attaques de script entre sites, nous devons accepter le fait que les cookies ne sont pas sans danger. Pour améliorer la sécurité, vous devez noter que phpsetcookies possède des fonctionnalités supplémentaires telles que 

bool setcookie (chaîne $ name [ chaîne $ value [ int $ expire = 0 [ chaîne $ path [ chaîne $ domain [ bool $ secure =] [false, bool $ httponly = false]]]]]])

  • secure (Utilisation de la connexion HTTPS)
  • httponly (Réduire le vol d'identité par le biais d'une attaque XSS)

Définitions 

  • Jeton (chaîne aléatoire imprévisible de longueur n, par exemple./Dev/urandom)
  • Référence (chaîne aléatoire imprévisible de longueur n p. Ex./Dev/urandom)
  • Signature (Génère une valeur de hachage à clé en utilisant la méthode HMAC)

Approche simple 

Une solution simple serait:

  • L'utilisateur est connecté avec Remember Me
  • Cookie de connexion émis avec jeton et signature
  • Quand est de retour, la signature est vérifiée
  • Si la signature est correcte, le nom d'utilisateur et le jeton sont recherchés dans la base de données. 
  • si non valide .. retour à la page de connexion
  • Si valide automatiquement connecter 

L’étude de cas ci-dessus résume tous les exemples donnés sur cette page, mais les inconvénients sont que 

  • Il n'y a aucun moyen de savoir si les cookies ont été volés
  • L'attaquant peut accéder à des opérations sensibles telles que le changement de mot de passe ou des données telles que des informations personnelles et de cuisson, etc.
  • Le cookie compromis serait toujours valable pour la durée de vie du cookie 

Meilleure solution

Une meilleure solution serait 

  • L'utilisateur est connecté et n'oubliez pas que moi est sélectionné 
  • Générer un jeton et une signature et enregistrer dans un cookie 
  • Les jetons sont aléatoires et ne sont valables que pour une seule authentification 
  • Les jetons sont remplacés à chaque visite sur le site
  • Lorsqu'un utilisateur non connecté visite le site, la signature, le jeton et le nom d'utilisateur sont vérifiés 
  • Rappelez-vous que mon identifiant doit avoir un accès limité et ne pas permettre la modification de mot de passe, d'informations personnelles, etc.

Exemple de code 

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

Classe utilisée 

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

Test dans Firefox et Chrome 

enter image description here

Avantage 

  • Meilleure sécurité 
  • Accès limité pour l'attaquant 
  • Lorsque le cookie est volé, il est uniquement valide pour un accès unique 
  • Lorsque l'utilisateur d'origine accédera ensuite au site, vous pourrez automatiquement détecter et informer l'utilisateur du vol. 

Inconvénient 

  • Ne supporte pas les connexions persistantes via plusieurs navigateurs (Mobile et Web)
  • Le cookie peut toujours être volé car l'utilisateur ne reçoit la notification qu'après la prochaine connexion.

Solution rapide

  • Introduction du système d'approbation pour chaque système devant avoir une connexion persistante 
  • Utiliser plusieurs cookies pour l'authentification

Approche de cookie multiple

Lorsqu'un attaquant est sur le point de voler des cookies, il ne peut s'agir que d'un site Web ou d'un domaine particulier, par exemple. exemple.com

Mais en réalité, vous pouvez authentifier un utilisateur de 2 domaines différents (example.com & fakeaddsite.com) et lui donner l'apparence d'un "cookie d'annonce". 

  • Utilisateur connecté à example.com avec Remember me
  • Nom d'utilisateur, jeton, référence dans le cookie
  • Stocker le nom d'utilisateur, le jeton, la référence dans la base de données, par exemple. Memcache 
  • Envoyez l'identifiant de référence via get et iframe à l'adresse fakeaddsite.com
  • fakeaddsite.com utilise la référence pour extraire l'utilisateur et le jeton de la base de données
  • fakeaddsite.com stocke la signature
  • Lorsqu'un utilisateur renvoie des informations de signature d'extraction avec iframe à partir de fakeaddsite.com
  • Combinez les données et faites la validation 
  • ..... tu connais le reste

Certaines personnes pourraient se demander comment utiliser 2 cookies différents? Eh bien, c’est possible, imaginez example.com = localhost et fakeaddsite.com = 192.168.1.120. Si vous inspectez les cookies, cela ressemble à ceci 

enter image description here

De l'image ci-dessus 

  • Le site actuel visité est localhost 
  • Il contient également des cookies définis à partir de 192.168.1.120.

192.168.1.120

  • Accepte uniquement le HTTP_REFERER défini
  • Accepte uniquement les connexions à partir du REMOTE_ADDR spécifié
  • Pas de JavaScript, pas de contenu mais rien de plus que de signer des informations et les ajouter ou les récupérer à partir d'un cookie 

Avantage

  • 99% du temps que vous avez trompé l'attaquant 
  • Vous pouvez facilement verrouiller le compte lors de la première tentative de l'attaquant. 
  • L'attaque peut être prévenue même avant la prochaine connexion, comme les autres méthodes 

Inconvénient 

  • Demande multiple au serveur pour une seule connexion 

Amélioration 

  • Terminé utilisation iframe use ajax 
71
Baba

Il y a deux articles très intéressants que j'ai trouvés en cherchant une solution parfaite au problème "souviens-moi de moi":

24
Stefan Gehrig

J'ai posé un angle de cette question ici , et les réponses vous mèneront à tous les liens de cookie de temporisation basés sur des jetons dont vous avez besoin.

Fondamentalement, vous ne stockez pas l'ID utilisateur dans le cookie. Vous stockez un jeton unique (chaîne énorme) que l'utilisateur utilise pour récupérer son ancienne session de connexion. Ensuite, pour le rendre vraiment sécurisé, vous demandez un mot de passe pour les opérations lourdes (comme changer le mot de passe lui-même).

6
Dan Rosenstark

Je recommanderais l'approche mentionnée par Stefan (c'est-à-dire, suivez les instructions de Meilleures pratiques relatives aux cookies de connexion persistante améliorées ) et recommandez également de vous assurer que vos cookies sont des cookies HttpOnly afin qu'ils ne soient pas accessibles, potentiellement malveillants , JavaScript.

5
Walter Rumsby

Vieux fil, mais toujours une préoccupation valable. J'ai remarqué de bonnes réponses concernant la sécurité et le fait d'éviter l'utilisation de «la sécurité par l'obscurité», mais les méthodes techniques réellement fournies n'étaient pas suffisantes à mes yeux. Choses que je dois dire avant d’apporter ma méthode:

  • NEVER stocke un mot de passe en texte clair ... JAMAIS!
  • NEVER stocke le mot de passe haché d'un utilisateur dans plusieurs emplacements de votre base de données. Le serveur dorsal est toujours capable d'extraire le mot de passe haché de la table users. Il n'est pas plus efficace de stocker des données redondantes au lieu de transactions de base de données supplémentaires, l'inverse est vrai.
  • Votre identifiant de session doit être unique, afin que deux utilisateurs ne puissent jamais partager un identifiant, d'où l'intérêt d'un identifiant (le numéro d'identification de votre permis de conduire pourrait-il correspondre à une autre personne? Non.) Ceci génère une combinaison unique en deux parties , basé sur 2 chaînes uniques. Votre table de sessions doit utiliser l'ID comme clé de configuration. Pour autoriser la connexion automatique de plusieurs périphériques pour la connexion automatique, utilisez une autre table pour les périphériques sécurisés, qui contient la liste de tous les périphériques validés (voir l'exemple ci-dessous) et est mappée à l'aide du nom d'utilisateur.
  • Il ne sert à rien de hacher des données connues dans un cookie, le cookie peut être copié. Ce que nous recherchons, c’est un appareil utilisateur conforme qui fournisse des informations authentiques qui ne peuvent pas être obtenues sans un attaquant susceptible de compromettre la machine de l’utilisateur (encore une fois, voir mon exemple). Toutefois, cela signifierait qu'un utilisateur légitime qui empêche les informations statiques de sa machine (adresse MAC, nom d'hôte de périphérique, useragent si le navigateur le restreint, etc.) de rester cohérent (ou de la falsifier au départ) ne pourra pas utiliser cette fonctionnalité. Mais si cela vous inquiète, considérez le fait que vous offrez la connexion automatique aux utilisateurs qui s'identifient de manière unique. Par conséquent, s'ils refusent de se faire connaître en usurpant leur MAC, en usurpant leur utilisateur en changeant leur nom d'hôte, en se cachant derrière des mandataires, etc., ils ne sont pas identifiables et ne doivent jamais être authentifiés pour un service automatique. Si vous le souhaitez, vous devez rechercher un accès par carte à puce associé à un logiciel côté client qui établit l’identité du périphérique utilisé.

Cela étant dit, il existe deux excellentes méthodes d’auto-connexion sur votre système.

Premièrement, le moyen facile et peu coûteux qui confie tout à quelqu'un d'autre. Si vous faites en sorte que l’assistance de votre site se connecte avec, par exemple, votre compte Google +, vous avez probablement un bouton simplifié Google + qui connectera l’utilisateur s’il est déjà connecté à Google (je l’ai fait ici pour répondre à cette question, car je suis toujours connecté à google). Si vous souhaitez que l'utilisateur se connecte automatiquement s'il est déjà connecté avec un authentifiant approuvé et pris en charge, et que la case correspondante est cochée, demandez à vos scripts côté client d'exécuter le code derrière le bouton "connexion avec" correspondant avant le chargement. Assurez-vous simplement que le serveur stocke un identifiant unique dans une table de connexion automatique comportant le nom d'utilisateur, l'identifiant de session et l'authentificateur utilisé pour l'utilisateur. Étant donné que ces méthodes de connexion utilisent AJAX, vous attendez quand même une réponse et cette réponse est une réponse validée ou un rejet. Si vous obtenez une réponse validée, utilisez-la normalement, puis continuez de charger normalement l'utilisateur connecté. Sinon, la connexion a échoué, mais ne le dites pas à l'utilisateur, continuez comme s'il n'était pas connecté, il le remarquera. Ceci afin d'empêcher un attaquant qui a volé des cookies (ou les a forgés dans le but de faire grimper ses privilèges) d'apprendre que l'utilisateur se connecte automatiquement au site.

Cela ne coûte pas cher et peut également être considéré comme sale par certains utilisateurs, car il essaie de valider votre connexion déjà existante avec des sites tels que Google et Facebook, sans même vous le dire. Cependant, il ne doit pas être utilisé sur les utilisateurs qui n'ont pas demandé à se connecter automatiquement à votre site. Cette méthode particulière est réservée à l'authentification externe, comme avec Google+ ou FB.

Un authentificateur externe ayant été utilisé pour indiquer au serveur en coulisse si un utilisateur était validé ou non, un attaquant ne peut obtenir rien d'autre qu'un identifiant unique, ce qui est inutile par lui-même. Je vais élaborer:

  • L'utilisateur 'joe' visite le site pour la première fois, l'identifiant de session placé dans le cookie 'session'.
  • L'utilisateur 'joe' se connecte, augmente les privilèges, obtient un nouvel identifiant de session et renouvelle le cookie 'session'.
  • L'utilisateur 'joe' choisit de se connecter automatiquement à l'aide de google + et obtient un identifiant unique placé dans le cookie 'keepmesignedin'.
  • L'utilisateur "joe" a Google pour le garder connecté, permettant à votre site de se connecter automatiquement à l'utilisateur en utilisant Google dans votre backend.
  • Attacker essaie systématiquement des identifiants uniques pour 'keepmesignedin' (il s'agit d'une information publique communiquée à chaque utilisateur), et n'est connecté à aucune autre adresse. essaie l'identifiant unique donné à 'joe'.
  • Le serveur reçoit un identifiant unique pour 'joe', extrait la correspondance dans la base de données pour un compte google +.
  • Le serveur envoie l'attaquant à la page de connexion qui exécute une demande AJAX à Google pour se connecter.
  • Le serveur Google reçoit une demande et utilise son API pour vérifier que l'attaquant n'est pas connecté pour le moment.Google répond qu'il n'y a pas d'utilisateur actuellement connecté via cette connexion.
  • La page de l'attaquant reçoit une réponse, le script redirige automatiquement vers la page de connexion avec une valeur POST codée dans l'URL.
  • La page de connexion obtient la valeur POST, envoie le cookie de 'keepmesignedin' à une valeur vide et une date de validité allant du 1-1-1970 pour décourager une tentative automatique, le navigateur de l'attaquant supprimant simplement le cookie.
  • L'attaquant reçoit une page de connexion normale pour la première fois.
  • Quoi qu'il en soit, même si un attaquant utilise un ID inexistant, la tentative doit échouer à toutes les tentatives, sauf lorsqu'une réponse validée est reçue.

Cette méthode peut et doit être utilisée conjointement avec votre authentificateur interne pour ceux qui se connectent à votre site à l'aide d'un authentificateur externe.

=========.

Maintenant, pour votre propre système d'authentification capable de se connecter automatiquement, voici comment je le fais:

DB a quelques tables:

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

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

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

Cela empêche toute personne d'usurper systématiquement l'adresse MAC et le nom d'hôte d'un utilisateur pour lequel il sait s'auto-enregistrer. NEVER demande à l'utilisateur de conserver un cookie avec son mot de passe, un texte en clair ou autre. Faites en sorte que le jeton soit régénéré à chaque navigation de page, exactement comme le jeton de session. Cela réduit considérablement la probabilité qu'un attaquant puisse obtenir un cookie de jeton valide et l'utiliser pour se connecter. Certaines personnes vont essayer de dire qu'un attaquant pourrait voler les cookies à la victime et faire une attaque de relecture de session pour se connecter. Si un attaquant pouvait voler les cookies (ce qui est possible), il aurait certainement compromis l'appareil en entier, ce qui signifie qu'il ne pourrait utiliser que l'appareil pour se connecter de toute façon, ce qui irait à l'encontre du but de voler des cookies. Tant que votre site fonctionne sur HTTPS (ce qui devrait être le cas lorsqu'il s'agit de mots de passe, de numéros CC ou d'autres systèmes de connexion), vous offrez toute la protection à l'utilisateur que vous pouvez dans un navigateur.

Une chose à garder à l'esprit: les données de session ne doivent pas expirer si vous utilisez la connexion automatique. Vous pouvez désactiver la possibilité de continuer la session de façon erronée, mais la validation dans le système doit permettre de reprendre les données de la session s'il s'agit de données persistantes censées continuer entre les sessions. Si vous voulez à la fois des données de session persistantes ET non persistantes, utilisez une autre table pour les données de session persistante avec le nom d'utilisateur comme clé PK et demandez au serveur de la récupérer comme les données de session normales. Utilisez simplement une autre variable.

Une fois cette connexion établie, le serveur doit toujours valider la session. C’est là que vous pouvez coder les attentes relatives aux systèmes volés ou compromis; les schémas et autres résultats attendus des connexions aux données de session peuvent souvent conduire à la conclusion qu'un système a été détourné ou que des cookies ont été forgés afin d'obtenir un accès. C’est là que votre ISS Tech peut définir des règles qui déclencheraient le verrouillage du compte ou le retrait automatique d’un utilisateur du système de connexion automatique, en laissant les attaquants suffisamment longtemps pour qu’ils déterminent le mode de réussite de l’attaquant et son mode de fonctionnement. coupez-les.En guise de conclusion, assurez-vous que toute tentative de récupération, tout changement de mot de passe ou tout échec de connexion au-delà du seuil entraîne la désactivation de la connexion automatique jusqu'à la validation correcte de l'utilisateur et la confirmation de son existence.

Je m'excuse si quelqu'un s'attendait à ce que le code soit donné dans ma réponse, cela ne se produira pas ici. Je dirai que j'utilise PHP, jQuery et AJAX pour exécuter mes sites et que je n'utilise JAMAIS Windows en tant que serveur ... jamais.

As a closing note, be sure that any recovery attempt, password changes, or login failures past the threshold result in auto-signin being disabled until the user validates properly and acknowledges this has occurred.

I apologize if anyone was expecting code to be given out in my answer, that's not going to happen here. I will say that I use PHP, jQuery, and AJAX to run my sites, and I NEVER use Windows as a server... ever.

4
user253780

Générez un hachage, avec peut-être uniquement un secret que vous connaissez, puis stockez-le dans votre base de données pour pouvoir l'associer à l'utilisateur. Devrait fonctionner assez bien.

4
Jani Hartikainen

Je ne comprends pas le concept de stockage d'éléments cryptés dans un cookie lorsque c'est la version cryptée de ce dernier qui doit être piratée. Si je manque quelque chose, commentez s'il vous plaît.

Je songe à adopter cette approche pour se souvenir de moi. Si vous pouvez voir des problèmes, veuillez commenter.

  1. Créez une table pour stocker les données "Remember Me" - distincte de la table des utilisateurs afin que je puisse me connecter à partir de plusieurs périphériques.

  2. En cas de connexion réussie (avec Remember Me coché):

    a) Générez une chaîne aléatoire unique à utiliser comme ID utilisateur sur cette machine: bigUserID

    b) Générer une chaîne aléatoire unique: bigKey

    c) Stocker un cookie: bigUserID: bigKey

    d) Dans le tableau "Remember Me", ajoutez un enregistrement avec: ID utilisateur, adresse IP, bigUserID, bigKey

  3. Si vous essayez d'accéder à quelque chose qui nécessite une connexion:

    a) Recherchez le cookie et recherchez bigUserID & bigKey avec une adresse IP correspondante

    b) Si vous le trouvez, connectez-vous à la personne mais définissez un indicateur dans la table utilisateur "Connexion logicielle" afin que, pour toute opération dangereuse, vous puissiez demander une connexion complète.

  4. À la déconnexion, marquez tous les enregistrements "Remember Me" pour cet utilisateur comme expirés.

Les seules vulnérabilités que je peux voir sont;

  • vous pouvez obtenir le portable de quelqu'un et usurper son adresse IP avec le cookie.
  • vous pouvez usurper une adresse IP différente à chaque fois et deviner le problème - mais avec deux grosses chaînes, ce serait ... faire un calcul similaire à celui indiqué ci-dessus ... Je n'ai aucune idée ... de grandes chances?
2
Enigma Plus

Ma solution est comme ça. Ce n'est pas à 100% à l'épreuve des balles, mais je pense que cela vous épargnera dans la plupart des cas.

Lorsque l'utilisateur est connecté avec succès, créez une chaîne avec ces informations:

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

Cryptez $data, définissez le type sur HttpOnly et définissez le cookie.

Lorsque l'utilisateur revient sur votre site, procédez comme suit:

  1. Obtenir des données de cookie. Supprimez les caractères dangereux dans le cookie. Explose-le avec le caractère :
  2. Vérifier la validité. Si le cookie a plus de X jours, redirigez l'utilisateur vers la page de connexion.
  3. Si le cookie n'est pas vieux; Obtenir l'heure de changement du mot de passe de la base de données. Si le mot de passe est modifié après la dernière connexion de l'utilisateur, redirigez la page de connexion.
  4. Si passe n'a pas été changé récemment; Obtenir l'agent de navigateur actuel de l'utilisateur. Vérifiez si (currentUserAgentHash == cookieUserAgentHash). Si les agents sont identiques, passez à l'étape suivante, sinon redirigez-vous vers la page de connexion.
  5. Si toutes les étapes ont réussi, autorisez le nom d'utilisateur.

Si des utilisateurs se déconnectent, supprimez ce cookie. Créez un nouveau cookie si l'utilisateur se reconnecte.

2
trante

J'ai lu toutes les réponses et j'ai toujours eu du mal à extraire ce que j'étais censé faire. Si une image vaut 1k mots, j'espère que cela aidera les autres à mettre en place un stockage persistant sécurisé basé sur Barry Jaspan's Meilleure pratique en matière de cookies de connexion persistants améliorés

 enter image description here

Si vous avez des questions, des commentaires ou des suggestions, je vais essayer de mettre à jour le diagramme afin que le débutant ait la possibilité de mettre en place une connexion persistante sécurisée.

2
Josh Woodcock

L'implémentation d'une fonctionnalité "Keep Me Logged In" signifie que vous devez définir exactement ce que cela signifiera pour l'utilisateur. Dans le cas le plus simple, j'utiliserais cela pour signifier que la session a un délai d'expiration beaucoup plus long: 2 jours (par exemple) au lieu de 2 heures. Pour ce faire, vous aurez besoin de votre propre stockage de session, probablement dans une base de données, afin de pouvoir définir des heures d'expiration personnalisées pour les données de session. Ensuite, vous devez vous assurer de définir un cookie qui restera en place pendant quelques jours (ou plus longtemps), plutôt que d’expirer à la fermeture du navigateur.

Je peux vous entendre demander "Pourquoi 2 jours? Pourquoi pas 2 semaines?". En effet, l'utilisation d'une session dans PHP va automatiquement repousser l'expiration. En effet, l'expiration d'une session dans PHP est en réalité un délai d'inactivité. 

Cela dit, je mettrais probablement en œuvre une valeur de délai d’attente plus dure que je stocke dans la session elle-même et à l’environ 2 semaines, et j’ajouterais du code pour voir cela et invalider de force la session. Ou du moins pour les déconnecter. Cela signifie que l'utilisateur sera invité à se connecter périodiquement. Yahoo! est ce que ca.

0
staticsan