Сессия в PHP — это способ хранить данные пользователя на сервере между несколькими HTTP-запросами, привязывая их к конкретному браузеру через уникальный идентификатор. В статье разберем, как PHP создает и завершает сессию, чем она отличается от cookie и какие настройки нужны, чтобы сессию нельзя было перехватить или подделать.
Содержание
Cookie и сессия: в чем разница
Эти два понятия путают чаще всего, хотя они решают разные задачи.
Cookie — это пара ключ-значение, которую браузер хранит у себя и отправляет на сервер с каждым запросом к тому же домену. Сами данные лежат на стороне клиента.
Сессия — это данные, которые хранятся на сервере (по умолчанию — в файле). Браузеру передается только идентификатор сессии, а не сами данные.
Связывает их одна cookie с именем PHPSESSID (имя можно изменить директивой session.name). В ней лежит только идентификатор — ключ, по которому сервер находит нужный файл сессии. Если эту cookie удалить или подделать, PHP не найдет сохраненные данные и начнет новую сессию.
Как PHP создает и ведет сессию
При вызове session_start() происходит несколько шагов:
- PHP проверяет, пришла ли от клиента cookie
PHPSESSID. Если да — ищет файл сессии с таким идентификатором. - Если cookie нет (первый визит), PHP генерирует новый идентификатор — строку по умолчанию длиной 32 символа, случайную и криптографически стойкую (с PHP 7.1 — на основе
random_bytes, не предсказуемый счетчик). - Сервер отправляет браузеру cookie
PHPSESSIDс этим идентификатором. - На сервере создается файл
sess_<идентификатор>в каталоге, который задан параметромsession.save_path(php.ini). Префикс файла — ровноsess_, без дополнительных букв. - Данные, которые скрипт кладет в массив
$_SESSION, PHP сериализует и записывает в этот файл при завершении запроса.
На каждой следующей странице, где вызван session_start() с той же cookie, PHP читает файл и восстанавливает $_SESSION — так сервер «узнает» пользователя между запросами, хотя сам HTTP-протокол состояния не хранит.
Базовые операции: код с проговоренным результатом
Запуск сессии и запись значений:
<?php
session_start();
$_SESSION['user_id'] = 42;
$_SESSION['username'] = 'php_user';
echo 'Сессия запущена, id: ' . session_id();
Результат в браузере — строка вида Сессия запущена, id: 8f14e45fceea167a5a36dedd4bea2543 (конкретные символы каждый раз случайны, важна их длина и то, что это одна и та же строка при повторных запросах из того же браузера).
Чтение значений на другой странице того же сайта (нужен session_start() в начале файла):
<?php
session_start();
if (isset($_SESSION['username'])) {
echo 'Привет, ' . htmlspecialchars($_SESSION['username']);
} else {
echo 'Сессия не найдена, нужна авторизация';
}
Если cookie PHPSESSID от предыдущего запроса сохранилась, скрипт выведет Привет, php_user. Без cookie (например, в приватном окне) — выведет вторую строку, потому что стартует новая пустая сессия.
Полное завершение сессии — частая ошибка в том, что session_destroy() удаляет файл на сервере, но не трогает cookie в браузере. Правильный вариант закрытия сессии выглядит так:
<?php
session_start();
$_SESSION = [];
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
session_destroy();
Здесь три шага: очистить массив в памяти, явно затереть cookie на клиенте с истекшим сроком, и только затем удалить файл сессии на сервере. Без второго шага браузер продолжит отправлять старый PHPSESSID, даже если файла с таким именем на сервере уже нет.
| Функция | Что делает |
|---|---|
session_start() |
открывает сессию: читает существующую или создает новую |
$_SESSION |
суперглобальный массив для чтения и записи данных сессии |
session_unset() |
очищает массив $_SESSION в текущем запросе |
session_destroy() |
удаляет файл сессии на сервере |
session_regenerate_id() |
меняет идентификатор сессии, сохраняя данные |
session_id() / session_name() |
возвращают текущий id сессии / имя ее cookie |
Защита сессии: фиксация, перехват, CSRF
Сессия хранит данные о вошедшем пользователе, поэтому ее защита — не опциональная надстройка, а обязательная часть логина. У трех типовых угроз разная природа и разная защита, путать их нельзя.
Session fixation. Атакующий заранее навязывает жертве известный ему идентификатор сессии (например, через ссылку с параметром), а после того как жертва вводит в эту сессию логин и пароль, использует тот же идентификатор для доступа. Защита — обязательно вызывать session_regenerate_id(true) сразу после успешной проверки логина и пароля, до того как в $_SESSION попадут данные авторизованного пользователя:
<?php
session_start();
// Логин и пароль уже проверены выше по коду
session_regenerate_id(true); // true - удалить старый файл сессии на сервере
$_SESSION['user_id'] = $user['id'];
$_SESSION['authenticated'] = true;
Параметр true важен: без него старый файл сессии останется на диске, и если атакующий успел его подсмотреть до регенерации, он какое-то время еще будет действовать.
Session hijacking (кража идентификатора). Если id сессии перехвачен — через XSS на странице или прослушивание незащищенного соединения, — атакующий может выдать себя за пользователя без пароля. От чтения cookie через JavaScript защищает флаг httponly, от перехвата в сети — флаг secure (cookie отправляется только по HTTPS). Задать их лучше до вызова session_start():
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
Флаг secure требует, чтобы сайт действительно работал по HTTPS — на локальном сервере без TLS браузер такую cookie не примет вовсе, и сессия перестанет сохраняться. Это частая причина, по которой «на проде работает, локально нет».
CSRF (подделка межсайтового запроса). Здесь cookie сессии не крадут, а используют штатно: браузер сам подставляет ее в запрос, отправленный со стороннего сайта. Атрибут samesite (Lax или Strict) ограничивает, в каких межсайтовых запросах браузер вообще подставляет cookie, но для форм и API это стоит дополнять отдельным CSRF-токеном — samesite снижает риск, но не заменяет токен полностью, особенно для GET-переходов при Lax.
| Угроза | В чем суть | Основная защита |
|---|---|---|
| Session fixation | Атакующий подсовывает свой id сессии до входа жертвы | session_regenerate_id(true) после логина |
| Session hijacking | id сессии украден (XSS, сеть) | httponly, secure, HTTPS |
| CSRF | Запрос со стороннего сайта использует чужие cookie | samesite + CSRF-токен в форме |
Отдельно стоит хранение самого файла сессии. На виртуальном хостинге с общим session.save_path файлы сессий разных сайтов могут физически лежать в одном каталоге — если права на каталог настроены слабо, это риск чтения чужих сессий. Для проектов на нескольких серверах файловое хранилище вообще не подходит — обычно используют Redis или базу данных через свой обработчик сессий (session_set_save_handler), а не одну общую файловую систему.
Актуальная версия PHP и настройки сессии
Механизм сессий, описанный выше, стабилен с PHP 7.1 (криптостойкая генерация id) и работает без изменений в текущих активных ветках PHP 8.x. На момент публикации актуальны ветки PHP 8.3-8.5, где ежегодный релиз-цикл PHP не менял ни session_start(), ни формат $_SESSION. Точный номер последней минорной версии и статус ее поддержки на дату чтения статьи есть на официальной странице поддерживаемых версий php.net.
Выводы
- Сессия хранит данные на сервере, cookie на клиенте передает только ее идентификатор — это разные механизмы.
- Жизненный цикл:
session_start()создает или продолжает сессию,$_SESSIONхранит данные,session_destroy()вместе с затиранием cookie завершает ее полностью. - После успешного логина обязательна
session_regenerate_id(true)— это защита от session fixation. - Флаги
httponly,secureиsamesiteзакрывают разные угрозы (кража id, перехват в сети, CSRF) и нужны все три вместе, а не по отдельности. - На многосерверных проектах файловое хранилище сессий заменяют на Redis или БД через собственный обработчик.
Где применяется / связь с практикой
Освойте тему на практике
Работа с сессиями — база любой авторизации на PHP: личный кабинет, корзина в интернет-магазине, админ-панель. Если хотите разобрать эту тему системно вместе с остальным стеком backend-разработки на PHP, посмотрите курс «Разработчик PHP» — там разбирают безопасность веб-приложений на реальных проектах. Проверить формат подачи можно на открытых уроках Otus — они бесплатные и идут регулярно по разным направлениям.
FAQ
Можно ли хранить в сессии PHP объекты, а не только строки и числа?
Да, PHP сериализует любые типы, включая массивы и объекты, при условии что класс объекта доступен на момент десериализации (обычно достаточно автозагрузки классов).
Что будет, если вызвать session_start() дважды за один запрос?
PHP выдаст notice о повторном старте сессии и проигнорирует второй вызов, действующей останется первая инициализация.
Сколько живет сессия, если пользователь ничего не делает на сайте?
По умолчанию файл сессии считается устаревшим через session.gc_maxlifetime секунд (обычно 1440, то есть 24 минуты) простоя, но фактическое удаление зависит от вероятностного сборщика мусора session.gc_probability/session.gc_divisor, поэтому точное время нужно проверять по настройкам конкретного сервера.



