<? phpukraine СПІВБЕСІДИ
⌕Пошук по платформі
БЕЗПЕКА · MIDDLE

Що таке фіксація і викрадення сесії і як правильно робити вхід і вихід?

Фіксація сесії: зловмисник заздалегідь підсовує жертві відомий йому id сесії, жертва входить, і якщо id після входу не змінився, зловмисник уже в акаунті. Тому при кожній зміні рівня доступу id регенерують: `session_regenerate_id(true)` у PHP, а в Laravel 13 це робить сам `Auth::login()` і `Auth::attempt()`. При виході сесію інвалідовують (`invalidate()` і `regenerateToken()`), а викрадення cookie ускладнюють прапорцями `Secure`, `HttpOnly`, `SameSite` і префіксом `__Host-`.

Навіщо регенерувати id сесії після входу, якщо користувач і так той самий?
Що не так з кодом входу: перевірили пароль і зробили `session(['user_id' => $user->id])`?
Після `Auth::logout()` старе cookie сесії ще працює. Чому?
Користувач змінив пароль, бо підозрює злам. Як розлогінити всі інші пристрої?
session fixation session hijacking session_regenerate_id logout cookies AuthenticateSession

Сесія тримається на одному рядку: id у cookie. Хто його знає, той для сервера і є користувачем. Звідси дві атаки. Викрадення: зловмисник отримує id вже залогіненої людини через XSS, незахищене HTTP-зʼєднання, лог чи URL. Фіксація хитріша: зловмисник отримує id від сервера сам, ще без входу, і підсовує його жертві через посилання з ?PHPSESSID=... у старих конфігураціях, через cookie з піддомену чи через XSS. Жертва входить, сервер записує в цю сесію user_id, і якщо id не змінився, зловмисник з тим самим cookie опиняється в її акаунті.

Від фіксації захищає просте правило: id сесії змінюється при кожній зміні привілеїв. У чистому PHP це session_regenerate_id(true) одразу після перевірки пароля; аргумент true видаляє стару сесію зі сховища, без нього старий id ще деякий час працює. Додатково вмикають session.use_strict_mode=1, щоб PHP не приймав id, яких сам не видавав, бо за замовчуванням ця опція вимкнена. У Laravel 13 регенерацію робить сам guard: SessionGuard::login(), через який проходять attempt(), login() і loginUsingId(), викликає regenerate(true), а той ще й видає новий CSRF-токен. Тому фіксацію в Laravel-проєкті майже завжди знаходять у власному «спрощеному вході», який пише user_id у сесію в обхід guard.

Вихід потребує тієї ж уважності. Auth::logout() прибирає користувача з сесії й оновлює remember-токен, але саму сесію не знищує: інші дані в ній і CSRF-токен лишаються. Тому контролер виходу додає $request->session()->invalidate() і regenerateToken(). Розлогінити інші пристрої після зміни пароля допомагає Auth::logoutOtherDevices() разом з middleware auth.session, який порівнює хеш пароля в сесії з поточним. Перед чутливими діями (зміна email, видалення акаунта, нові API-токени) пароль просять ще раз через password.confirm.

Викрадення ускладнюють прапорці cookie. HttpOnly не дає скрипту прочитати id, Secure не дає браузеру відправити cookie по HTTP, SameSite=Lax обмежує крос-сайтові запити, а префікс __Host- забороняє перезаписати cookie з піддомену. У Laravel HttpOnly і Lax увімкнені за замовчуванням, а SESSION_SECURE_COOKIE варто ставити явно, бо без нього прапорець залежить від того, чи бачить застосунок HTTPS за балансувальником. Привʼязка сесії до IP чи User-Agent допомагає мало: мобільні IP змінюються постійно, а заголовок підробляється. Механіку сесій, сховища і блокування паралельних запитів розібрано в картці про сесії й cookies.

// Чистий PHP: після успішної перевірки пароля новий id, старий файл сесії видаляємо.
session_start();
if (password_verify($password, $user->password_hash)) {
    session_regenerate_id(true);
    $_SESSION['user_id'] = $user->id;
}

// Laravel 13: attempt() і login() самі викликають regenerate(true)
// і заодно видають новий CSRF-токен.
if (Auth::attempt($credentials, $request->boolean('remember'))) {
    $request->session()->regenerate(); // як у документації; повторна регенерація не шкодить
    return redirect()->intended('/dashboard');
}

// Було: власний «логін» в обхід guard. id, з яким прийшла людина, стає автентифікованим.
session(['user_id' => $user->id]);

// Вихід: logout() лише прибирає користувача, сама сесія лишається.
Auth::logout();
$request->session()->invalidate();      // нова порожня сесія, стару видалено зі сховища
$request->session()->regenerateToken(); // новий CSRF-токен

// Інші пристрої: працює разом з middleware auth.session на маршрутах.
Auth::logoutOtherDevices($request->input('current_password'));
Що фіксація і викрадення це різні атаки: у першій зловмисник навʼязує id до входу, у другій краде його після.
Що id сесії міняють на кожній зміні привілеїв: вхід, вихід, підвищення ролі, підтвердження пароля для чутливих дій.
Що в Laravel 13 `SessionGuard::login()` сам викликає `regenerate(true)`, тож фіксацію відкриває лише власний «логін» в обхід guard.
Що `Auth::logout()` прибирає користувача з сесії, але не знищує її, тому поруч потрібні `invalidate()` і `regenerateToken()`.
Що `session.use_strict_mode=1` у PHP не приймає id, яких сервер не створював, і що за замовчуванням ця опція вимкнена.
Зробити власний вхід через `session(['user_id' => $id])` чи `$_SESSION['user_id'] = $id` без регенерації: id, з яким людина прийшла, стає автентифікованим.
Викликати `session_regenerate_id()` без `true`: новий id видано, але старий файл сесії лишається на сервері і ще якийсь час працює.
Обмежитись `Auth::logout()` у контролері виходу: дані сесії і CSRF-токен лишаються, а скопійоване раніше cookie веде в ту саму сесію.
Привʼязувати сесію до IP-адреси як головний захист: мобільні користувачі змінюють IP постійно, а зловмисник за тим самим NAT чи проксі проходить перевірку.
Вважати, що HTTPS на сайті автоматично робить cookie `Secure`: якщо прапорця немає, браузер відправить cookie і по HTTP, наприклад через підмінене посилання в публічному Wi-Fi.
ПОРАДА

Скажіть правило одним реченням: «id сесії змінюється щоразу, коли змінюється те, що ця сесія дозволяє». З нього випливає і регенерація при вході, і інвалідація при виході, і повторна перевірка пароля перед чутливими діями.

оновлено 8 жовтня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

Суть фіксації в тому, що зловмисник знає id ще до входу, тож захист у зміні id в момент входу. `Auth::logout()` лише очищує дані користувача в сесії; знищує її `invalidate()`. `HttpOnly` заважає прочитати cookie скриптом, але фіксація не потребує читання: id уже відомий. Без аргументу `true` старий файл сесії лишається на сервері.