Базовые принципы управления сессиями

Безопасность сессии

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

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

Существует несколько способов утечки существующего идентификатора сессии третьим лицам. Такая утечка позволяет злоумышленнику получить доступ ко всем данным, связанным с конкретным идентификатором сессии. Во-первых, передача идентификатора сессии в URL. При переходе на внешний сайт идентификатор сессии пользователя и адрес ресурса могут попасть в статистику переходов данного сайта. Во-вторых, при более активной атаке возможно прослушивание сетевого трафика злоумышленником. Если канал передачи данных не зашифрован, идентификаторы сессии будут переданы в виде простого текста. В таком случае решением является обязательное использование SSL пользователями при доступе к сайту. Для этих целей следует применять HSTS.

Замечание: Даже HTTPS иногда не может защитить конфиденциальные данные. Например уязвимости типа CRIME, BEAST могут позволить злоумышленнику получить доступ к данным. Многие сети используют HTTPS MITM прокси для аудита. Атакующие также могут настроить такие прокси.

Неадаптивное управление сессиями

В настоящее время PHP использует адаптивное управление сессиями. Адаптивное управление сессиями несет дополнительные риски.

С версии PHP 5.5.2 доступна опция session.use_strict_mode. При её включении и при условии, что обработчик сохранения сессий её поддерживает, неинициализированный сессионный ID отвергается и создается новый. Это защищает от атак, которые принуждают пользователя использовать заранее извесный ID. Ататкующий может размещать ссылки или отправлять письма, которые содержат сессионный ID. Например http://example.com/page.php?PHPSESSID=123456789 . Если опция session.use_trans_sid включена, то жертва откроет сессию с этим идентификатором. Опция session.use_strict_mode уменьшает этот риск.

Внимание

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

Cookie с сессионным ID должна устанавливаться с указанием domain, path, httponly и secure. Их приоритетность определяется браузерами. Опираясь на эту приоритетность, атакующий может может установить сессионный ID, который будет использоваться бесконечно. Применение session.use_only_cookies не решает эту проблему. session.use_strict_mode уменьшает риск. session.use_strict_mode=On, не допускает использование неинициализированных сессионных ID.

Замечание: Даже при уменьшении риска с помощью session.use_strict_mode атакующий все ещё может заставить пользователя использовать уже инициализированную сессию, созданную атакующим. Например JavaScript-инъекция. Эта атака может быть смягчена, если следовать рекомендациям этого руководства. Если вы следуете этому руководству, вы должны разрешить session.use_strict_mode, использовать управление сессиями на основе временных меток и пересоздавать идентификатор сессии с помощью session_regenerate_id(), как рекомендуется. Если вы всё это сделаете, идентификатор сессии злоумышленника в итоге будет удален. Если произошел доступ к истекшей сессией, вы должны сохранить все данные активных сессий пользователя. Это позволит для дальнейшего расследования причин произошедшего. После этого, принудительно заставьте пользователя выйти из всех активных сессий, т.е. потребуйте от пользователей переавторизации. Это позволит предотвратить атаку с использованием краденной сессии.

Внимание

Доступ к истекшей сессии не всегда означает атаку. Нестабильное сетевое соединение и/или немедленное удаление активной сессии может повлечь за собой подобное поведение.

С PHP 7.1.0 добавлена функция session_create_id(). Эта функция может быть полезна для создания идентификатора сессии с использованием идентификатора пользователя в качестве префикса для достижения большей управляемости. При её использовании крайне важно разрешать session.use_strict_mode. Иначе, недобросовестные пользователи смогут устанавливать поддельные идентификаторы сессий для других пользователей.

Замечание: В версиях PHP до 7.1.0, необходимо использовать CSPRNG, т.е. /dev/urandom или random_bytes() и функции хеширования для генерации идентификаторов сессий. session_create_id() имеет встроенный функционал обнаружения коллизий и генерирует идентификатор основываясь на INI-настройках. Использование session_create_id() является предпочтительной практикой.

Пересоздание идентификатора сессии

Использование session.use_strict_mode - это хорошо, но недостаточно. Разработчик должен использовать session_regenerate_id() для обеспечения безопасности сессий.

Пересоздание идентификаторов сессий сильно уменьшает риск кражи сессии, соответственно надо на переодической основе запускать session_regenerate_id(). Например пересоздавать идентификатор сессии каждые 15 минут для особо секретных данных. Даже если сессию украдут, она достаточно скоро станет истекшей и попытка её использовать приведет к ошибке истекшей сессии.

Идентификатор сессии должен пересоздаваться при аутентификации пользователя. Функция session_regenerate_id() должна вызываться до записи авторизационной информации в $_SESSION. (В PHP 7.0.0, session_regenerate_id() сохраняет данные текущей сессии автоматически). Убедитесь, что только текущая сессия отмечена как авторизованная.

Разработчики НЕ ДОЛЖНЫ полагаться на механизм истечения срока действия идентификатора сессии с помощью session.gc_maxlifetime. Атакующие могут переодически получать доступ к сессии для предотвращения её срока действия и продолжать использовать идентификатор жертвы, включая аутентифицированные сессии.

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

Внимание

Несмотря на то, что менеджер сессий может прозрачно управлять временными метками, этот функционал не реализован. Данные старых сессий сохраняются до момента запуска сборщика мусора. В то же время, разработчики должны убедиться, что данные истекших сессий удалены. Однако разработчики НЕ должны удалять данные активных сессий немедленно. Например, никогда не вызывайте session_regenerate_id(true); и session_destroy() для активных сессий. Это может показаться противоречивым, но это обязательное требование.

