PHP: Хранение сессий в защищённых куках
На некоторой стадии развития веб-проекта возникает одна из следующих ситуаций:
- backend перестаёт помещаться на одном сервере и требуется хранилище сессий, общее для всех backend-серверов
- по различным причинам перестаёт устраивать скорость работы встроенных файловых сессий
Однако, есть альтернатива этому подходу. Возможно безопасно и надёжно хранить данные сессии в браузерной куке у самого пользователя, если заверить данные сессии криптографической подписью. Если вдобавок к этому данные ещё и зашифровать, то тогда содержимое сессии не будет доступно пользователю. Главное достоинство этого способа хранения в том, что он не требует централизованной базы данных для сессий со всеми вытекающими из этого плюсами в виде надёжности, скорости и масштабирования.
Описание механизма
Эта идея не нова и реализована во множестве фрэймворков и библиотек для различных языков программирования. Вот пара примеров:
- Python/Django
- Ruby/Ruby on Rails
Большинство имеющихся реализаций работают следующим образом: записывают в какую-то куку строку, содержащую время истечения сессии, данные сессии и HMAC-подпись времени истечения и данных. При запросе клиента кука читается соответствующим обработчиком, затем проверяется подпись и сравнивается текущее время с временем истечения сессии. Если всё совпадает, обработчик возвращает данные сессии в приложение.
Однако, шифрование куки в распространённых реализациях этого механизма отсутствует.
Сравнение с классическим подходом
В итоге, хранение сессий в куках имеет следующие достоинства:
- Возрастает производительность веб-приложения, так как небольшая криптографическая операция дешевле сеанса сетевого обмена или доступа к диску для извлечения данных сессии.
- Возрастает надёжность веб-приложения, так как оно не зависит от внешнего KV-хранилища. Даже если хранилище сессий обеспечено средствами отказоустойчивости, это не наделяет его абсолютной стабильностью: переключение требует времени, а часть проблем (такие как ухудшение сетевой связности между регионами) и вовсе неискоренимы. Зачастую же сессии и вовсе хранятся на единственном сервере, являющимся единой точкой отказа всего веб-приложения.
- Экономия ресурсов. Не нужно больше хранить сессии, а значит от этого выиграют и владельцы маленьких сайтов, у которых сократится дисковая активность, и освободят несколько серверов владельцы крупных веб-проектов.
- Возрастает объём данных, передаваемый клиентом
- Имеется ограничение на размер данных в сессии, связанное с ограничениями на размер кук. Обычно это чуть меньше 4 КБ кодированных данных.
- Клиент может откатить состояние сессии на любое выданное и подписанное ранее значение, криптоподпись которого ещё действительна в текущий момент времени.
Реализации для PHP
Когда я попытался отыскать что-то похожее для PHP, я с удивлением обнаружил, что не существует ни одной библиотеки, которая дотягивает до минимума требований:
- Безопасность: отсутствие ошибок при использовании криптографии
- Актуальная кодовая база: поддержка современных версий PHP, отсутствие deprecated-расширений в зависимостях (таких как mcrypt)
- Наличие тестов: сессии — это один из фундаментальных механизмов, и в основе реального приложения нельзя использовать незрелый код
- Возможность шифрования: открытое хранилище сессии на клиенте, читаемое клиентом, не всем подходит.
- Максимально компактное представление данных — ради минимизации оверхеда и запаса ёмкости сессии
- Встраиваемость через SessionHandlerInterface
- Потенциально уязвима к атакам по времени из-за прямого сравнения хэша с образцом
- Нет шифрования
- Нет тестов
- Неэкономная упаковка куки
- Время истечения куки не хранится со значением и не охвачено подписью. Это значит. что клиент, единожды получив данные в сессии, может воспроизводить их бесконечно.
- Мелкие баги: read() после write() в рамках одного выполнения скрипта показывает не то, что записано и пр.
Спойлер
Почему важна проверка подписи и шифрования вместо подписи недостаточно? Во-первых, есть заметная вероятность, что кука с мусором расшифруется в какую-то сессию, особенно запись сессии короткая. Во-вторых, строка с сессией подвергается десериализации, а на вход десериализатора нельзя подавать строки из недоверенных источников.
После всех поисков я решил реализовать такую библиотеку самостоятельно.
Собственная реализация
- Обязательное шифрование. Алгоритм и режим — любой симметричный шифр на выбор, доступный в OpenSSL. По умолчанию: AES-256-CTR.
- HMAC-подпись куки любым хэш-алгоритмом на выбор из ассортимента криптографического расширения Hash. Он же используется для генерации производных ключей шифрования. По умолчанию: SHA-256.
- Реализованы контрмеры против атак по времени
- Помимо основного набора данных и времени истечения, подписью охвачен и ID сессии, что оставляет простор для связывания данных сессии с внешними данными.
- Реализация представлена в виде класса, совместимого с SessionHandlerInterface, а значит её можно прозрачно использовать практически с любыми PHP-приложениями.
- Минимальный оверхед хранения, привносимый шифрованием и подписью.
Примеры использования
Небольшой скрипт, иллюстрирующий работу с этим хэндлером:
$value) $_SESSION[$key] = $value; echo "Updated session:"; > else echo "Current session data:\n"; var_dump($_SESSION);
Пронаблюдать его работу, задавая разные значения сессии в строке запроса, можно по адресу: https://vm-0.com/sess.php.
Пример интеграции в Symfony:
framework: session: handler_id: session.handler.cookie services: session.handler.cookie: class: VladislavYarmak\StoragelessSession\CryptoCookieSessionHandler public: true arguments: ['reallylongsecretplease']
В качестве реального демо я подключил этот хэндлер сессий к первому пришедшему на ум веб-приложению, которое использует сессии. Им оказалось DokuWiki: wiki.vm-0.com. На сайте работает регистрация и логин, а работу сессий можно наблюдать в куках.
Благодарю за внимание и надеюсь, что эта статья поможет развитию ваших проектов.
Сессии в PHP
Сессия — это механизм для сохранения информации на разных веб-страницах для идентификации пользователей при навигации по сайту или приложению. В отличие от файлов cookie, информация не сохраняется на компьютере пользователя.
Что такое сессия в PHP?
Известно, что веб-сервер не поддерживает постоянного соединения с посетителем, и каждый запрос обрабатывается, как новый, без связи с предыдущими. А это означает, что сервер не может запоминать конкретного посетителя между несколькими запросами, т.е. при доступе к веб-странице сервер отвечает за предоставление содержимого только конкретной запрашиваемой страницы. Часто возникает необходимость отображать информацию определенного пользователя на всех страницах и, при этом, вам нужно аутентифицировать пользователя в каждом запросе. Представьте, что вам нужно было бы вводить логин и пароль пользователя на каждой странице, где была представлена ваша информация о профиле. Это было бы вообще не практично, и именно в таких случаях необходимы сессии.
Переменные сессии решают эту проблему, сохраняя информацию о пользователе, которая будет использоваться на нескольких веб-страницах (например, логин посетителя, любимая музыка и др.). По умолчанию переменные сессии действуют до тех пор, пока пользователь не закроет браузер.
Одним из недостатков файлов cookie является то, что они хранятся на компьютере пользователя. Это дает пользователю возможность получать доступ, просматривать и изменять этот файл cookie, что может привести к сбою приложения. Сессии PHP, наоборот, хранят в системе пользователя только идентификационный файл cookie, который используется для ссылки на файл сессии на сервере. Таким образом, пользователь не имеет доступа к содержимому файла сессии, тем самым обеспечивая безопасную альтернативу файлам cookie. Сессии PHP также работают, когда пользователь отключает поддержку файлов cookie браузером.
Сеанс создает файл во временном каталоге на сервере, где хранятся зарегистрированные переменные сеанса и их значения. Эти данные будут доступны для всех страниц сайта во время этого посещения.
Сессия создает файл во временном каталоге на сервере, где хранятся зарегистрированные переменные сессии и их значения. Эти данные будут доступны для всех страниц сайта во время этого посещения пользователем.
Расположение временного файла определяется настройкой в файле php.ini в директиве session.save_path .
Пример использования в файле php.ini:
session.save_path = «/var/www/my_site/data/tmp»
Изменить директорию для хранения файлов сессий можно добавив в файл .htaccess:
php_value session.save_path «/var/www/my_site/data/tmp»
Перед использованием любой переменной сеанса убедитесь, что вы установили этот путь.
Когда сессия стартует, происходит следующее:
- Сначала PHP создает уникальный идентификатор для этой конкретной сессии, который представляет собой случайную строку из 32 шестнадцатеричных чисел, например 5c9foj24c3jj973hjkop2fc937e3463.
- Файл cookie под названием PHPSESSID автоматически отправляется на компьютер пользователя для хранения уникальной строки идентификации сессии.
- Файл автоматически создается на сервере в назначенном временном каталоге и имеет имя уникального идентификатора с префиксом sess_, т.е. sess_5c9foj24c3jj973hjkop2fc937e3463.
Когда сценарию PHP нужно получить значение из переменной сессии, PHP автоматически получает строку уникального идентификатора сессии из файла cookie PHPSESSID , а затем ищет в своем временном каталоге файл c этим именем и проверка может быть выполнена путем сравнения обоих значений.
С помощью специальных функций мы можем получить данный идентификатор:
echo session_id(); // идентификатор сессии
echo session_name(); // имя — PHPSESSID
То же значение мы могли бы получить, обратившись к cookie напрямую:
echo $_COOKIE[‘PHPSESSID’];
Сессия заканчивается, когда пользователь закрывает браузер или покидает сайт, сервер завершает сеанс через заранее определенный период времени, обычно продолжительностью 30 минут.
Запуск сессии PHP
Сессию PHP легко запустить, вызвав функцию session_start() . Эта функция сначала проверяет, запущена ли уже сессия, и если она не запущен, запускает её.
Переменные сессии хранятся в суперглобальном массиве $_SESSION[] . Доступ к этим переменным можно получить в течение всего сеанса.
В следующем примере запускается сессия, а затем регистрируется переменная с именем counter , которая увеличивается каждый раз при посещении страницы во время сессии.
Чтобы проверить, установлена ли уже переменная сессии или нет мы будем использовать функцию isset() .
Поместите этот код в файл test.php и загрузите его несколько раз, чтобы увидеть результат:
Пример
else < $_SESSION['counter'] = 1; >$msg = "Вы посетили эту страницу ". $_SESSION['counter']. " раз"; $msg .= " в этой сессии."; ?> Настройка сессии PHP
Примечание: Функция session_start() должна быть объявлена в самом начале вашего документа — ПЕРЕД всеми html-тегами.
Доступ к данным сессии PHP
Переменные сессии не передаются индивидуально на каждую новую страницу, вместо этого они извлекаются из сессии, которую мы открываем в начале каждой страницы функцией session_start() .
Также обратите внимание, что все значения переменных сессии хранятся в суперглобальной переменной $_SESSION .
Создадим еще одну страницу под названием test2.php . С этой страницы мы получим доступ к информации о сессии, которую мы установили на первой странице test.php :
Пример
Ещё один способ показать все значения переменных сессии для пользовательского сеанса — запустить следующий код:
Пример
Как изменить переменную сессии?
Чтобы изменить переменную сессии, достаточно просто её перезаписать. Поменяем значение переменной counter из предыдущих примеров с числа на строку:
Пример
Уничтожение сессии
Чтобы удалить все глобальные переменные сессии и уничтожить сессию, используйте функции session_unset() и session_destroy() :
Пример
Автоматическое включение сессии
Вам не обязательно вызывать функцию start_session() для запуска сессии, когда пользователь посещает ваш веб-сайт, вместо этого вы можете в файле php.ini установить для переменной session.auto_start значение 1 .
Сессии без куки
Рассмотрим еще один метод отправки идентификатора сеанса в браузер пользователя, когда он не разрешает хранить файлы cookie на своем компьютере.
В качестве альтернативы вы можете использовать константу SID , которая устанавливается при запуске сессии. Если клиентское ПО не хранит подходящую сессионную cookie, SID имеет вид session_name=session_id . В противном случае содержит пустую строку. Таким образом, вы можете в любом случае внедрять его в URL.
В следующем примере показано, как зарегистрировать переменную и правильно установить ссылку на другую страницу с помощью SID:
Пример
else < $_SESSION['counter'] = 1; >$msg = "Вы посетили эту страницу ". $_SESSION['counter']; $msg .= " раз."; echo ( $msg ); ?> Чтобы продолжить, нажмите следующую ссылку
">
Функция htmlspecialchars() может использоваться для вывода SID с целью предотвращения XSS-атак.
Сессии. Подробное описание работы и объяснение механизма.
Введение
Сессии — это на самом деле очень просто.
Надо только понимать, для чего они нужны и как устроены.
Ответим сначала на первый вопрос.
Как показано в соответствующем разделе этого FAQ, веб-сервер не поддерживает постоянного соединения с клиентом, и каждый запрос обрабатывается, как новый, безо всякой связи с предыдущими.
То есть, нельзя ни отследить запросы от одного и того же посетителя, ни сохранить для него переменные между просмотрами отдельных страниц. Вот для решения этих двух задач и были изобретены сессии.
Собственно, сессии, если в двух словах — это механизм, позволяющий однозначно идентифицировать браузер и создающий для этого браузера файл на сервере, в котором хранятся переменные сеанса.
Подробно расписывать нужду в таком механизме я не буду. Это такие хрестоматийнык случаи, как корзина покупок в е-магазине, авторизация, а так же, и не совсем тривиальные проблемы, такие, например, как защита интерактивных частей сайта от спама.
В принципе, довольно несложно сделать собственный аналог сессий, не такой функциональный, как встроенный в PHP, но похожий по сути. На куках и базе данных.
При запросе скрипта смотрим, пришла ли кука с определенным именем. Если куки нет, то ставим ее и записываем в базу новую строку с данными пользователя. Если кука есть, то читаем из базы данные. Еще одним запросом удаляем из базы старые записи и вот у нас готов механизм сессий. Совсем несложно. Но есть некоторые нюансы, которые делают предпочтительным использование именно встроенного механизма сессий.
Как устроены, и как работают сессии?
Для начала надо как-то идентифицировать браузер. Для этого надо выдать ему уникальный идентификатор и попросить передавать его с каждым запросом. Стыдно признаться, но когда я впервые узнал о сессиях, я думал, что это какой-то особый механизм, некий новый способ общения браузера с сервером — «сессии». Что идентификатор сессии передается каким-то особым образом. Разочарование было жестоким.
Сессии используют стандартные, хорошо известные способы передачи данных. Собственно, других-то просто и нет.
Идентификатор — это обычная переменная. По умолчанию ее имя — PHPSESSID.
Задача PHP отправить ее браузеру, чтобы тот вернул ее со следующим запросом. Из уже упоминавшегося раздела FAQ ясно, что переменную можно передать только двумя способами: в куках или POST/GET запросом.
PHP использует оба варианта.
За это отвечают две настройки в php.ini:
session.use_cookies — если равно 1, то PHP передает идентификатор в куках, если 0 — то нет.
session.use_trans_sid если равно 1, то PHP передает его, добавляя к URL и формам, если 0 — то нет.
Менять эти и другие параметры сессий можно так же, как и другие настройки PHP — в файле php.ini, а так же с помощью команды ini_set() или в файлах настройки веб-сервера
Если включена только первая, то при старте сессии (при каждом вызове session_start() ) клиенту устанавливается кука. Браузер исправно при каждом следующем запросе эту куку возвращает и PHP имеет идентификатор сессии. Проблемы начинаются, если браузер куки не возвращает. В этом случае, не получая куки с идентификатором, PHP будет все время стартовать новую сессию, и механизм работать не будет.
Теоретически, в наших с вами самодельных сессиях на куках и базе, можно самому, руками приписать ко всем ссылками передачу ид — и тогда наши собственные сессии будут работать независимо от кук. Но, согласитесь — приятнее, когда эту работу делает кто-то другой? 😉
По умолчанию в последних версиях PHP включены обе опции. Как PHP поступает в этом случае? Кука выставляется всегда. А ссылки автодополняются только если РНР не обнаружил куку с идентификатором сессии. Когда пользователь в првый раз за этот сеанс заходит на сайт, ему ставится кука, и дополняются ссылки. При следующем запросе, если куки поддерживаются, PHP видит куку и перестает дополнять ссылки. Если куки не работают, то PHP продолжает исправно добавлять ид к ссылкам, и сессия не теряется.
Пользователи, у которых работают куки, увидят длинную ссылку с ид только один раз.
Фух. С передачей идентификатора закончили.
Теперь осталось привязать к нему файл с данными на стороне сервера.
PHP это сделает за нас. Достаточно просто написать
session_start();
$_SESSION[‘test’]=’Hello world!’;
И PHP запишет в файл, связанный с этой сессией, переменную test.
Здесь очень важное замечание.
Массив $_SESSION — особенный.
В нем, собственно, и находятся переменные, которые мы ходим сделать доступными в различных скриптах.
Чтобы поместить переменную в сессию, достаточно присвоить ее элементу массива $_SESSION.
Чтобы получить ее значение — достаточно обратиться к тому же элементу. Пример будет чуть ниже.
Cборкой мусора — удалением устаревших файлов PHP тоже занимается сам. Как и кодированием данных и кучей всяких других нужных вещей. В результате этой заботы работа с сессиями оказывается очень простой.
Вот мы, собственно, и подошли к примеру работы сессий.
Пример очень маленький:
session_start();
if (!isset($_SESSION[‘counter’])) $_SESSION[‘counter’]=0;
echo «Вы обновили эту страницу «.$_SESSION[‘counter’]++.» раз. «;
echo «
обновить»;
?>
Мы проверяем, есть ли у нас в сессии переменная counter, если нет, то создаем ее со значением 0, а дальше выводим ее значение и увеличиваем на единицу. Увеличенное значение запишется в сессию, и при следующем вызове скрипта переменная будет иметь значение 1, и так далее.
Все очень просто.
Для того, чтобы иметь доступ к переменным сессии на любых страницах сайта, надо написать ТОЛЬКО ОДНУ(!) строчку в самом начале КАЖДОГО файла, в котором нам нужны сессии:
session_start();
И далее обращаться к элементам массива $_SESSION. Например, проверка авторизации будет выглядеть примерно так:
session_start();
if ($_SESSION[‘authorized’]<>1) header(«Location: /auth.php»);
exit;
>
Удаление переменных из сессии.
Если у вас register_globals=off , то достаточно написать
unset($_SESSION[‘var’]);
Если же нет, то тогда рядом с ней надо написать
session_unregister(‘var’);
Область применения.
Очень важно понимать, для чего сессии стоит использовать, а для чего — нет.
Во-первых, помните, что сессии можно применять только тогда, когда они нужны самому пользователю, а не для того, чтобы чинить ему препятствия. Ведь он в любой момент может избавиться от идентификатора!
Скажем, при проверке на то, что заполняет форму человек, а не скрипт, пользователь сам заинтересован в том, чтобы сессия работала — иначе он не сможет отправить форму! А вот для ограничения количества запросов к скрипту сессия уже не годится — злонамеренный скрипт просто не будет возвращать идентификатор.
Во-вторых. Важно четко себе представлять тот факт, что сессия — это сеанс работы с сайтом, так как его понимает человек. Пришел, поработал, закрыл браузер — сессия завершилась. Как сеанс в кино. Хочешь посмотреть еще один – покупай новый билет. Стартуй новый сеанс. Этому есть и техническое объяснение. Гарантированно механизм сессий работает только именно до закрытия браузера. Ведь у клиента могут не работать куки, а в этом случае, естественно, все дополненные идентификатором ссылки пропадут с его закрытием.
Правда, сессия может пропасть и без закрытия браузера. В силу ограничений, рассмотренных в самом главном разделе этого FAQ, механизм сессий не может определить тот момент, когда пользователь закрыл браузер. Для этого используется таймаут – заранее определенное время, по истечении которого мы считаем, что пользователь ушел с сайта. По умолчанию этот параметр равен 24 минутам.
Если вы хотите сохранять пользовательскую информацию на более длительный срок, то используйте куки и, если надо — базу данных на сервере. В частности, именно так работают все популярные системы авторизации:
— по факту идентификации пользователя стартует сессия и признак авторизованности передается в ней.
— Если надо «запомнить» пользователя, то ему ставится кука, его идентифицирующая.
— При следующем заходе пользователя на сайт, для того, чтобы авторизоваться, он должен либо ввести пароль, либо система сама его опознает по поставленной ранее куке, и стартует сессию. Новую сессию, а не продолжая старую.
В-третьих, не стоит стартовать сессии без разбору, каждому входящему на сайт. Это создаст совершенно лишнюю нагрузку. Не используйте сессии по пустякам – к примеру, в счетчиках. То, что спайлог называет сессиями, считается, конечно же, на основе статистики заходов, а не с помощью механизма сессий, аналогичного пхп-шному.
К тому же, возьмем поисковик, который индексирует ваш сайт. Если поисковый робот не поддерживает куки, то пхп по умолчанию будет поставлять к ссылкам PHPSESSID, что — согласистесь — может не сильно понравится поисковику, который, по слухам, и так-то динамические ссылки не жалует, а тут вообще при каждом заходе — новый адрес!
Если сессии используются для ограничения доступа к закрытому разделу сайта, то все просто поисковик и не должен его индексировать.
Если же приходится показывать одну и ту же страницу как авторизованным, так и не авторизованным пользователям, то тут поможет такой трюк – стартовать сессию только тем, кто ввел пароль, или тем, у кого уже стартовала сессия.
Для этого в начало каждой страницы вместо просто session_start() пишем
if (isset($_REQUEST[session_name()])) session_start();
таким образом, Мы стартуем сессию только тем, кто прислал идентификатор.
Соответственно, надо еще в первый раз отправить его пользователю – в момент авторизации.
Если имя и проль верные – пишем session_start() !
Самыми распространенными ошибками, которые выдает РНР при попытке работать с сессиями, являются такие:
Две из них,
Warning: Cannot send session cookie — headers already sent
Warning: Cannot send session cache limiter — headers already sent
вызваны одной и той же причиной, решение описано в этом факе здесь
Третья,
Warning: open(/tmp\sess_SID, O_RDWR) failed: No such file or directory (2) in full_script_path on line number (ранее она выглядела, как Warning: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/tmp) ),
если перевести ее с английского, подробно объясняет проблему: недоступен указанный в php.ini путь к каталогу, в который пишутся файлы сессий. Эту ошибку исправить проще всего. Просто прописать каталог, который существует, и доступен на запись, например,
session.save_path = c:\windows\temp
И не забыть перезагрузить апач после этого.
Как выясняется, сообразительность людская не имеет пределов, и поэтому я вынужден пояснить:
сообщение о третьей ошибке (невозможно найти каталог) НЕИЗБЕЖНО приведет к появлению первых двух, поскольку сообщение об ошибке — это вывод в браузер и после него заголовками пользоваться нельзя. Поэтому не спешите искать преждевременный вывод, а сначала пропишите правильный путь!
Следующей по распространенности проблемой при работе с сессиями является тяжелое наследие register_globals. НЕ давайте переменным скрипта имена, совпадающие с индексами массива $_SESSION!
При register_globals=on значения будут перезаписывать друг друга, и вы запутаетесь.
А при register_globals=off появится другая ошибка: «Your script possibly relies on a session side-effect which existed until PHP 4.2.3.», в случае, если в скрипте есть переменная сессии не имеющая значения, и глобальная переменная с тем же именем. Чтобы от неё избавиться, надо всегда инициализировать переменные перед использованием (или хотя бы проверять на существование) и не давать глобальным переменным имена, совпадающие с индексами массива $_SESSION.
Если не работает, но и никаких сообщений не выводится, то добавьте в самое начало скрипта две строчки, отвечающие за вывод ВСЕХ ошибок на экран — вполне возможно, что ошибки есть, но вы их просто не видите.
ini_set(‘display_errors’,1);
error_reporting(E_ALL);
или смотрите ошибки в error_log. Вообще, тема отображения сообщений об ошибках выходит за рамки данной статьи, поэтому просто убедитесь хотя бы, что вы можете их видеть. Чуть продробнее о поиске ошибок можно прочитать в этом разделе.
Если этот пример не заработает, то проблема либо в банальных опечатках (половина «проблем» с сессиями происходит от неправильно написанного имени переменной), либо в слишком старой версии PHP: поддержка сессий появилась в версии 4.0, а массив $_SESSION — в 4.1 (До этого использовался $HTTP_SESSION_VARS ).
Если же заработает — то проблема в куках. Отслеживайте — что за куку ставит сервер браузеру, возвращает ли браузер ее. Искать очень полезно, просматривая просматривая обмен HTTP-заголовками между браузером и сервером.
Объяснение принципа работы кук выходит за рамки этого и так уж слишком большого текста, но хотя бы убедитесь, что сервер куку с идентификатором посылает, а браузер — возвращает. И при этом идентификаторы совпадают друг с другом =)
Установка куки должна выглядеть, как
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;
или как
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; path=/
(если вы запрашиваете скрипт не из корневого каталога)
Ответ сервера должен выглядеть, как
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6
либо
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b
если браузер возвращает другие куки, кроме идентификатора сессии.
Если браузер куки не возвращает — проверьте, работают ли куки вообще.
Убедитесь, что домен, к которому вы обращаетесь, имеет нормальное имя (в котором есть хотя бы одна точка и не содержится запрещенных символов, например подчеркивания) и почистите кэш браузера — это две основные причины, по которм куки могут не работать.
Если пример отсюда работает, а ваш собственный код — нет, то проблема, очевидно, не в сессиях, а в алгоритме. Ищите, где потеряли переменную, по шагам переносите пример отсюда, отлаживайте свой скрипт.
Еще одна проблема может возникнуть, если вы используете перенаправление через header или навигацию с помощью JavaScript.
Дело в том, что РНР автоматически дописывает идентификатор сессии только к ссылкам вида , но не делает этого для header-ов, яваскрипта, мета-тегов.
Поэтому надо добавлять идентификатор руками, например, так:
header(«Location: /script.php?».session_name().’=’.session_id());
Следует помнить, что пхп лочит файл сессии. То есть, если один ваш скрипт стартует сессию и долго выполняется, а другой пытается в это время стартовать её с тем же идентификатором, то он зависнет. Поэтому в долго выполняющихся скриптах следует стартовать сессию только тогда, когда она нужна, и тут же закрывать её, с помощью session_write_close()
Так же, весьма редкая, и совершенно непонятно, откуда появляющаяся, проблема бывает в том, что настройка session.save_handler имеет значение, отличное от files. Если это не так — исправляйте.
Безопасность
Безопасность сессий — тема обширная. Поэтому остановлюсь на нескольких основных моментах.
Самый хрестоматийный — не передавать идентификатор через адресную строку. Об этом написано даже в php.ini, но это ограничивает функциональность сессий. Если вы решите последовать этому совету, то кроме session.use_trans_sid = 0 не забудьте session.use_only_cookies = 1
Желательно привязывать сессию к IP адресу: таким образом, если идентификатор будет украден, то злодей все равно не сможет им воспользоваться в большинстве случаев.
Рекомендуется пользоваться директивой session.save_path, с помощью которой задать собственный каталог для сохранения файлов сессий. Это более безопасно, чем когда они хранятся в общем временном каталоге сервера по умолчанию.
Пример авторизации с помощью сессий
Проиллюстрируем все вышенаписанное небольшим примером:
создадим файл auth.php:
if (isset( $_POST [ ‘auth_name’ ]))
$sql = «SELECT * FROM users WHERE name=?s» ;
$row = $db -> getRow ( $sql , $_POST [ ‘auth_name’ ]);
if ( $row && password_verify ( $_POST [ ‘auth_pass’ ], $row [ ‘pass’ ])) $_SESSION [ ‘user_id’ ] = $row [ ‘id’ ];
>
header ( «Location: http://» . $_SERVER [ ‘HTTP_HOST’ ]. $_SERVER [ ‘REQUEST_URI’ ]);
exit;
>
if (isset( $_GET [ ‘action’ ]) AND $_GET [ ‘action’ ]== «logout» ) session_start ();
session_destroy ();
header ( «Location: http://» . $_SERVER [ ‘HTTP_HOST’ ]. «/» );
exit;
>
теперь достаточно написать во всех защищаемых скриптах строчку
require «auth.php»;
В данном примере предполагается, что сессия уже стартовала и соединение с БД создано, с использованием Класс для безопасной и удобной работы с MySQL. Также предполагается, что пароль хэширован с использованием рекомендованной функции password_hash .
Пример защищаемого файла:
Что такое сессии в PHP и для чего они нужны
Сессии — способ хранения данных пользователя на протяжении сеанса его работы с web-приложением. Их применяют для того, чтобы хранить временную информацию, которую нет нужды сохранять на постоянной основе. Такими данными могут быть, например, информация о том, залогинен ли пользователь в системе, если нужно, чтоб через некоторое время, а также после закрытия браузера он автоматически разлогинивался.
Работает это следующим образом: чтобы создать сессию, программист должен использовать функцию session_start. Кроме этого, сессия может быть создана автоматически при запуске скрипта (соответственно, при заходе пользователя на страницу). Для этого можно установить директиву PHP session.auto_start. Когда создается сессия, пользователю передаётся cookie — специальная запись, которая некоторое время хранится в его браузере, а потом удаляется. Для cookie можно задавать время, через которое он будет автоматически удален. В cookie записывается идентификатор сессии для данного пользователя. Сама же сессия хранится на сервере. Есть несколько способов ее хранения. Базовым способом является запись сессии в файл. Но FrameWorks зачастую позволяют использовать и другие — например, сохранять её в базе данных.
В сессию можно записывать различные данные. Для записи и чтения из сессии используют суперглобальный массив $_SESSION, либо дополнительные возможности фреймворков. Этот массив называется суперглобальным по той причине, что он имеет глобальную область видимости, то есть его можно использовать из любого места кода — хоть изнутри функции, хоть из метода класса. И всё это без дополнительных действий. Он является ассоциативным. Производить запись в сессию можно, соответственно, по ключу. Ключом может являться строка текста. Например, сделать это можно так:
Записав сюда что-либо, эти данные можно использовать не только на протяжении работы скрипта, как обычные переменные, но и при последующих его запусках. В том числе и при запусках других скриптов с того же сайта. Это позволяет удобно хранить значения переменных между запусками. Чтение значений из сессии происходит обратным образом:
Удалить данные из сессии можно с помощью PHP-команды unset:
По умолчанию сессии хранятся 24 минуты, если не закрывать браузер, а также удаляются при его закрытии. Но эти параметры можно изменить. Это время можно изменить через файл настроек PHP. Для этого там надо установить такие записи:
php_value session.gc_maxlifetime 172800
php_value session.cookie_lifetime 172800
172800 — время хранения сессии в секундах. В данном случае это 2 суток. Вместо этого числа можно вписать требуемое Вам значение. Эти же действия можно выполнить прямо из скрипта без изменения в файле настроек с помощью команды ini_set:
ini_set(‘session.gc_maxlifetime’, 172800);
ini_set(‘session.cookie_lifetime’, 172800);
Однако, стоит помнить, что механизм работы сессий предполагает, что они будут автоматически удалены после закрытия браузера.
