Определить, существует ли session_id (some-id)

Возможный дубликат:
Определить, существует ли сеанс PHP

Руководство по PHP, похоже, не предоставляет средств для проверки существования данного session_id. Например, session_id() имеет необязательный параметр id, но он заменяет существующий id, а не выполняет поиск по желаемому методу: session_id_exists(some-id)

Зачем мне проверять, существует ли данный session_id? Пример использования – служба подписки на спорт, где совместное использование пароля стало проблемой. При входе в систему я сохраняю идентификатор сеанса пользователя в БД и использую его для сравнения с любыми другими существующими идентификаторами сеанса, прикрепленными к данному userID.

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

Я предполагаю, что есть простой способ добиться этого...


person virtualeyes    schedule 17.11.2011    source источник
comment
Для этого нет встроенного способа (AFAIK) - я думаю, вам придется зацикливать файлы в каталоге данных сеанса и проверять наличие соответствующего имени файла. Моей немедленной реакцией было то, что вы могли бы сделать это только с базой данных, но теперь я думаю об этом, я не могу понять, как...   -  person DaveRandom    schedule 17.11.2011
comment
Я не понимаю, как здесь поможет получение идентификатора сеанса. Идентификатор сеанса генерируется при первом посещении сеанса. Два пользователя никогда не получат одинаковый идентификатор сеанса. Что вы можете сделать, так это проверить при каждом просмотре страницы, соответствует ли идентификатор сеанса текущего пользователя идентификатору сеанса, хранящемуся в БД, и, если нет, выйти из системы.   -  person CodeCaster    schedule 17.11.2011
comment
Мне приходит в голову, что это может быть функция безопасности; т. е. не разрешать поиск данных сеанса, не связанных с сеансом текущего пользователя. Мое намерение состоит в том, чтобы при обнаружении нескольких пользователей, вошедших в одну учетную запись, выйти из них всех (отменить их сеансы), заблокировать учетную запись и потребовать смены пароля.   -  person virtualeyes    schedule 17.11.2011
comment
@CodeCaster, я бы предпочел избегать подхода к просмотру каждой страницы и накладных расходов на поиск в БД (хотя и минимальных). re: 2 пользователя никогда не получат один и тот же сеанс, в этом и есть смысл. Если средство отслеживания сеансов БД содержит 1 или более записей, прикрепленных к одному идентификатору пользователя, и сохраненный идентификатор сеанса активен, то мы знаем, что пароль был утерян, поскольку пользователь, пытающийся войти в систему, и пользователи, которые уже вошли в систему, не могут быть одним и тем же лицом. (также устройство слежения и IP)   -  person virtualeyes    schedule 17.11.2011
comment
@CodeCaster Я думаю, вы немного упустили суть - ему нужно проверить, связаны ли с двумя существующими сеансами один и тот же пользователь. Таким образом, когда пользователь входит в систему с идентификатором сеанса, он сохраняется, но если другой пользователь входит в систему с тем же именем пользователя, но другим идентификатором сеанса, пароль был передан кому-то. Суть в том, чтобы проверить, существуют ли два идентификатора сеанса одновременно - возможно, у законного пользователя будет два идентификатора сеанса, но срок действия одного из них истек. Случай имеет смысл, хотя подход немного ошибочен (например, если один пользователь использует 2 браузера).   -  person DaveRandom    schedule 17.11.2011
comment
Я бы предпочел избежать подхода с просмотром каждой страницы и накладных расходов на поиск в БД (хотя и минимальных) Да, и вы предпочитаете сканировать все сеансы, чтобы увидеть, содержат ли они идентификатор текущего пользователя. Вы сохраняете один идентификатор сеанса для каждого пользователя или у вас есть таблица, содержащая идентификатор сеанса и идентификатор пользователя?   -  person CodeCaster    schedule 17.11.2011
comment
@DaveRandom, да, моя первоначальная мысль заключалась в том, чтобы пройти через каталог сеанса, но это создает проблемы / проблемы с безопасностью при реализации. Я могу просто отказаться от этой идеи и перейти к заданию root cron, которое проверяет журнал пользователей, ищет одновременные (в течение 20 минут) входы в систему с разных IP-адресов в одной учетной записи, а затем блокирует/принудительно сбрасывает пароль соответственно.   -  person virtualeyes    schedule 17.11.2011
comment
Чтобы кто-то мог правильно ответить на этот вопрос, вам нужно показать, как вы в настоящее время храните свои логины и сеансы. Вы определенно не должны не сканировать все файлы сеанса, чтобы увидеть, содержит ли один из них тот же идентификатор пользователя, что и текущий сеанс.   -  person CodeCaster    schedule 17.11.2011
comment
+1 @Дэйв. Я бы учитывал только активные сеансы, а также сравнивал IP-адрес, хранящийся в БД, с IP-адресом пользователя при входе в систему (чтобы избежать блокировки действительной учетной записи пользователя в случае, если они по какой-то причине попытаются получить доступ к странице входа напрямую, когда уже вошли в систему в). Проблема с браузером 2 актуальна, точнее клиент уходит с работы, садится в машину и, застряв в пробке, проверяет сайт на своем iPhone.   -  person virtualeyes    schedule 17.11.2011
comment
@CodeCaster, правда, сканирование всего каталога сеанса и поиск идентификатора пользователя grep было бы довольно неэффективным ;-) re: таблица БД: идентификатор пользователя, идентификатор сеанса, ip, logTime. Что-то такое.   -  person virtualeyes    schedule 17.11.2011


