управление сеансами пользователя в php
Управление сеансами пользователя в php
Модуль сессии не гарантирует, что хранимая сессионная информация доступна только пользователю, который создал сессию. Необходимо принять дополнительные меры по защите конфиденциальности сессии, основываясь на связанных с ней данных.
Существует несколько способов утечки существующего идентификатора сессии третьим лицам. Например инъекции JavaScript, передача идентификатора сессии в URL, перехват пакетов, физический доступ к устройству и т.д. Перехваченный идентификатор сессии позволит третьим лицам получить доступ ко всем ресурсам, связанным с данной сессией. Во-первых, передача идентификатора сессии в URL. При переходе на внешний сайт идентификатор сессии пользователя и адрес ресурса могут попасть в статистику переходов данного сайта. Во-вторых, при более активной атаке возможно прослушивание сетевого трафика злоумышленником. Если канал передачи данных не зашифрован, идентификаторы сессии будут переданы в виде простого текста. В таком случае решением является обязательное использование SSL/TLS пользователями при доступе к сайту. Для этих целей следует применять HSTS.
Замечание: Даже HTTPS иногда может не защитить конфиденциальные данные. Например, уязвимости типа CRIME, BEAST могут позволить злоумышленнику получить доступ к данным. Многие сети используют прокси HTTPS MITM для аудита. Атакующие также могут настроить такие прокси.
Неадаптивное управление сессиями
В настоящее время PHP использует адаптивное управление сессиями по умолчанию. Адаптивное управление сессиями несёт дополнительные риски.
Определённый пользователем обработчик сохранения также может поддерживать строгий сессионный режим, путём реализации функции/метода проверки корректности идентификатора сессии. Все определённые пользователем обработчики сохранения обязаны его реализовывать.
Cookie с сессионным ID должна устанавливаться с указанием параметров domain, path, httponly, secure и, начиная с PHP 7.3, атрибут SameSite. Их приоритетность определяется браузерами. Опираясь на эту приоритетность, атакующий может может установить сессионный ID, который будет использоваться бесконечно. Применение session.use_only_cookies не решает эту проблему. session.use_strict_mode уменьшает риск. session.use_strict_mode=On, не допускает использование неинициализированных сессионных ID.
Доступ к истёкшей сессии не всегда означает атаку. Нестабильное сетевое соединение и/или немедленное удаление активной сессии может повлечь за собой подобное поведение.
Замечание: В версиях PHP до 7.1.0 необходимо использовать CSPRNG, то есть /dev/urandom или random_bytes() и функции хеширования для генерации нового идентификатора сессии. session_create_id() имеет встроенная функциональность обнаружения коллизий и генерирует идентификатор основываясь на INI-настройках. Использование session_create_id() является предпочтительной практикой.
Пересоздание идентификатора сессии
Разработчики НЕ ДОЛЖНЫ полагаться на механизм истечения срока действия идентификатора сессии с помощью session.gc_maxlifetime. Атакующие могут периодически получать доступ к сессии для предотвращения её срока действия и продолжать использовать идентификатор жертвы, включая аутентифицированные сессии.
Вместо этого, вы должны самостоятельно реализовать управление данными сессии базируясь на временной метке.
Несмотря на то, что менеджер сессий может прозрачно управлять временными метками, эта функциональность не реализована. Данные старых сессий сохраняются до момента запуска сборщика мусора. В то же время, разработчики должны убедиться, что данные истёкших сессий удалены. Однако разработчики НЕ должны удалять данные активных сессий немедленно. Например, никогда не вызывайте session_regenerate_id(true); и session_destroy() совместно для активных сессий. Это может показаться противоречивым, но это обязательное требование.
session_regenerate_id() по умолчанию не удаляет старые сессии. Устаревшие авторизованные сессии могут быть доступны для использования. Разработчики должны пресечь любую возможность использования старых сессий кем-либо, запретив доступ к истёкшим сессиям самостоятельно, используя временные метки.
Немедленное удаление активных сессий может повлечь нежелательные побочные эффекты. Сессия может прерваться из-за нестабильности сети или конкурентного доступа к сайту/приложению.
Потенциальный недобросовестный доступ будет невозможно отследить и проанализировать, если данные сессий будут немедленно удаляться.
Если пользователь пытается получить доступ к истёкшей сессии, вы не должны его предоставлять. В этом случае рекомендуется удалять статус «авторизован» со всех активных сессий пользователя, поскольку это очень похоже на атаку.
session.use_only_cookies и правильное использование session_regenerate_id() могут привести к персональной DoS посредством установки неудаляемой cookie. Если такое происходит, то вы можете попросить пользователя удалить cookie и предупредить его о возможных проблемах с безопасностью. Атакующий может устанавливать вредные cookie через уязвимость в веб-приложении (то есть JavaScript-инъекция), уязвимость в браузерном плагине и т.д.
Удаление данных сессии
Данные истёкших сессий должны быть недоступны и удалены. Существующий механизм управления сессиями делает это не очень хорошо.
Данные истёкших сессий надо удалять так быстро, как только возможно. С другой стороны, данные активных сессий НЕ ДОЛЖНЫ удаляться сразу же. Для обеспечения этих противоречивых требований, вы ДОЛЖНЫ самостоятельно реализовать механизм контроля за истёкшими сессиями на базе временных меток.
Сессии и блокировки
По умолчанию данные сессии заблокированы, чтобы избежать состояния гонки. Блокировка обязательна для обеспечения консистентности данных сессии между запросами.
Активные сессии
Разработчики должны следить за активными сессиями каждого пользователя и оповещать его, сколько есть активных сессий, с каких IP (и где географически), как долго они активны и т.д. PHP не сделает этого за вас. Вы должны это делать.
Есть несколько путей реализации. Вы можете хранить всю нужную информацию в специальной базе данных. Так что, когда сессия будет удалена сборщиком мусора, вы должны это отслеживать и соответственно обновлять свою базу данных.
Для подобной настройки требуется включение session.use_strict_mode. Убедитесь, что эта опция включена, иначе база данных активных сессий может быть скомпрометирована.
Управление сессиями на базе временных меток является обязательным для определения устаревших сессий. Если обнаружена попытка доступа к устаревшей сессии, необходимо сбросить флаги авторизации для всех активных сессий пользователя.
Сессии и автоматический вход
Разработчики НЕ ДОЛЖНЫ использовать долгоживущие сессии для реализации автоматического входа в систему, потому что это резко повышает вероятность кражи сессии. Автоматический вход в систему должен реализовываться разработчиком самостоятельно.
Если пользователь не авторизован, проверьте, корректен ли одноразовый ключ автологина. Если ключ корректен, авторизуйте пользователя и установите ему новый одноразовый ключ. Ключ автологина обязательно должен быть одноразовым, то есть никогда не используйте его повторно, а всегда создавайте новый.
Разработчик должен реализовать функционал, который отключает автоматический вход в систему и удаляет ненужные «cookie», установленные для его реализации.
CSRF (Межсайтовая подделка запроса)
Сессии и авторизация не защищают от атак типа CSRF. Разработчики должны самостоятельно реализовывать защиту.
output_add_rewrite_var() может быть использована для защиты от CSRF. Читайте руководство для подробностей.
Замечание: До PHP 7.2.0 использовался один и тот же буфер вывода и INI-настройки для «trans sid». Так что использование output_add_rewrite_var() с PHP более ранних версий не рекомендуется.
Многие фреймворки поддерживают защиту от CSRF. Обратитесь к документации своего фреймворка для более подробной информации.
Начиная с PHP 7.3, для сессионной cookie можно установить атрибут SameSite. Это обеспечит дополнительную защиту против CSRF.
Управление сеансами в РНР.
Что такое управление сеансом.HTTP иногда называют «протоколом без состояния». Это означает, что данный протокол не имеет встроенного способа поддержки состояния между двумя транзакциями. Когда пользователь запрашивает друг за другом две страницы, HTTP не обеспечивает возможности уведомить, что оба запроса исходят от одного и того же пользователя. Таким образом, идея управления сеансами заключается в обеспечении отслеживания пользователя в течение одного сеанса связи с Web-сайтом. Если это удастся осуществить, мы сможем легко поддерживать подключение пользователя и предоставление ему содержимого сайта в соответствии с его уровнем прав доступа или персональными настройками. Мы сумеем отслеживать поведение пользователя.
В более ранних версиях РНР управление сеансами осуществлялось средствами PHPLib, базовой библиотеки РНР, которая и сейчас является полезным набором инструментов. Об этом можно прочесть на
Четвертая версия РНР включает собственные встроенные функции управления сеансом. Концептуально они подобны PHPLib, но PHPLib помимо этого обеспечивает em и ряд дополнительных функциональных возможностей. Так что если окажется, что эти собственные функции не вполне отвечают вашим требованиям, ничто не мешает рассмотреть возможность использования PHPLib.
Основные функциональные средства управления сеансом.Для запуска сеанса в РНР используется уникальный идентификатор сеанса, представляющий собой зашифрованное случайное число. Идентификатор сеанса генерируется РНР и сохраняется на стороне клиента в течение всего времени жизни сеанса. Для хранения идентификатора сеанса используется либо cookie-набор на компьютере пользователя, либо URL.
Идентификатор сеанса играет роль ключа, обеспечивающего возможность регистрации некоторых специфических переменных в качестве так называемых переменных сеанса. Содержимое этих переменных сохраняется на сервере. Единственной информацией, «видимой» на стороне клиента, является идентификатор сеанса. Если во время определенного подключения к вашему сайту идентификатор сеанса является «видимым» либо в cookie-наборе, либо в URL, имеется возможность получить доступ к переменным сеанса, которые сохранены на сервере для данного сеанса. По умолчанию переменные сеанса хранятся в двумерных файлах на сервере (при желании способ хранения можно изменить и использовать вместо двумерного файла базу данных, но для этого потребуется написать собственную функцию).
Скорее всего, придется иметь дело с Web-сайтами, на которых для хранения идентификатора сеанса используется URL. Если в вашем URL имеется строка данных, которые выглядят случайными, то это, скорее всего, свидетельствует об использовании одной из двух описанных здесь разновидностей управления сеансом.
Другим решением проблемы сохранения состояния на протяжении некоторого количества транзакций, при наличии чистого внешнего вида URL, являются cookie-наборы.
Что такое cookie-набор?cookie-набор — это небольшой фрагмент информации, который сценарии сохраняют на клиентской машине. Чтобы установить cookie-набор на машине пользователя, необходимо отправить ему HTTP-заголовок, содержащий данные в следующем формате.
Set-Cookie: NAME=VALUE; [expires=DATE;] [path=PATH;] [domain=DOMAIN_NAME;] [secure]
Это создаст cookie-набор с именем NAME и значением VALUE. Все остальные параметры являются необязательными. В expires задается дата истечения срока действия, после наступления которой cookie-набор перестанет рассматриваться как актуальный. Заметим, что если дата истечения срока действия не задана, cookie-набор будет постоянным, пока его кто-нибудь не удалит вручную — либо вы, либо сам пользователь). Два параметра path и domain применяются для определения одного или нескольких URL, к которым относится данный cookie-набор. Ключевое слово secure означает, что cookie-набор не может отправляться через простое HTTP-соединение.
Когда браузер соединяется с URL, он сначала ищет cookie-наборы, хранящиеся локально. Если какие-либо из них относятся к URL, с которым установлено соединение, они передаются обратно на сервер.
Установка cookie-наборов из РНР.cookie-наборы в РНР можно установить вручную, используя функцию setcookie(). Она имеет следующий прототип:
int setcookie (string name [, string value [, int expire [, string path [, string domain [, int secure]]]]])
Параметры в точности соответствуют тем, которые используются в описанном выше заголовке Set-Cookie. Если cookie-набор установлен как
setcookie («mycookie», «value»);
Для удаления cookie-набора необходимо вызвать setcookie() с тем же именем, но без указания значения. Если cookie-набор устанавливался с другими параметрами (такими как специфические URL или даты истечения), потребуется отправить те же параметры повторно, иначе cookie-набор удален не будет.
Для установки cookie-набора вручную можно воспользоваться также функцией Header() и описанным выше синтаксисом представления cookie-набора. Однако при этом следует иметь в виду, что заголовки cookie-наборов должны отправляться перед всеми другими заголовками (иначе заголовки cookie-наборов работать не будут).
Использование cookie-наборов в сеансах.При использовании cookie-наборов возникают некоторые проблемы: есть браузеры, которые не принимают cookie-наборы, а есть пользователи, которые запрещают использование cookie-наборов в своих браузерах. Это одна из причин, по которым в сеансах РНР используются двойной метод cookie-набор/адрес URL (ниже этот вопрос рассматривается более подробно).
В сеансе РНР нет необходимости задавать cookie-наборы вручную. Это за вас сделают функции сеанса. Для того чтобы просмотреть содержимое cookie-набора, установленное при управлении сеансом, можно воспользоваться функцией session_get_cookie_params(). Она возвращает ассоциативный массив, содержащий элементы lifetime, path и domain.
Можно использовать также:
Этот оператор устанавливает параметры cookie-набора для сеанса.
Если возникнет желание получить более подробную информацию о cookie-наборах, то за консультациями по спецификации cookie-наборов следует обратиться на сайт компании Netscape:
Можно встроить идентификатор сеанса в ссылку, чтобы обеспечить его передачу. Идентификатор сеанса будет запоминаться в константе SID. Для того, чтобы передать его вручную, его потребуется добавить в конец ссылки, аналогично параметру GET:
Реализация управления простым сеансом.Основными этапами использования сеанса являются следующие:
· Регистрация переменных сеанса
· Использование переменных сеанса
· Отмена регистрации переменных и закрытие сеанса
Заметим, что все перечисленные этапы не обязательно могут содержаться в одном сценарии, и некоторые из них могут находиться в нескольких сценариях. Рассмотрим каждый из этих этапов последовательно.
Запуск сеанса.Прежде чем можно будет воспользоваться функциональными возможностями сеанса, следует запустить сам сеанс. Существует три способа сделать это.
Первый (и самый простой) заключается в том, что сценарий начинается с вызова функции
Эта функция проверяет, существует ли идентификатор текущего сеанса. Если нет, она его создает. Если же идентификатор текущего сеанса уже существует, она загружает зарегистрированные переменные сеанса, чтобы они стали доступными для использования.
Надо отметить, что это прекрасный способ — вызов session_start() в начале сценариев, в которых используются сеансы.
Второй способ заключается в том, что сеанс запускается при попытке зарегистрировать переменные сеанса (см. далее).
Третий способ запустить сеанс — задать установки РНР, при которых сеанс будет запускаться автоматически, как только кто-либо посетит ваш сайт. Для этого следует воспользоваться опцией session.auto_start в файле php.ini (более подробно указанный способ будет описан при рассмотрении конфигурации).
$myvar = 5; session_register(«myvar»);
За один прием можно зарегистрировать более одной переменной, передав разделенный запятыми список имен переменных:
Переменные сеанса не могут быть перезаписаны данными GET или POST. Это хорошо с точки зрения обеспечения безопасности, однако сопряжено с некоторыми ограничениями при кодировании.
С другой стороны, от вас потребуется тщательность при проверке на предмет того, установлены ли уже переменные сеанса (например, с использованием isset() либо empty()). Кроме того, следует иметь в виду, что переменные могут быть установлены пользователем через GET или POST. Проверить, является ли переменная зарегистрированной переменной сеанса, можно обратившись к функции session_is_registered(). Вызов функции выполняется следующим образом:
Отмена регистрации переменных и завершение сеанса.После окончания работы с переменной сеанса ее регистрацию можно отменить, воспользовавшись функцией
По завершении сеанса сначала потребуется отменить регистрацию всех переменных, а затем для обнуления идентификатора сеанса вызвать
Листинг 6.15. page1.php — запуск сеанса и регистрация переменной
Мы зарегистрировали переменную и установили ее значение.
Заметим, что мы изменили значение переменной уже после ее регистрации. Можно, однако, сделать и наоборот — установить значение, а после этого зарегистрировать переменную. Конечное значение переменной на странице — это то значение, которое будет доступно на последующих страницах. В конце сценария переменная сеанса преобразуется в последовательную форму (сериализуется), или замораживается, до своей перезагрузки через следующий вызов session_start(). Таким образом, следующий сценарий начинается с вызова session_start(). Сценарий показан в листинге 6.16
Листинг 6.16. page2.php — получение доступа к переменной сеанса и отмена регистрации
И наконец, мы переходим к page3.php, последнему сценарию в рассматриваемом примере.
Листинг 6.17. page3.php — завершение сеанса
Конфигурирование управления сеанса.А сейчас мы предлагаем ознакомиться с набором опций конфигурации для сеансов, которые можно установить в своем файле php.ini. В табл. 7.8 перечисляются некоторые из наиболее полезных опций вместе с их кратким описанием.
Таблица 6.8. Опции конфигурации сеанса
Имя опии и | Значение no умолчанию | Действие |
session.auto_start | 0 (запретить) | Автоматический запуск сеансов. |
session.cache_expire | Установка времени жизни для кэшированных станиц сеанса (в минутах). | |
session.cookie_domain | none | Домен для установки в cookie-наборе сеанса. |
session.cookie_lifetime | Определяет продолжительность существования cookie-набора идентификатора сеанса на машине пользователя. По умолчанию 0 — пока не будет закрыт браузер. | |
session.cookie_pafh | / | Путь для установки в cookie-наборе сеанса. |
session.name | PHPSESSID | Имя сеанса, которое в системе пользователя используется как имя cookie-набора. |
session.save_handler | файлы | Определяет место хранения данных сеанса. Здесь можно указать базу данных, однако для этого потребуется реализовать собственные функции. |
session.save_path | /tmp | Путь к месту хранения данных сеанса. В более общем случае для определения и обработки передаваемых на хранение аргументов используется session.save_handler. |
session.use_cookies | 1 (разрешить) | Конфигурация сеанса с возможностью использования cookie-наборов на стороне клиента. |
Выполнение аутентификации пользователей средствами управления сеансом.В завершение рассмотрим более важный пример использования контроля сеанса.
Наиболее часто, пожалуй, управление сеансом применяется в целях отслеживания пользователей после того, как они были аутентифицированы через механизм входной регистрации. В предлагаемом примере можно видеть, как эти функциональные возможности обеспечиваются за счет сочетания аутентификации при помощи базы данных MySQL и использования механизма управления сеансом.
В нашем примере мы воспользуемся базой данных аутентификации, которая была создана ранее. Это требуется для работы с модулем mod_auth_mysql. Пример включает три простых сценария. Первый, authmain.php, обеспечивает форму для входной регистрации и аутентификации пользователей Web-сайта. Второй, members_only.php, представляет информацию только для тех пользователей, которые успешно прошли входную регистрацию. Третий, logout.php. реализует выход пользователей из системы.
Первая страница предоставляет пользователю возможность войти в систему. В случае, если он предпримет попытку получить доступ к секции Members, не пройдя входную регистрацию, будет выдано сообщение. Если же пользователь сначала прошел входную регистрацию (с именем пользователя: testuser и паролем: test123, как было задано ранее), а потом попытался войти на страницу Members, он увидит соответствующее сообщение.
Давайте посмотрим на код приложения. Большая часть кода сосредоточена в сценарии authmain.php, приведенном в листинге 6.18. Давайте изучим его более подробно.
Листинг 6.18. authmain.php — Основная часть приложения аутентификации
// если пользователь как раз пытается зарегистрироваться
$db_conn = mysql_connect(«localhost», «webauth», «webauth»);
$query = «select * from auth where name=’$userid'»