Как предотвратить множественные логины на веб-сайте PHP

Я хочу предотвратить несколько логинов в приложении php.

Сначала я создаю статус входа (активный, неактивный) в таблице пользователя.

Когда пользователь A регистрирует статус пользователя, будет установлен "активный", и если пользователь выйдет из системы, статус будет установлен на "notactive". Когда другой клиент пытается войти в систему с использованием той же самой учетной записи пользователя, я проверяю таблицу пользователя. Если пользователь все еще активен, логин ошибки будет отправлен пользователю.

Проблема возникла, если пользователь закрыл браузер, статус в таблице пользователя может быть обновлен, потому что пользователь не нажал кнопку выхода из системы.

Есть ли у вас какие-либо предложения по этому поводу?

Ответы

Ответ 1

(Обратите внимание, что пока эта методика по-прежнему несколько верна, образцы PHP не должны копироваться дословно, так как есть более безопасные способы включения пользовательских значений в SQL-запрос)


Вместо того, чтобы сохранять, активен ли пользователь\неактивен, лучше сохранить некоторый атрибут, который можно проверить против пользователя на основе действия; как и в случае, каждый раз, когда пользователь пытается сделать что-то, что требует аутентификации, он проверяет, соответствует ли этот атрибут до его продолжения.

Я рекомендую вам сделать следующее:

Во-первых, создайте хэш, чтобы однозначно идентифицировать пользователя при каждом входе в систему. Я бы предположил, что sha1 time() будет достаточно, чтобы избежать столкновений. Независимо от того, что вы выберете, убедитесь, что он достаточно разнообразен, чтобы другой пользователь регистрировался с невероятно низкой вероятностью получения одного и того же хеша (например, не хешируйте IP-адрес или пользовательский агент браузера, поскольку они не меняются довольно).

Во-вторых, сохраните этот хэш в своей базе данных и в сеансе пользователя во время входа в систему. Это позволит эффективно "выйти из системы" предыдущего пользователя, так как хеш должен отличаться при каждом входе в систему.

Поскольку мы используем сеансы, cookie должен быть автоматически помещен в пользовательский браузер, который будет содержать уникальный идентификатор, который идентифицирует пользователя для его или ее данных сеанса. Содержимое cookie не вызывает беспокойства.

Затем создайте функцию под названием authenticateUser() или аналогичную, которая будет вызываться в начале каждого скрипта, чтобы гарантировать аутентификацию пользователя. Этот скрипт должен запрашивать базу данных, проверяя, имеет ли пользователь с вашим идентификатором пользователя хэш, соответствующий вашему хешу пользователя.

Например:

function authenticateUser($id, $hash, $databaseLink) {
    # SQL
    $sql = 'SELECT EXISTS(
               SELECT 1
               FROM 'tbl_users'
               WHERE 'id' = \''.mysql_real_escape_string($id).'\'
               AND 'hash' = \''.mysql_real_escape_string($hash).'\'
               LIMIT 1
           );';

    # Run Query
    if ($query = mysql_query($sql, $databaseLink)) {
        # Get the first row of the results
        # Assuming 'id' is your primary key, there
        # should only ever be one row anyway.       
        $result = mysql_fetch_row($query);

        # Casting to boolean isn't strictly necessary here
        # its included to indicate the mysql result should
        # only ever been 1 or 0.
        return (bool)($result[0]);
    } else {
        # Query error :(
        return false;
    }
}

Затем мы просто передаем authenticateUser() ID пользователя, hash (за ваши данные сеанса) и database link на базу database link (для подключения к базе данных, которое вы должны были открыть ранее).

Если authenticateUser() возвращает true, пользователь аутентифицируется. Если false, пользователь не является ИЛИ база данных недоступна или имеется ошибка SQL.

Обратите внимание, что это увеличит нагрузку на сервер, поскольку запрос на базу данных отправляется один раз на запрос страницы. Вероятно, не так уж и важно сделать это на гигантских проектах, в которые тысячи людей входят в систему в любой момент времени. Я уверен, что кто-то может предложить улучшения.

Кроме того, ожидание истечения срока действия файла cookie не является лучшим способом заставить людей, которые были неактивными, выйти из системы, так как вы никогда не должны доверять куки. Вместо этого вы можете добавить в столбец last_active который вы можете обновлять каждый раз, когда пользователь аутентифицируется. Это также увеличит нагрузку на сервер, но позволит вам вручную переопределить устаревшие записи, удалив hash для пользователей, которые, скажем, неактивны в течение 3 часов.

