Créer un système de connexion "Se souvenir de moi"

23 Septembre 2016 à 12:30 Programmation

Je ne publierai plus rien sur ce blog. Un nouveau blog existe maintenant. Il se nomme Hardly Smart.

Vous pourrez y retrouver des astuces plus larges sur l'informatique ainsi que mes anciens billets de blog.

Je vous souhaite une bonne lecture sur Hardly Smart.


Dans mon billet précédent, Crypter et décrypter une chaîne de caractères en PHP, je vous expliquais comment crypter une chaine de caractère. Voici un cas concret de son utilisation

Avant-propos

J'ai un site Internet où dessus mes utilisateurs peuvent s'inscrire afin de faire diverses actions. Et j'aimerai que lors de la connexion, mes utilisateurs, puissent cocher la case "Se souvenir de moi" afin de ne pas devoir en permanence se reconnecter.

Base de données

Voici la structure de mes base de données où sont stockés les informations.

Table des utilisateurs

id username password
1 dupont $1$C9Ujb263$6bxCPzmeEbXIZ1YsSR918.
... ... ...

Ici mes mots de passes sont cryptés via la fonction crypt de PHP.

Table des cookies de connexion

id user_id cookie
... ... ...

Processus

Avant d'aller plus loin, voici comment va se dérouler le système :

  1. L'utilisateur se log sur le site et coche la case se souvenir de moi
  2. Je crée la session de l'utilisateur
  3. Je crée un cookie (que j'appellerai keep_log) qui contiendra un tableau avec son IP et son identifiant mais le tout crypté
  4. J'insert ce cookie dans ma base de données
  5. L'utilisateur quitte le site et revient plus tard
  6. Comme sa session n'existe plus je vais regarder son cookie keep_log
  7. Est-ce qu'il existe dans ma BDD ?
    1. Non, je vais à l'étape 1
    2. Oui je continu
  8. Je décrypte le cookie et je regarde si l'IP correspond à son IP actuelle
    1. Elle ne correspond pas ? => étape 1
    2. Si oui, je continu
  9. Je regarde si le username du cookie correspond à l'id dans ma table des utilisateurs
    1. Aucune correspondance => étape 1
    2. Sinon, je récupère les données et les mets en session

Code source

<?php

    use \Defuse\Crypto\Crypto;
    use \Defuse\Crypto\Key;


    // Faite ici votre vérification pour savoir si l'utilisateur existe en BDD


    // Etape 1 à 3
    session_start();

    $_SESSION['user'] = User::getSession(); // A remplacer par votre système de gestion des utilisateurs

    $cookie = array(
        'ip'       => $_SERVER['REMOTE_ADDR'],
        'username' => $_POST['username']
    );

    $key = Key::createNewRandomKey(); // Pensez bien à stocker cette clé à un endroit sûr

    $cookieCrypted = Crypto::encrypt(json_encode($cookie), $key);
    setcookie('keep_log', $cookieCrypted, time()+60*60*24*365);


    // Etape 4
    // Je considère que vous avez déjà une variable $pdo qui est connecté à votre BDD
    $prepare = $pdo->prepare("INSERT INTO cookie (user_id, cookie) VALUES (:user_id, :cookie)");
    $prepare->execute(array(
        ':user_id' => User::get('id'), // A remplacer par votre système de gestion des utilisateurs
        ':cookie'  => $cookieCrypted,
    ));
    $prepare->closeCursor();


    // Etape 6
    $cookieCrypted = $_COOKIE['keep_log'];


    // Etape 7
    $prepare = $pdo->prepare("SELECT user_id FROM cookie WHERE cookie = :cookie");
    $prepare->execute(array(
        ':cookie' => $cookieCrypted,
    ));
    $datas = $prepare->fetchAll(PDO::FETCH_OBJ);
    $prepare->closeCursor();

    if(count($datas) === 0) {
        return Router::redirectTo('login'); // Adaptez en fonction de votre système de gestion des routes
    }


    // Etape 8
    $cookie = Crypto::decrypt($cookieCrypted, $key);
    $cookie = json_decode($cookie);

    if($cookie->ip !== $_SERVER['REMOTE_ADDR']) {
        return Router::redirectTo('login'); // Adaptez en fonction de votre système de gestion des routes
    }


    // Etape 9
    $prepare = $pdo->prepare("SELECT id, username FROM user WHERE id = :id AND username = :username");
    $prepare->execute(array(
        ':id'       => $datas[0]->user_id,
        ':username' => $cookie->username,
    ));
    $datas = $prepare->fetchAll(PDO::FETCH_OBJ);
    $prepare->closeCursor();

    if(count($datas) === 0) {
        return Router::redirectTo('login'); // Adaptez en fonction de votre système de gestion des routes
    }

Et voilà, vous avez maintenant un système de connexion où l'utilisateur sera reconnu dès qu'il arrivera sur votre site.

Alors bien sûr, je vous laisse l'adapter pour y ajouter si besoin plus d'informations dans les cookies ou plus de vérifications mais les grandes lignes sont là.

Vous pouvez même stocker dans votre base de données de cookie une date de validité puis créer un cron qui supprimera les lignes inutiles.

Articles liés

Les commentaires ont été désactivés.

2 commentaires

robob 9 Novembre 2022 à 19:05

Bonjour,
Merci pour ce petit tuto : je l'ai utilisé comme base pour réfléchir à un code similaire.
- J'ai utilisé les fonctions openssl_encrypt et openssl_decrypt après avoir codé avec \Defuse\Crypto : cela m'évite de charger la bibliothèque "defuse".
- Je ne comprends pas la vérification du username (etape 9) : si le cookie correspond à celui de la BDD, c'est que le username correspondra aussi ?
- je me demande s'il n'est pas préférable dans ce cas, de laisser un cookie coté client dans lequel il n'y a aucune donnée sensible (username ou mail...). Par exemple une chaine randomisée quelconque. Dans la table cookie, on stocke l'adresse IP pour la vérification mais pas dans le cookie.
- Le seul risque que je vois, c'est la copie du cookie client utilisé sur un autre ordinateur qui usurperait l'adresse IP du premier.

Merci encore pour cette démo bien utile.

BaBeuloula 10 Novembre 2022 à 12:03

Salut robob,
Pour l'étape 9 est juste là pour connecter l'utilisateur si on arrive à reconnaître l'utilisateur dans la BDD.
Tu peux oui mettre une chaîne randomisée pour maximiser la sécurité. Je dois t'avouer qu'en 2016 je n'avais pas les mêmes connaissances en sécurité ^^