web-dev-qa-db-fra.com

Autoriser jusqu'à 5 sessions de connexion simultanées

Je ne parviens pas à mettre en place une vérification de connexion simultanée.

Le site doit empêcher plus de 5 sessions simultanées pour un utilisateur particulier à la fois.

Exemple: L'utilisateur Matt peut avoir 5 sessions actives.

Si l'utilisateur Matt essaie de se connecter avec une 6ème session, il supprimera la session qui s'est connectée en premier et n'avait aucune activité antérieure à 4 heures. Si les 5 sessions ont été actives au cours des 4 dernières heures, la connexion échoue et l'utilisateur reçoit un message d'erreur/message destiné à contacter l'administrateur du site.

Je sais que Wordpress a WP_Session_Tokens mais il semble qu'ils n'enregistrent que les 'expirations' et les 'identifiants' sans 'last_activity'. Existe-t-il un moyen de vérifier la dernière activité par Wordpress ou par PHP Sessions?

Si ce n'est pas le cas, une question secondaire est la meilleure façon de comparer la dernière connexion à l'heure actuelle et de vérifier si elle dépasse 4 heures.

Voici mon code actuel:

// On login, check if there are already 5 sessions active for the user
function check_sessions($login) {

    global $user_ID;
    $user = get_user_by( 'slug', $login );

    //If there are less than 5 sessions, let user login normally
     if( count( wp_get_all_sessions() ) < 5 ) {
         return true;
     }

    $sessions = WP_Session_Tokens::get_instance(  $user->id );

    $all_sessions = $sessions->get_all();

    $first_login = $all_sessions[0]['login'];

    if( $first_login->diff(time()) > 4hrs ) {
        // log out first_login user & login new user
        WP_Session_Tokens::destroy( $all_sessions[0] );
        return true;
    }

    else {

       // display message to user
    }
}
add_action('wp_login','check_sessions');
5
Gil

Cette question m'a vraiment intéressé. Il a fallu environ 5 heures de mon samedi pour créer la solution complète :)

Plugin Limiter les sessions de connexion

Il ne fournit pas encore de page de paramètres, donc toutes les options sont actuellement codées en dur. Le plugin implémente les éléments suivants (selon OP):

  1. Un utilisateur peut avoir un maximum de 5 sessions de connexion sur différents navigateurs et appareils.
  2. Si plus de 5 sessions sont tentées, une erreur s’affichera, à moins que la session d’activité la plus ancienne ait plus de 4 heures.
  3. Si la session d'activité la plus ancienne date de plus de 4 heures, cette session sera fermée et la tentative actuelle de connexion sera autorisée.

J'ai essayé d'ajouter des explications dans le code avec des commentaires. La plupart du code du plugin devrait être explicite. Si certaines parties ne sont pas claires, n'hésitez pas à commenter.

Le dépôt GitHub peut être trouvé ici . N'hésitez pas à fourcher et à l'améliorer :) Si quelqu'un pense que ce serait un ajout utile au référentiel de plugins WordPress, faites-le moi savoir et je le téléchargerai sur WordPress.org si nécessaire.

<?php
/*
Plugin Name: Limit Login Sessions
Version: 1.0.0
Author: Sisir Kanti Adhikari
Author URI: https://sisir.me/
Description: Limits users login sessions.
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Limit Login Sessions is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
any later version.

Limit Login Sessions is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (http://www.gnu.org/licenses/gpl-2.0.html)
 for more details.

*/

add_filter('authenticate', 'lls_authenticate', 1000, 2);

function lls_authenticate($user, $username){

    if(!username_exists($username) || !$user = get_user_by('login', $username))
        return null; // will trigger WP default no username/password matched error

    // setup vars
    $max_sessions = 5;
    $max_oldest_allowed_session_hours = 4;
    $error_code = 'max_session_reached';
    $error_message = "Maximum $max_sessions login sessions are allowed. Please contact site administrator.";

    // 1. Get all active session for this user
    $manager = WP_Session_Tokens::get_instance( $user->ID );
    $sessions =  $manager->get_all();

    // 2. Count all active session
    $session_count = count($sessions);

    // 3. Return okay if active session less then $max_sessions
    if($session_count < $max_sessions)
        return $user;

    $oldest_activity_session = lls_get_oldest_activity_session($sessions);

    // 4. If active sessions is equal to 5 then check if a session has no activity last 4 hours
    // 5. if oldest session have activity return error
    if(
        ( $session_count >= $max_sessions && !$oldest_activity_session ) // if no oldest is found do not allow
        || ( $session_count >= $max_sessions && $oldest_activity_session['last_activity'] + $max_oldest_allowed_session_hours * HOUR_IN_SECONDS > time())
    ){
        return new WP_Error($error_code, $error_message);
    }

    // 5. Oldest activity session doesn't have activity is given recent hours
    // destroy oldest active session and authenticate the user

    $verifier = lls_get_verifier_by_session($oldest_activity_session, $user->ID);

    lls_destroy_session($verifier, $user->ID);

    return $user;

}

