Senior: питання на співбесіді з теми «Автентифікація»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
3 питання
2FA вимагає двох факторів: «що знаю» (пароль) + «що маю» (код із застосунку/SMS). Найпоширеніше - TOTP (Time-based One-Time Password) сумісно з Google Authenticator.
У Laravel найпростіше через Fortify, який має 2FA з коробки:
- генерація секрету й QR-коду для прив'язки;
- перевірка 6-значного коду при вході;
- одноразові recovery codes на випадок втрати пристрою.
// Fortify вмикає features:
Features::twoFactorAuthentication(['confirm' => true]),
Під капотом - пакет pragmarx/google2fa. Важливо: зберігати секрет зашифрованим, давати recovery-коди, за бажанням «запам'ятати пристрій».
Докладніше в документації: Двофакторна автентифікація (Fortify)
Типовий випадок - користувач змінив пароль, бо підозрює злам, і стара сесія на чужому пристрої має перестати працювати.
Для сесій у браузері:
Route::middleware(['auth', 'auth.session'])->group(function () {
// ...
});
Auth::logoutOtherDevices($request->input('current_password'));
logoutOtherDevices() перехешовує пароль, а middleware auth.session (AuthenticateSession) на кожному запиті порівнює хеш, збережений у сесії, з поточним. На інших пристроях хеші більше не збігаються - сесія закривається. Без auth.session на маршрутах виклик нічого не дає, і це найчастіша помилка.
Для API-токенів Sanctum механізм інший - токени відкликають явно:
$user->tokens()->delete(); // усі
$user->tokens()->where('id', $tokenId)->delete(); // один пристрій
«Запам'ятати мене» скидається через новий remember_token.
Повна картина для користувача - сторінка «Активні сесії» з пристроями й можливістю закрити окрему. Для цього сесії мають бути в базі (драйвер database), щоб їх можна було перелічити й видалити за user_id.
Після зміни пароля варто також надіслати лист «Ваш пароль змінено» - якщо це зробив не власник, він дізнається одразу.
Докладніше в документації: Скасування сесій на інших пристроях
Паролі зберігають лише як повільний односторонній хеш - bcrypt чи argon2id через Hash::make().
- Не шифрування: зашифроване відновлюється ключем, і витік ключа відкриває всі паролі.
- Не SHA-256 чи MD5: вони швидкі, і на відеокарті перебирають мільярди варіантів за секунду.
- Сіль генерується для кожного пароля окремо й зберігається в самому рядку хешу - окремо нічого не треба.
Посилення без скидання. Параметри хешування з часом піднімають - наприклад, BCRYPT_ROUNDS з 12 до 13. Старі хеші при цьому лишаються робочими, бо параметри записані в них самих.
Оновити їх можна лише в момент, коли відомий відкритий пароль, - при вході:
if (Hash::needsRehash($user->password)) {
$user->update(['password' => Hash::make($plainPassword)]);
}
У Laravel це робиться автоматично: опція rehash_on_login у config/hashing.php увімкнена за замовчуванням, і після успішного входу хеш перезаписується з новими параметрами.
Перехід на інший алгоритм (bcrypt → argon2id) потребує ще одного кроку. За замовчуванням Hash::check() відхиляє хеш, створений іншим алгоритмом, і кидає RuntimeException - це захист від підміни хешу. На час переходу ставлять HASH_VERIFY=false, і тоді перезапис при вході поступово переведе активних користувачів. Неактивні лишаться на старому, і для них можна запланувати примусове скидання через рік-два, а потім повернути перевірку.
Докладніше в документації: Визначення потреби в перехешуванні