Наш канал в telegram

Что такое сессии и как с ними работать в php

Что такое сессии и зачем они нужны

Протокол http, используемый для загрузки содержимого web-страниц, работает следующим образом: клиент присылает серверу http-запрос, сервер его обрабатывает и отправляет клиенту сформированный ответ. Всё, на этом общение закончено. Если нужны новые данные — клиент посылает новый запрос, а сервер присылает новый ответ и так далее.

При этом существует ряд задач, когда серверу для формирования ответа нужно понимать, присылал ли этот клиент какие-то http-запросы ранее и что происходило в процессе их обработки. Самый очевидный пример — авторизация. Скажем, клиент в очередном http-запросе запрашивает какую-то страницу личного кабинета. Что делать серверу? Если клиент авторизован (ранее присылал правильные данные для авторизации), то нужно ему эту страницу показать, а если не авторизован — то не нужно. А как узнать, авторизован пользователь или не авторизован? Значит нужно где-то хранить результаты выполнения предыдущих http-запросов.

Как раз для таких случаев и придумали сессии, — специальный механизм, позволяющий серверу идентифицировать присылающих http-запросы клиентов, а также запоминать для этих клиентов различные данные.

Что такое сессии в php

Применительно к php можно сказать, что сессии — это механизм, позволяющий php-скрипту, обрабатывающему очередной http-запрос, сделать следующие вещи:

  1. понять, присылал ли этот же клиент http-запросы ранее
  2. восстановить сохранённые ранее для этого клиента данные
  3. сохранить данные для использования при обработке последующих http-запросов от этого клиента

Как работают сессии

Работают сессии следующим образом: сервер запоминает данные сессии (какие-то переменные) в специальном хранилище, которое подписывает сгенерированным уникальным идентификатором. Этот же идентификатор отправляется клиенту. При следующем http-запросе клиент присылает серверу полученный идентификатор, по которому сервер понимает от какого клиента пришёл запрос и с каким хранилищем ему работать (откуда восстанавливать данные и куда их сохранять).

Как работают сессии в php

Для работы с сессиями в php существуют специальные встроенные функции. Этих функций довольно много, основные я опишу ниже, а остальные можете посмотреть в документации, например вот здесь. Функции для работы с сессиями имеют большое количество всяких настроек, которые можно настраивать через конфиг php-сервера (файл php.ini), либо задавать в качестве опций непосредственно при вызове соответствующих функций. Многие настройки можно посмотреть через php_info().

По-умолчанию в качестве хранилища данных сессий php использует обычные файлы во временной папке. За это отвечает опция session.save_handler, которая по умолчанию установлена в значение files. При желании можно назначить свои собственные обработчики на связанные с сессиями события (старт, идентификация, сохранение данных и так далее) и хранить данные сессии как угодно и где угодно. Путь ко временной папке можно узнать при помощи функции sys_get_temp_dir().

Скрипт php может передать клиенту идентификатор созданного хранилища с помощью установки cookie или дописывая его к расположенным на загруженной странице адресам в качестве параметра GET-запроса. Выбор способа определяется настройками session.use_cookies, session_use_only_cookies и session_use_trans_sid. По умолчанию php настроен на использование только cookies (первые две настройки установлены в единицу, а последняя — в ноль). Используемое имя сессионной куки по-умолчанию — PHPSESSID.

Клиент может передать php-скрипту на сервере полученный ранее идентификатор любым способом (через cookie в заголовке запроса или указывая в качестве параметра GET-запроса) независимо от настроек. В случае с редиректом через header(‘Location: url’) куки не работают и идентификатор сессии должен быть передан на сервер в качестве параметра GET-запроса.

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

Время жизни сессии определяется параметром session.gc_maxlifetime. По прошествии этого времени файлы данных сессии будут рассматриваться как мусор и могут быть удалены. Кроме того, можно задать время жизни отправляемой клиенту cookie (для этого используется параметр session.cookie_lifetime). По-умолчанию для session.cookie_lifetime установлено значение 0, которое означает, что кука будет актуальна до закрытия браузера (просроченные куки называются «протухшими»).

Основные функции для работы с сессиями в php

Далее давайте рассмотрим основные функции для работы с сессиями из php скриптов. Лучше всего установить какой-нибудь WAMP/WNMP комплект и попробовать проделать своими руками всё, о чём ниже пойдёт речь. Я обычно использую OpenServer + Firefox (в этом браузере удобнее всего вызывать отладчик — одной кнопкой F12).

Первая функция, которая нам понадобится, — это функция session_start(). Эта функция отвечает за то, чтобы по полученным в запросе данным определить, есть ли для отправившего запрос клиента активная сессия, и далее либо восстановить сохранённые данные сессии в суперглобальный массив $_SESSION (если сессия существует), либо стартовать новую сессию (если сессии для данного клиента нет). Очень удобно — просто вызываем в скрипте эту функцию и она сама разбарается, что дальше делать, — запускать новую сессию или восстанавливать старую.

Функция session_start() имеет довольно много опций, которые позволяют настраивать различные параметры сессии (имя, время жизни, безопасность и так далее). Подробности можно прочитать в документации. Если никакие опции не указывать, то будут использоваться настройки по-умолчанию, — из конфига php. Главное правило при использовании функции session_start() состоит в том, что она должна вызываться до какого-либо вывода (то есть до начала формирования тела ответа).

Вторая нужная функция — ini_set(). С помощью этой функции можно менять настройки php по-умолчанию. При этом значения в файле php.ini не изменяются, новые настройки действуют только для текущего скрипта и только до окончания его обработки.