Ответы (4)


Как насчет того, чтобы сделать что-то подобное в верхней части index.php?

// Update current userid/session record with current timestamp
mysql_query("UPDATE sessions SET last_activity = CURRENT_TIMESTAMP() WHERE user = '$username' AND sid = '".session_id()."'");
// Search for multiple records with timestamp in the last 20 minutes where user id is the same
$result = mysql_fetch_assoc(mysql_query("SELECT COUNT(*) AS current_sessions FROM sessions WHERE user = '$username' AND last_activity > '".date('Y-m-d H:i:s', time() - 1200)."'"));
if ($result['current_sessions'] > 1) {
  // handle duplicates here
}

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

person DaveRandom    schedule 17.11.2011
comment
Конечно, это сработало бы хорошо, просто не предпочтительный подход, учитывая, imo, ненужные накладные расходы на БД. Если задание cron позволяет заблокировать учетные записи с общим паролем и не нести накладных расходов, то это путь, который я исследую в первую очередь. - person virtualeyes; 17.11.2011
comment
@virtualeyes, не желающий выполнять один запрос, звучит для меня как преждевременная оптимизация. - person CodeCaster; 18.11.2011
comment
@CodeCaster, зависит, как насчет сайта, который одновременно обрабатывает много пользователей? Это сводится к выполнению 2 дополнительных запросов для каждого отдельного запроса страницы (подход Дейва), чтобы определить, кто вошел в систему под учетной записью X), или во время входа в систему проверить количество активных сеансов под учетной записью X. Последнее решение требует небольших затрат. чтобы не было накладных расходов; Сейчас выложу через некоторое время, тестирую... - person virtualeyes; 18.11.2011

После комментария CodeCaster:

Вместо проверки идентификаторов сеанса PHP вы должны для каждого подключения/отключения пользователя и истечения срока действия сеанса поддерживать список подключенных в настоящее время пользователей (возможно, вместе со временем подключения, IP-адресом и т. д.), например, в таблице БД.

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

Вы также можете попытаться создать небольшой скрипт, перебирающий все файлы сеанса (если вы используете хранилище по умолчанию), использовать для него «unserialize()» и проверить, имеют ли несколько файлов сеанса одинаковые идентификаторы пользователя (при условии, что вы их храните в $_SESSION)

EDIT: поскольку это должно быть сделано для каждого пользовательского соединения, подход к таблице БД (например, что предлагает CodeCaster) кажется лучше.

