Сесія тримається на одному рядку: 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 сесії змінюється щоразу, коли змінюється те, що ця сесія дозволяє». З нього випливає і регенерація при вході, і інвалідація при виході, і повторна перевірка пароля перед чутливими діями.