Где хранятся сессии php. PHP сессии под скальпелем
В Интернете можно найти тысячи туториалов о том, что такое сессии, для чего они нужны и как с ними работать. Но, к сожалению, прочитав их, всё равно остаётся множество вопросов. На мой взгляд, самый простой способ разобраться во всём — это посмотреть, как работают сессии изнутри. Т.е. изучить логи обмена браузера и веб-сервера, а также посмотреть, какие данные сохраняются на стороне клиента и на стороне сервера.
После этого многие моменты становятся куда более понятными, а сам механизм работы — более прозрачным.
Работу сессий будем изучать на следующем стандартном скрипте:
Он работает следующим образом:
Блок 1. Функция session_start() создаёт новую сессию или загружает старую, используя уникальный идентификатор сессии PHPSESSID.
Блок 2. Если удалось восстановить сессию, то значение $_SESSION["views"] увеличивается на единицу. Если нет — инициализируется единицей.
По идее, если в браузере включена поддержка кук, механизм должен работать и при каждом обновлении страницы значение счётчика будет увеличиваться на единицу.
Первая загрузка скрипта
Заголовки запроса
GET / HTTP/1.1 Host: firingrange.local User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:6.0.2) Gecko/20100101 Firefox/6.0.2 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Cache-Control: max-age=0Заголовки ответа
HTTP/1.1 200 OK Date: Thu, 29 Sep 2011 20:36:15 GMT Server: Apache/2.2.13 (Win32) PHP/5.2.10 X-Powered-By: PHP/5.2.10 Set-Cookie: PHPSESSID=k33en6ccgcia7125mitj5te4u6; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Content-Length: 58 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/htmlКомментарий
В исходном запросе браузер не идентифицировал себя никаким образом, поэтому механизм сессий PHP сгенерировал новый уникальный идентификатор сессии и скомандовал браузеру создать куку, в которой будет храниться этот самый идентификатор.
Сторона сервера
В результате работы скрипта на стороне сервера создаётся файл sess_k33en6ccgcia7125mitj5te4u6 следующего содержания:
Сторона клиента
На стороне клиента создаётся кука PHPSESSID, в которой хранится значение уникального идентификатора сессии.
Примечание. При настройках PHP по умолчанию, время жизни куки PHPSESSID — до закрытия браузера. Т.е. как только браузер будет закрыт, кука будет удалена, а соответственно будет потеряна сессия. Время жизни куки PHPSESSID можно менять, варьируя значение session.cookie_lifetime.
Результат работы скрипта
Вторая загрузка скрипта
Заголовки запроса
GET / HTTP/1.1 Host: firingrange.local User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:6.0.2) Gecko/20100101 Firefox/6.0.2 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Cookie: PHPSESSID=k33en6ccgcia7125mitj5te4u6 Cache-Control: max-age=0Заголовки ответа
HTTP/1.1 200 OK Date: Thu, 29 Sep 2011 20:49:41 GMT Server: Apache/2.2.13 (Win32) PHP/5.2.10 X-Powered-By: PHP/5.2.10 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Content-Length: 58 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/htmlКомментарий
Браузер отправляет веб-серверу куку PHPSESSID, используя которую PHP инициализирует массив $_SESSION значениями из файла sess_k33en6ccgcia7125mitj5te4u6. Соответственно, в блоке 2 отрабатывает ветка IF (прямая).
Сторона сервера
В результате работы скрипта содержимое файла sess_k33en6ccgcia7125mitj5te4u6 меняется:
Сторона клиента
На стороне клиента ничего не меняется.
Результат работы скрипта
Что дальше?
Последующие загрузки страницы до закрытия браузера будут работать по аналогии с тем, как работала вторая загрузка скрипта.
Т.к. время жизни куки было ограничено текущей сессией браузера, то после его закрытия уникальный идентификатор сессии будет утерян и при перезапуске процесс пойдёт по новой.
Тем не менее можно вернуться к сохранённой сессии, если явно указать PHPSESSID в качестве параметра скрипта:
Возвращение к сессии довольно условное, т.к. в результате работы скрипта в данном случае кука не создаётся. Заголовки ответа сервера:
HTTP/1.1 200 OK Date: Thu, 29 Sep 2011 21:01:52 GMT Server: Apache/2.2.13 (Win32) PHP/5.2.10 X-Powered-By: PHP/5.2.10 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Content-Length: 58 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html
Т.е. для поддержания работы именно с этой сессией ко всем ссылкам придётся приписывать?PHPSESSID=k33en6ccgcia7125mitj5te4u6.
Примечание. Можно указать PHP, чтобы уникальный идентификатор сессии передавался только через куку. Для этого нужно установить session.use_only_cookies в значение 1. В этом случае трюк, продемонстрированный выше, не пройдёт.
Если куки в браузере отключены, то можно передавать идентификатор сессии через параметры, как мы делали выше. Причём в PHP есть механизм, который будет сам дописывать нужный параметр в ссылки и добавлять скрытые поля в формы. Принцип работы точно такой же, как и с куками, поэтому не будем разбирать этот случай отдельно.
Небольшой вопросник (FAQ)
Где физически хранятся данные сессий?
Данные сессий хранятся на сервере. По умолчанию они записываются в файлы, но можно задать свой собственный механизм хранения данных сессий (например с использованием базы данных). Если хотите подробностей, смотрите функцию session_set_save_handler.
Кто генерирует уникальный идентификатор сессии?
Уникальный идентификатор сессии (PHPSESSID) генерирует сервер.
Можно ли написать собственный механизм сессий?
Да, это вполне возможно. Как видите, PHP не использует ничего сверхъестественного — идентификатор сохраняется между запросами с помощью кук, данные сессий хранятся в файлах на сервере.
Например, собственный механизм работы с сессиями есть в популярном фреймворке CodeIgniter .
Насколько безопасен механизм сессий?
Сессия идентифицируется только с помощью уникального идентификатора сессии, поэтому в общем случае злоумышленнику достаточно украсть его, чтобы запутать сервер. Возьмём тестовый скрипт, который мы использовали выше. Если обращение к нему будет с другого IP (по отношению к создавшему сессию), но PHPSESSID будет передаваться тот же самый, то сессия будет успешно восстановлена и счётчик будет увеличиваться с предыдущего сохранённого значения.
Обеспечивать дополнительную защиту придётся вам самим. Например:
- Можно сохранять в данных сессии IP и User-Agent клиента (будет храниться на стороне сервера), а затем при каждом обращении проверять, что актуальные значения совпадают с сохранёнными. В данном случае приходится искать компромисс между безопасностью и удобством работы пользователя.
К примеру, если у пользователя динамический IP и вы используете сессии для поддержания авторизации, но при этом проверяете совпадение IP, то при каждой смене адреса пользователю придётся заново вводить логин и пароль.
Точно также строка User-Agent может меняться при обновлении версии браузера или при установке некоторых плагинов.
- Одним из рекомендуемых механизмов защиты сессий является повторная генерация идентификатора при каждом обращении к скрипту (см. функцию session_regenerate_id). Посмотреть скрипт и алгоритм работы в разрезе можно ниже.
Примечание. Если верить обсуждению на официальном сайте , то при повторной генерации идентификатора могут возникнуть проблемы с параллельным доступом к данным.
Работа сессий с повторной генерацией идентификатора в разрезе
Скрипт
//
блок
1
session_start();
if
(isset
($_SESSION[
"initiated"
]))
session_regenerate_id();
else
$_SESSION[
"initiated"
] =
true
;
//
блок
2
if
(isset
($_SESSION[
"views"
]))
$_SESSION[
"views"
]++
;
else
$_SESSION[
"views"
] = 1;
//
блок
3
echo
"
<
body>
Количество
просмотров
: ".
$_SESSION[
"views"]."