session_regenerate_id() по умолчанию не удаляет старые сессии. Старые авторизованные сессии могут быть доступны для использования. Разработчики должны пресечь любую возможность использования старых сессий кем-либо, должны запретить доступ к истекшим сессиям самостоятельно, используя временные метки.

Внимание

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

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

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

Вы не должны запрещать доступ к старым сессиям сразу же после вызова session_regenerate_id(). Необходимо подождать несколько секунд для стабильных сетевых соединений и несколько минут для нестабильных, например для WiFi или мобильного интернета.

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

session.use_only_cookies и правильное использование session_regenerate_id() могут привести к персональной DoS. Если такое происходит, то вы можете попросить пользователя удалить cookie и предупредить его о возможных проблемах с безопасностью. Атакующий может устанавливать вредные cookie через уязвимость в веб-приложении (т.е. JavaScript-инъекция), уязвимость в браузерном плагине и т.д.

Внимание

Не недооценивайте риск DoS. use_strict_mode=On обязателен для общей безопасности идентификаторов сессий! Все сайты должны использовать use_strict_mode.

DoS (отказ в обслуживании) может произойти только тогда, когда аккаунт находится под атакой взломщиков. Наиболее частая предпосылка для него - JavaScript-инъекция.

Удаление данных сессии

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

Данные истекших сессий надо удалять так быстро, как только возможно. С другой стороны, данные активных сессий НЕ ДОЛЖНЫ удаляться сразу же. Для обеспечения этих противоречивых требований, вы ДОЛЖНЫ самостоятельно реализовать механизм контроля за истекшими сессиями на базе временных меток.

Устанавливайте и управляйте временными метками жизни сессии через $_SESSION. Запрещайте доступ к данным истекших сессий. Если обнаружена попытка доступа к данным устаревшей сессии, снимайте статус авторизации со всех активных сессий пользователя и вынуждайте его переавторизоваться. Доступ к данным истекшей сессии может означать атаку. Для обеспечения такого поведения вы должны отслеживать все активные сессии пользователя.

Замечание: Доступ к истекшей сессии может также произойти из-за нестабильного сетевого доступа и/или конкурентного доступа к приложению/веб-сайту. Сервер может попытаться установить новый идентификатор сессии через cookie, но пакет "Set-Cookie" может не дойти до клиента из-за плохой связи. Одно соединение может вызвать пересоздание идентификатора посредством session_regenerate_id(), а другое, в то же время, может не получить нового идентификатора. Следовательно, вы должны запретить доступ к истекшим сессиям чуть-чуть позже. Т.е. управление сессиями на базе временных меток является обязательным.

Короче говоря, не уничтожайте данные сессии ни с помощью session_regenerate_id(), ни session_destroy(), а используйте механизм доступа к сессии на базе временных меток. Пусть session_gc() сам удаляет старые сессии из хранилища.

Сессии и блокировки

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

Однако блокировка может быть использована атакующим для организации DoS-атаки. Для уменьшения риска DoS с использованием блокировки сессий, минимизируйте их. Используйте блокировку "read only", когда сессию не нужно обновлять. Используйте опцию 'read_and_close' с session_start(). session_start(['read_and_close'=>1]); Закрывайте сессию с помощью session_commit() сразу, как только вы закончили обновлять $_SESSION.

Текущий механизм управления сессиями не следит за изменениями $_SESSION, пока сессия неактивна. Это ваша зона ответственности, следить за тем, чтобы подобного не случалось.

Активные сессии

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

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

Самый простой способ - использовать в качестве префикса идентификатора сессии идентификатор пользователя и хранить всю требуемую информацию в $_SESSION. Многие базы данных умеют достаточно быстро выбирать строки по префиксу. Вы можете использовать session_regenerate_id() и session_create_id() для этого.

Внимание

Никогда в качестве префикса не используйте конфиденциальные данные. Если идентификатор пользователя конфиденциален, рассмотрите возможность использовать функцию hash_hmac().

Внимание

Для подобной настройки требуется разрешение session.use_strict_mode. Убедитесь, что эта опция разрешена, иначе база данных активных сессий может быть скомпрометирована.

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

Сессии и автоматический вход

Разработчики НЕ ДОЛЖНЫ использовать долгоживущие сессии для реализации автоматического входа в систему, потому что это резко повышает вероятность кражи сессии. Автоматический вход в систему должен реализовываться разработчиком самостоятельно.

Устанавливайте безопасные хешированные одноразовые ключи в качестве ключей автологина с помощью setcookie(). Используйте безопасное хэширование, посильнее чем SHA-2, например SHA-256 или выше со случайными данными из random_bytes() или /dev/urandom.

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

Ключ автологина - это очень долгоживущий ключ авторизации. Его надо защищать по максимуму. Используйте path/httponly/secure cookie для его защиты. Никогда не передавайте ключ автологина, кроме случаев, когда это необходимо.

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

CSRF (Межсайтовая подделка запроса)

Сессии и авторизация не защищают от атак типа CSRF. Разработчики должны самостоятельно реализовывать защиту.

output_add_rewrite_var() может быть использована для защиты от CSRF. Читайте документация для подробностей.

Замечание: До PHP 7.1.0 использовался один и тот же буфер вывода и INI-настройки для "trans sid". Так что использование output_add_rewrite_var() с PHP более ранних версий не рекомендуется.

Многие фреймворки поддерживают защиту от CSRF. Обратитесь к документации своего фреймворка для более подробной информации.

add a note add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top