person Frosty Z    schedule 17.11.2011
comment
Опять же, несмотря на то, что накладные расходы невелики, выполнение запросов по каждому отдельному запросу страницы для нескольких пользователей, вошедших в систему под одной учетной записью, неэффективно. Теперь я склоняюсь к тому, чтобы запустить раннее задание AM root cron, которое проверяет журнал пользователей, проверяет одновременные входы в систему в течение дня и соответственно блокирует учетные записи (наряду с отправкой предупреждающего электронного письма). Нулевые накладные расходы и достижение цели блокировки учетных записей с общим паролем. - person virtualeyes; 17.11.2011
comment
@virtualeyes, даже если вы собираетесь сделать это с помощью cronjob, вам все равно нужно хранить идентификаторы времени / сеанса, в которые пользователь выполнял действия, что в любом случае включает запись в файл или базу данных при каждой загрузке страницы - другого пути нет это потому, что PHP удалит файлы сеанса с истекшим сроком действия, поэтому не будет данных для проверки в начале утра. - person DaveRandom; 17.11.2011
comment
@virtualeyes На самом деле, вы можете попробовать установить session.gc_probability на 0 на всех своих страницах, а затем вручную выполнить сборку мусора с помощью cronjob, но я не рекомендую этого, если только сервер не выделен для этого сайта и только для этого сайта - даже тогда это рискованно. - person DaveRandom; 17.11.2011
comment
@Dave, неправда, при входе в систему я сохраняю идентификатор пользователя, ip, logTime, браузер и т. д. В противном случае для межстраничных запросов я ничего не делаю. Журнал аудита находится в таблице журнала. Для учетных записей с общим паролем это совершенно ясно, просто взглянув на журналы (почти одно и то же время входа, но разные IP-адреса и разные браузеры) - person virtualeyes; 17.11.2011
comment
@virtualeyes Но он не вошел в систему в течение x времени, о котором вы беспокоитесь, он был активен в течение x времени. Если я войду в 9 утра и останусь в системе весь день, а кто-то еще войдет в систему с теми же данными в 16:00, простая проверка времени входа не уловит тот факт, что 2 пользователя вошли в систему с одним и тем же именем пользователя в одно и то же время. время, потому что время входа в систему составляло 7 часов... следовательно, вам нужно регистрировать активность, а не входы в систему - person DaveRandom; 17.11.2011
comment
@Dave Я согласен, но в данном конкретном случае нет ;-) Нет входа в систему в течение всего дня, вам придется щелкать страницу каждые 20 минут (время истечения сеанса), что нереально, учитывая, что пользователи входят в систему. чтобы увидеть, была ли опубликована новая статья, в среднем 1 или 2 статьи в день. Другими словами, вы должны быть довольно сумасшедшим, чтобы ваш сеанс оставался активным весь день. Что совершенно ясно видно из журнала, так это то, что географически разрозненные пользователи (по IP-адресу) заходят на сайт с разницей в 10-20 минут, что является явным признаком совместного использования пароля. - person virtualeyes; 17.11.2011

Вам следует создать собственную реализацию Состояния сеанса, которая предлагает интерфейс для действий, необходимых для вашего варианта использования.

class SessionState
{
    public function idExists($id)
    {
        $gateway = new SessionGateway();
        $result = $gateways->searchById($id);
        # ... 
    }
}

Если вы реализовали детали, вы можете просто использовать объект(ы) в своем приложении.