Ещё одна нужная функция — session_name(). С помощью этой функции можно получить имя текущей сессии (если вызвать функцию без параметров) или изменить имя сессии (если указать в качестве параметра новое имя).

Следующая интересная функция называется session_unset(). Эта функция очищает все переменные массива $_SESSION. При этом сама сессия остаётся активной и данные из хранилища не стираются (они будут стёрты только когда завершится выполнение скрипта и php перепишет туда новое содержимое массива $_SESSION). При этом вы можете продолжить записывать в хранилище какие-то новые данные и они будут сохраняться.

Последняя функция, о которой я упомяну, — это функция session_destroy(). Она, в отличии от session_unset, как раз уничтожит все данные в хранилище, при этом не трогая текущее состояние массива $_SESSION. После вызова этой функции никакие новые данные сохранить в сессии уже не получится.

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

Небольшая практика

Ну и, наконец, давайте проверим как это всё работает. Создайте в блокноте (кодировка utf-8 без BOM) и разместите в корневой папке своего локального сервера файл index.php следующего содержания:

Текст под спойлером

	session_start();

[свернуть]

Теперь запускаем локальный web-сервер, открываем браузер и переходим по адресу http://my_local_site/index.php.

В итоге должна загрузиться пустая страница (конечно, мы же ничего не выводили), но нас тело ответа и не интересует, нас интересуют заголовки. Чтобы их увидеть — вызываем отладчик (кнопка F12) и переходим на вкладку «Сеть». Далее, заходим в настройки браузера на вкладку «Приватность», жмём «Удалить отдельные куки» и удаляем куки для нашего локального сайта. После этого нужно вернуться к открытой пустой страничке и обновить её.

В результате в окне отладки должен появиться наш запрос. Заголовки можно увидеть кликнув в отладчике по соответствующему запросу, а также щёлкнув по кнопке «Показать детали запроса» в правом верхнем углу отладчика.

Заголовки запроса и ответа при первом входе на страницу

заголовки запроса и ответа при первом входе на страницу

[свернуть]

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

Заголовки запроса и ответа при последующих входах

заголовки запроса и ответа при последующих входах

[свернуть]

Теперь давайте попробуем сохранить в сессии какую-нибудь переменную. Напишем следующий код:

Текст под спойлером

session_start();	// запускает новую сессию или восстанавливает старую
if (!isset($_SESSION['x'])) {	// если переменной x не существует (новая сессия)
	$_SESSION['x'] = 1;	// создаём переменную x и сохраняем в данных сессии
}
else{	// если мы оказались здесь, значит это старая сессия
	$_SESSION['x'] += 1;	// увеличиваем значение сессионной переменной
}
echo $_SESSION['x'];

[свернуть]

Теперь каждый раз обновляя страницу index.php мы должны увидеть как наш счётчик постоянно увеличивается.

Измените скрипт следующим образом:

Текст под спойлером

//ini_set('session.use_cookies',0);
//ini_set('session.use_only_cookies',0);
//ini_set('session.use_trans_sid',1);
session_start();	// запускает новую сессию или восстанавливает старую
if (!isset($_SESSION['x'])) {	// если переменной x не существует (новая сессия)
	$_SESSION['x'] = 1;	// создаём переменную x и сохраняем в данных сессии
}
else{	// если мы оказались здесь, значит это старая сессия
	$_SESSION['x'] += 1;	// увеличиваем значение сессионной переменной
}
echo $_SESSION['x'];
echo '<br><a href="">Ссылка</a>';

[свернуть]

Проведите следующие эксперименты:

  • Выключите в вашем браузере cookie, запустите скрипт и пощёлкайте по ссылке, наблюдая в отладчике кто кому какие заголовки отправляет.
  • С выключенными в браузере куками, раскомментируйте вторую и третью строку скрипта, после чего снова запустите скрипт и пощёлкайте по ссылке, одноременно наблюдая что происходит с заголовками и адресной строкой.
  • Обратно включите куки в браузере и закомментируйте вторую и третью строки в скрипте, но раскомментируйте первую. Снова запустите скрипт и пощёлкайте по ссылке.

Можно придумать ещё много разных экспериментов с разными функциями, дальше — сами.

Пара слов про авторизацию

Ну и напоследок вернёмся туда, откуда начали, — к авторизации. Просто накидаю несколько советов:

  • Не нужно стартовать сессии всем подряд, лучше запускать их только для зарегистрированных пользователей. Например, первый раз мы запускаем сессию при отправке авотризационных данных (если они окажутся верными), а далее выполняем функцию session_start() только в том случае, если имя сессии присутствует в заголовках или параметрах запроса.
  • Не нужно хранить в сессиях логины, пароли, хэши паролей и прочую конфиденциальную информацию. Лучше хранить просто переменную, показывающую авторизован юзер или нет (скажем, какой-нибудь его идентификатор).
  • Для обеспечения безопасности лучше оставить настройки передачи идентификатора сессии от сервера к клиенту по-умолчанию, то есть только через куки.
  • Для безопасности же нужно устанавливать для кук флаг session.cookie_httponly, который разрешает доступ к куке только через http-протокол (кука будет недоступна из javascript).
  • Нужно обязательно установить для кук флаг session.cookie_secure, который разрешает передачу кук только через https-соединение.
  • Настройте параметр session.cookie_samesite запретив или ограничив передачу кук в кроссдоменных запросах.
  • Используйте различные дополнительные проверки, чтобы удостовериться что куки не ворованные и не поддельные, типа проверок User-Agent, IP и так далее.

Добавить комментарий