function lls_destroy_session($verifier, $user_id){

    $sessions = get_user_meta( $user_id, 'session_tokens', true );

    if(!isset($sessions[$verifier]))
        return true;

    unset($sessions[$verifier]);

    if(!empty($sessions)){
        update_user_meta( $user_id, 'session_tokens', $sessions );
        return true;
    }

    delete_user_meta( $user_id, 'session_tokens');
    return true;

}

function lls_get_verifier_by_session($session, $user_id = null){

    if(!$user_id)
        $user_id = get_current_user_id();

    $session_string = implode(',', $session);
    $sessions = get_user_meta( $user_id, 'session_tokens', true );

    if(empty($sessions))
        return false;

    foreach($sessions as $verifier => $sess){
        $sess_string = implode(',', $sess);

        if($session_string == $sess_string)
            return $verifier;

    }

    return false;
}


function lls_get_oldest_activity_session($sessions){
    $sess = false;

    foreach($sessions as $session){

        if(!isset($session['last_activity']))
            continue;

        if(!$sess){
            $sess = $session;
            continue;
        }

        if($sess['last_activity'] > $session['last_activity'])
            $sess = $session;

    }

    return $sess;
}

// add a new key to session token array

add_filter('attach_session_information', 'lls_attach_session_information');

function lls_attach_session_information($session){
    $session['last_activity'] = time();
    return $session;
}

add_action('template_redirect', 'lls_update_session_last_activity');

function lls_update_session_last_activity(){

    if(!is_user_logged_in())
        return;

    // get the login cookie from browser
    $logged_in_cookie = $_COOKIE[LOGGED_IN_COOKIE];

    // check for valid auth cookie
    if( !$cookie_element = wp_parse_auth_cookie($logged_in_cookie) )
        return;

    // get the current session
    $manager = WP_Session_Tokens::get_instance( get_current_user_id() );

    $current_session = $manager->get($cookie_element['token']);

    if(
        $current_session['expiration'] <= time() // only update if session is not expired
        || ( $current_session['last_activity'] + 5 * MINUTE_IN_SECONDS ) > time() // only update in every 5 min to reduce db load
    ){
        return;
    }

    $current_session['last_activity'] = time();
    $manager->update($cookie_element['token'], $current_session);

}

Pour certaines fonctionnalités, je devais interagir directement avec la valeur user_meta de la base de données. Certaines méthodes étaient protégées dans la classe, elles ne pouvaient donc pas être consultées directement.

Le plugin est testé localement avec WP v4.3.1.

12
Sisir

Il est impossible d'avoir une réponse à la question car elle est posée car le protocole http n'a pas de session à long terme. Une session sur http est une requête et une réponse.

Les sessions telles que nous les connaissons sur Internet ne sont que des hacks conçus pour supprimer le besoin de fournir un identifiant et un mot de passe pour chaque chargement de page.

wordpress 4.1 fait un pas en avant en associant mieux une "session" à un terminal, mais il s'agit simplement d'un meilleur bidouillage qu'auparavant et il n'est pas fiable à 100%, car vous pouvez toujours copier les cookies et/ou utiliser un proxy pour l'encrasser. croyant que deux périphériques finaux distincts appartiennent à la même session. OTOH il va penser que deux navigateurs sur la même machine sont sur des appareils différents.

Vous essayez simplement de faire fonctionner la GDN, et au cours des 20 dernières années, une seule chose a été prouvée concernant la GDN: 1. Cela n'empêche pas les gens d'obtenir un accès "illégal" au contenu, cela prend juste un peu plus longtemps. 2. Cela agace les clients payants.

Votre système spécifique dépend de la connaissance du prochain appareil à utiliser, et votre hypothèse selon laquelle il s'agit du dernier appareil actif est sans fondement. La session active OTOH 5 sera probablement suffisante pour tout mon bloc, vous ne pouvez donc même pas "bloquer le vol de contenu"

Ce n’est pas que la question n’a aucun mérite. Je peux voir quelque chose comme cela utilisé pour améliorer la sécurité, mais DRM dépend beaucoup des petits détails et une fois que vous utilisez un terme à la mode mais critique comme "sessions actives" sans en définir les détails, cela signifie pour moi que vous ne le faites pas. sachez vraiment ce que vous attendez réellement du système, car la découverte des "sessions actives" est la partie la plus critique de la mise en œuvre de votre schéma.

0
Mark Kaplun