person hakre    schedule 17.11.2011
comment
Это довольно редко, я предполагаю, что под реализацией состояния сеанса вы имеете в виду сохранение состояния сеанса в БД вместо реализации в памяти php по умолчанию? Не собираюсь посвящать свою жизнь этому проекту, будет достаточно быстрого и грязного решения (@DaveRandom возглавляет список в настоящее время), php достаточно неприятен, чтобы пересматривать его, не говоря уже о мысли о том, чтобы снова делать в нем реальную работу ;-) - person virtualeyes; 17.11.2011
comment
Лолз, как бы он ни был редок, настолько же быстро он кодируется. В любом случае, это совершенно не зависит от выбранного вами уровня хранения. Шлюз позаботится об этом, просто делайте все, что вам нужно, и готово. Если вы позже перейдете на хранилище на основе РСУБД вместо хранилища файловой базы данных, сделайте это. Если вам нужен синтаксический анализатор файлов сеансов, воспользуйтесь библиотекой Serialized. - person hakre; 17.11.2011
comment
проблема в том, что я понятия не имею, о чем вы говорите, как и никто другой из-за отсутствия комментариев / голосов. Однако, учитывая ваш тяжелый рейтинг SO, ясно, что вы знаете, о чем говорите ;-) Поэтому, говоря о довольно разреженном, я имел в виду, не могли бы вы уточнить, что вы подразумеваете под, например, new SessionGateway()? Другими словами, класс SessionState еще не запускает момент озарения, хотя он может быть немного более подробным и/или ссылаться на пример реализации. Спасибо... - person virtualeyes; 17.11.2011
comment
@virtualeyes: Хорошо, забудьте о шаблонах, они более или менее инкапсулируют то, что меняется в вашей ситуации, и должны облегчить вашему приложению решение общей проблемы. - person hakre; 17.11.2011
comment
@virtualeyes: Но вы более сосредоточены на голой проблеме: вы хотите, чтобы идентификатор пользователя был идентификатором сеанса. Вы можете зашифровать его с помощью секрета, а затем использовать как session_id (как вы уже знаете, вы можете установить его). Рядом с этим сохраните IP внутри сеанса. Если пользователь повторно использует сеанс с другим IP-адресом, активируется сброс пароля. Вы можете легко сделать это с дополнительным сеансом (используя два сеанса последовательно), текущим и новым идентификатором пользователя. Просто не используйте файлы cookie для нового, чтобы он оставался приватным. Отфильтруйте идентификатор сеанса для общедоступного, который не может вводить идентификатор пользовательского сеанса. - person hakre; 17.11.2011
comment
+1 @hakre, умница, не подумал об установке зашифрованного идентификатора сеанса. Выполнение запросов при каждом отдельном запросе для отслеживания активности пользователя просто неэффективно, состояние сеанса уже показывает, кто вошел в систему под данной учетной записью; достижение указанного состояния - это другое дело, лучше всего подходящее для работы root cron, imo. Запускается каждые 10 минут, cron может выполнять пакетную обработку зарегистрированных пользователей, находить общие пароли; бросить молоток (заблокировать учетную запись) и (поскольку root имеет полный доступ к каталогу сеанса) сдуть незаконные пользовательские сеансы, сея чудесный wtf-happend havok ;-) - person virtualeyes; 17.11.2011

Прежде всего, это было хорошим исследованием способов предотвращения обмена паролями.

Моя внутренняя реакция на отслеживание активности пользователей по каждому запросу (т.е. решение с двумя запросами, предложенное @DaveRandom) была ааааа, нет! Как указывает @CodeCaster, вероятный случай преждевременной оптимизации, но эй, у нас есть небольшая (несколько тысяч), но бешеная пользовательская база, которая будет очень довольна началом хоккейного сезона и получением результатов игры. На сайте есть работал быстро в течение многих лет, не хочу раскачивать лодку, это платная услуга, поэтому производительность должна быть отличной.

Хорошо, решение:

Пользователь apache имеет доступ для чтения/записи к файлам сеанса в каталоге сеанса. Записав session_id во время входа в систему, у нас есть ингредиенты для блокировки учетных записей с общим паролем. При успешном входе:

- Loop through stored session ids related to target account
- if /path/to/session-id-file not empty, increment login counter
- if login counter exceeds number of users allowed for a given plan:
- delete all session files related to target account
- lock the account and force a password reset

Накладные расходы минимальны, реализация — торт, проблема решена.

Примечание. Первоначально я думал, что невозможно получить доступ к файлам каталога сеанса без создания петли безопасности; однако это не так (по крайней мере, в моей стандартной настройке CentOS 5). Вы не можете получить идентификатор session_id, не связанный с сеансом текущего пользователя, но вы можете сохранить идентификатор сеанса данного пользователя и получить доступ к файлу сеанса, в котором хранится его сеанс, из сеанса любого пользователя (включая удаление файла). Ключ имеет идентификатор сеанса для поиска соответствующего /path/to/session-file

person virtualeyes    schedule 18.11.2011