Ответ 2

Здесь решение не требует постоянного доступа к базе данных для работы...

(что позволит избежать необходимости проверять session_id() на значение базы данных каждый раз, когда вы запрашиваете/обновляете страницу, снимая стресс db/server)...

1. При входе в систему, закрепите уже существующий session_id, хранящийся в БД для этого пользователя, и сделайте следующее:

session_id("the pre-existing session id in the database goes here");
session_start();
session_destroy();

2. Затем запустите новый сеанс и сохраните этот новый session_id в базе данных, перезаписав предыдущий. Это приведет к выходу из предыдущей сессии этого пользователя, если есть один активный (фактически выйдя из другого парня, использующего эту учетную запись).

Попробуйте и дайте мне знать, если это трюк!

Ответ 3

Что вам нужно сделать, это проверить, были ли они активны последние несколько минут при попытке входа в систему. Это можно сделать с помощью штампа lastonline и его следует установить для каждого запроса страницы в таблице пользователя.

Если это не сделано с помощью javascript, вы можете проверить при входе в систему, если пользователь был активен последние 15 минут. Если вы не можете войти в систему как новый пользователь.

Вы также можете сделать это с помощью javascript. Сделайте вызов ajax, который срабатывает каждую минуту или около того.

<script>
setInterval(function() {
  // do the ajax call
}, 60000);
</script>

Позвольте этому вызову перейти к script, который отредактирует штамп lastonline в db пользователя. При попытке войти в систему вы проверяете пользователя db, если марка lastonline превысила минута, и у вас есть чек, если вы можете войти в систему. Это поможет, когда вы находитесь на странице, но вы не активны последние 15 минут, и вы не хотите, чтобы кто-то еще входил в систему.

Ответ 4

Вы можете изменить свою модель, чтобы войти в систему мог только самый последний пользователь.

Если вы записываете самый последний идентификатор сеанса, видимый для каждого пользователя, при повторном входе во второй раз вы можете уничтожить любой существующий сеанс, эффективно выгружая их.

Для обычного пользователя вещи кажутся "просто работающими". Если вы хотите запретить "ненормальным" пользователям распространять свои учетные данные, это должно быть сдерживающим фактором.

Ответ 5

Вам нужно создать уникальный идентификатор и сохранить его в базе данных. То, что я сделал, тоже создавалось. Я храню один в переменной сеанса и использую это, чтобы предотвратить захват сеанса, а другой в базе данных, чтобы предотвратить несколько логинов. Следующий код создаст уникальный идентификатор:

$unique_id = sha1('xzr4'.gethostbyaddr($_SERVER['REMOTE_ADDR']).$random_string.$_SERVER['HTTP_USER_AGENT'].'f8k2');

Если уникальный идентификатор не совпадает, вы просто регистрируете пользователя.

Ответ 6

Использование javascript на стороне клиента для отслеживания зарегистрированного пользователя ненадежно.

Вы можете получить тот же результат, просто создав поле lastlogindate в db и обновив его с последней отметкой времени входа пользователя.

При каждой попытке входа в систему, если now() - $lastlogindate > predefined_timeout, вы должны принять новый логин, иначе откажитесь.

Ответ 7

Это решение похоже на prograhammer, но не требует от вас возиться с переключением сеансов. Он не требует доступа к базе данных на каждой странице и не блокирует пользователя после того, как ему не удалось выйти из системы.

Добавьте поле для sessionID в вашу пользовательскую таблицу в базе данных.

Установите обработчик сеанса по умолчанию перед вызовом session_start() (необходим для работы следующей строки кода):

session_set_save_handler(new \SessionHandler());

При каждом успешном входе в систему извлекайте сохраненный $ sessionID из базы данных. Уничтожить старый сеанс с:

(new \SessionHandler())->destroy($sessionID);

Получите новый идентификатор сессии с:

$sessionID = session_id();

Сохраните новый идентификатор сеанса в базе данных.

Ответ 8

Чтобы ответить на ваш вопрос о downvote @Роберт Кабри.

Ajax-вызов JUST для поиска существующего сеанса в настоящее время является плохой идеей, поскольку у вас может быть другой способ, чем вызывать ajax каждую секунду, что увеличит ресурс вашего сервера.

Кроме того, если я деактивирую JS в своем браузере, я могу легко войти в систему с одним и тем же пользователем.