Logowanie, sesje i 2FA w Laravelu: jak chronimy konta w panelach firmowych
Autor:
Paweł Matusiak
·
Najsłabszym ogniwem aplikacji często nie jest serwer, tylko hasło Firma2024! używane jeszcze w trzech innych serwisach. Dobre uwierzytelnianie to połączenie Laravela, polityki haseł i drugiego składnika.
Panel CRM albo księgowy to łakomy kąsek: są tam faktury, dane klientów, czasem dane kart. Atakujący nie zawsze łamie kryptografię. Częściej zgaduje hasło, kradnie sesję albo resetuje konto przez źle zrobiony e-mail. Dlatego logowanie projektujemy tak samo poważnie jak samą logikę biznesową. W OWASP Top 10 2025 to A07 Authentication Failures — i nadal jeden z najczęstszych powodów, dla których w audycie starego panelu otwieramy drzwi bez łamania czegokolwiek.
W GESOFT nie piszemy własnego lepszego logowania od zera. Używamy utrzymywanego mechanizmu Laravel Auth, hashu bcrypt albo argon2id, sesji albo cookie Sanctum w panelu Vue i tokenów Sanctum w Androidzie. Drugi składnik dokładamy tam, gdzie stawka jest wysoka: admin, księgowość, eksport bazy. Poniżej: co jest fundamentem, czym różni się SPA od API, jak blokujemy zgadywanie i jak wygląda 2FA, które ludzie naprawdę włączą — nie takie, które wyłączają po tygodniu, bo SMS nie dochodzi.
Fundament: Laravel Auth, nie własny wymysł
Laravel ma gotowy, utrzymywany mechanizm rejestracji, logowania i resetu hasła. Hasła hashujemy algorytmem bcrypt albo argon2id — w bazie nie ma jawnego hasła i nie da się go odzyskać, tylko ustawić nowe. Reset idzie jednorazowym, wygasającym linkiem na e-mail, nigdy nowym hasłem w treści wiadomości. Komunikat przy błędzie logowania nie zdradza, czy e-mail istnieje (enumeracja kont). Throttle na te endpointy jest włączony, nie zostawiony na później.
Fortify (albo równoważny, pilnowany przepływ) daje 2FA TOTP, potwierdzenie hasła przy wrażliwych akcjach i reguły resetu. Nie dokładamy przypadkowej paczki z katalogu, której autor zniknął. Łańcuch dostaw (A03) dotyczy też logowania: porzucona biblioteka 2FA jest gorsza niż brak 2FA, bo daje poczucie bezpieczeństwa i dziurę naraz. Szerszy kontekst ataków — w bezpieczeństwie aplikacji Laravel.
Sesje i API: dwa różne światy (Sanctum)
Dokumentacja Sanctum mówi wprost: do własnego SPA pierwszej strony nie używaj tokenów API. Użyj wbudowanego logowania ciasteczkowego. Sanctum nie wystawia wtedy tokenu — korzysta z sesji Laravela i ochrony CSRF. Ciasteczko sesji jest httpOnly, więc JavaScript go nie czyta. SPA na tej samej (albo uzgodnionej) domenie wysyła cookie, a mutujące requesty niosą X-XSRF-TOKEN. To jest właściwy model dla panelu Vue + Laravel, który opisujemy w Laravel + Vue: CRM i panele.
Aplikacja Android rozmawia z API przez tokeny Sanctum (personal access tokens). Token można nazwać, ograniczyć i unieważnić, gdy telefon zginie albo pracownik odejdzie z firmy. Nie trzymamy haseł w aplikacji mobilnej. Nie mieszamy sesji przeglądarki z tokenem mobilnym w jednym worku: to dwa klientów, dwa cykle życia, dwa sposoby odwołania dostępu. Gdy Sanctum widzi request, najpierw sprawdza cookie, potem nagłówek Authorization — kolejność jest po to, żeby SPA i telefon nie deptali sobie po sesjach.
Co blokuje zgadywanie i kradzież konta
- Limit prób logowania (rate limiting) na IP i na konto — po serii błędów konto się przycina, a nie czeka w nieskończoność. To tanie i skuteczne wobec list haseł.
- Blokada trywialnych haseł i wymóg minimalnej długości; przy panelach z danymi finansowymi albo szczególnymi kategoriami — dodatkowo 2FA.
- Wygasanie sesji przy bezczynności i wylogowanie na wszystkich urządzeniach po zmianie hasła albo po offboardingu.
- Ostrzeżenie e-mail przy logowaniu z nowej lokalizacji albo nowej przeglądarki (tam, gdzie to ma sens i nie zaleje skrzynki).
- Brak szczegółowego komunikatu nie ma takiego e-maila — atakujący nie enumeruje kont.
- Potwierdzenie aktualnego hasła przed zmianą e-maila, hasła albo wyłączeniem 2FA — inaczej skradziona sesja kończy przejęciem na stałe.
2FA, które ludzie naprawdę włączą
Dwuskładnikowe logowanie opieramy na TOTP (aplikacja authenticator: Authy, Google Authenticator, menedżer haseł) albo kodzie SMS, zależnie od branży i od tego, czy użytkownicy mają służbowe telefony. Dla administratorów i księgowości 2FA jest obowiązkowe, nie opcjonalne. Kody zapasowe trafiają do klienta raz, przy aktywacji — nie wysyłamy ich potem mailem na wszelki wypadek. Recovery musi być procedurą z weryfikacją tożsamości, nie przyciskiem wyślij kody jeszcze raz na ten sam zhackowany mail.
Uprawnienia po zalogowaniu
Zalogowanie to dopiero początek. W Laravelu policies i gates decydują, czy użytkownik może zobaczyć fakturę, wyeksportować bazę albo zmienić rolę kolegi. Testujemy to automatycznie: scenariusz handlowiec nie pobierze raportu płac ma być zielony na CI, nie tylko wydaje się, że w menu nie ma linku. Ukryty URL z ID faktury (A01) jest częstszy niż złamany bcrypt. Wspólne konto biuro uniemożliwia ten model — nie wiesz, kto wyeksportował, nie wyłączysz jednej osoby, nie spełnisz rozliczalności z RODO.
Offboarding jest częścią logowania. Gdy handlowiec odchodzi: konto się deaktywuje, tokeny Sanctum padają, sesje gaszą, skrzynka nie zostaje celem resetu hasła. Jeśli hasła były w Excelu, pierwszy tydzień projektu to rozdzielenie osób, własne hasła i 2FA na adminie — jeszcze przed nowymi funkcjami. Inaczej budujemy piękny panel na fundamencie, który ktoś zgadnie w weekend.
Reset hasła, enumeracja i maile, które nie pomagają atakującemu
Formularz nie pamiętam hasła zawsze odpowiada tak samo: jeśli konto istnieje, wysłaliśmy instrukcję. Link ma krótki TTL, jednorazowość i nie jest tokenem w URL-u, który potem wyląduje w logach proxy. Nowe hasło nigdy nie jedzie w treści maila. Skrzynka do resetu musi być tą, którą użytkownik kontroluje — nie aliasem biuro@, do którego ma klucz połowa firmy. Przy panelach z danymi szczególnych kategorii reset z nowej przeglądarki może wymagać drugiego kanału albo zwłoki z alertem do admina.
Jeśli Twój obecny panel ma wspólne konto biuro albo hasła w Excelu — to pierwszy temat do rozmowy, jeszcze przed nowymi funkcjami. Opisz, kto i do czego ma mieć dostęp. Zaprojektujemy logowanie pod realne ryzyko, nie pod checklistę z internetu. W nowym projekcie to jest w zakresie od dnia jednego, razem z Git i 6 miesiącami gwarancji. W starym — często jako osobny, krótki etap przed resztą backlogu.
Sanctum w praktyce: SPA, cookie i pierwszy request
Typowy błąd przy Vue + Laravel to wystawienie tokenu API do własnego panelu na tej samej domenie i trzymanie go w localStorage. XSS wtedy kradnie token na stałe. Model z dokumentacji jest inny: SPA woła endpoint cookies (CSRF cookie), loguje się przez zwykłą sesję, kolejne requesty idą z credentials i nagłówkiem X-XSRF-TOKEN. Sanctum sprawdza, czy Origin / Referer jest z listy stanów SPA. To nie jest magia — to konfiguracja SANCTUM_STATEFUL_DOMAINS i SESSION_DOMAIN. Źle ustawione ciasteczko na IP albo na http kończy się wylogowaniem co odświeżenie albo wyciekiem na siostrzanej subdomenie.
Tokenów używamy świadomie: Android, integracja partnera, skrypt, który nie jest przeglądarką. Każdy token ma nazwę (telefon Magdy, import nocny), datę i możliwość skasowania z panelu admina. Nie ma jednego wiecznego klucza w README. Przy odejściu pracownika kasujemy usera i jego tokeny w jednej transakcji. To taniej niż tłumaczenie klientowi, że były stażysta nadal widzi cennik, bo JWT wygasa za 30 dni i nie mamy denylist.
Polityka haseł bez teatru
Wymaganie wielkiej litery, cyfry i znaku specjalnego przy ośmiu znakach daje Firma2024! — hasło ze wszystkich list. Lepiej: większa minimalna długość, blokada oczywistych haseł, sprawdzenie czy nie wyleciało w znanym wycieku (tam gdzie to legalne i nie spowalnia logowania), menedżer haseł po stronie ludzi. Siła jest w długości i unikalności, nie w cyrku znaków. 2FA na adminie zamyka resztę: nawet wyciek z innej usługi nie otwiera księgowości bez drugiego składnika.
Kapslock, spacja na końcu, menedżer, który wkleja z nową linią — komunikaty logowania mają pomagać człowiekowi, nie atakującemu. Po udanym logowaniu nie wołamy całego drzewa uprawnień na trzy rundy. Po nieudanym nie mówimy konto zablokowane na zawsze, tylko spróbuj za N minut i dajemy właścicielowi panel do odbicia kolegi, który wpisał stare hasło dziesięć razy. Inaczej w poniedziałek stoi cała firma, bo nikt nie umie zdjąć throttle.
WebAuthn, kody zapasowe i onboarding zespołu
Klucz sprzętowy albo passkey (WebAuthn) jest dziś najtwardszym drugim składnikiem: nie ma SMS do przejęcia i nie ma TOTP do sfotografowania. W MŚP wdrażamy go tam, gdzie trzy osoby admina to uniosą i gdzie zgubiony klucz ma procedurę. Reszta zespołu zostaje na TOTP. Mieszany park (klucz na adminie, TOTP na handlowcu, SMS na magazynie bez smartfona służbowego) jest normalny. Ważne, żeby polityka była w kodzie (wymagane / opcjonalne per rola), nie w mailu proszę włączcie.
Onboarding nowego pracownika: konto imienne, tymczasowe hasło jednorazowe, wymuszenie 2FA przed wejściem w kartoteki, szkolenie trzech kliknięć (logowanie, wylogowanie, zgubiony telefon). Offboarding lustrzany. Jeśli agencja zewnętrzna potrzebuje dostępu, dostaje własne konto z datą ważności, nie hasło właściciela. To samo dotyczy GESOFT na etapie prac — po wdrożeniu nasze konta znikają albo zostają jako utrzymanie z 2FA, zapisane w umowie, nie jako wieczny backdoor.
Sesja w SPA musi umieć umrzeć: timeout bezczynności, wyloguj wszędzie, rotacja po zmianie hasła. Remember me na panelu z fakturami ustawiamy ostrożnie albo wcale. Na wspólnym komputerze w magazynie konto nie może zostawać na tydzień. To detale, które w dokumentacji Sanctum są oczywiste, a w firmie z trzema zmianami — nie. Zapisujemy je w instrukcji wdrożenia, którą oddajemy razem z Gitem, nie w Slacku tydzień po starcie. 6 miesięcy gwarancji obejmuje błędy w tym flow, nie nowe metody logowania dopisane ad hoc po starcie bez aneksu. Instrukcja offboardingu i lista aktywnych tokenów Sanctum są częścią protokołu odbioru i dokumentacji Git.
Hash, pepper i to, czego nie odzyskasz z bazy
Laravel Hash::make domyślnie używa bcrypt (algorytm 2y) z kosztem, który da się podnieść w config/hashing.php. Argon2id jest dostępny i bywa lepszy na nowszym PHP, gdy hosting nie obcina memory_cost. W bazie jest wyłącznie hash. „Odzyskanie hasła” nie istnieje — jest reset. Sól jest w samym bcrypt/argon, nie dokładamy własnego MD5 na wierzchu. Pepper (sekret aplikacji dokładany przed hashem) stosujemy tylko świadomie, bo zgubiony APP_KEY/pepper oznacza masowy reset haseł. Nigdy nie logujemy hasła, nawet przy błędzie walidacji.
Wyciek tabeli users bez 2FA i z bcrypt o niskim koszcie to offline cracking. Dlatego na adminie 2FA jest obowiązkowe, a koszt hasha nie zostaje na default sprzed pięciu lat bez przeglądu. Przy migracji ze starych paneli md5/sha1 wymuszamy reset albo przezroczysty rehash przy pierwszym logowaniu (Hash::needsRehash). Nie zostawiamy dwóch algorytmów „bo stażyści jeszcze mają stare hasła” bez daty końca.
SameSite, Secure, CSRF i pierwszy request SPA
Cookie sesji Laravela: HttpOnly (JS nie czyta), Secure na HTTPS, SameSite=Lax jako rozsądny default dla panelu, który otwiera linki z maila. Strict, gdy panel nie musi wstawać z zewnątrz. Sanctum dla własnego SPA nie wydaje tokenu — używa tej sesji plus X-XSRF-TOKEN. SANCTUM_STATEFUL_DOMAINS musi zawierać origin frontu; SESSION_DOMAIN źle ustawiony na goły IP albo http kończy się wylogowaniem albo wyciekiem na siostrzanej subdomenie. localStorage na token własnego panelu jest błędem: XSS kradnie go na stałe. Dokumentacja Sanctum mówi to wprost od lat.
CSRF nie dotyczy bezpiecznych GET. Mutujące endpointy (POST/PUT/PATCH/DELETE) bez tokenu / bez cookie stateful odrzucamy. API token dla Androida idzie nagłówkiem Authorization, nie w query (logi proxy). Token ma nazwę, datę i revoke. Idle timeout na panelu z fakturami jest krótszy niż na blogu. Remember-me na magazynowym PC wyłączamy. Wyloguj na wszystkich urządzeniach po zmianie hasła kasuje sesje i tokeny w jednej transakcji — to offboarding w kodzie, nie prośba na Slacku.
Fortify, TOTP i recovery bez maila „wyślij kody jeszcze raz”
Laravel Fortify (albo równoważny, utrzymywany przepływ) daje TOTP, potwierdzenie hasła przy wyłączeniu 2FA i przy zmianie e-maila, throttle na challenge. Sekret TOTP nie wraca potem w JSON. Kody zapasowe hashowane, pokazane raz. Recovery to procedura z weryfikacją tożsamości poza pasmem, nie przycisk na tę samą skrzynkę, która mogła paść. SMS zostawiamy, gdy zespół nie uniesie authenticatora; SIM swap jest realny przy księgowości. WebAuthn/passkey — dla wąskiego grona adminów, z procedurą zgubionego klucza.
A07 2025 (Authentication Failures) to nie tylko 2FA. To enumeracja, słabe reset URL w logach, brak throttlingu, sesja po wylogowaniu, JWT bez denylist. Checklistę wiążemy z OWASP i Laravelem i z audytem, gdy panel już stoi. W nowym projekcie logowanie, role i 2FA tam gdzie trzeba są w zakresie, z Git i 6 miesiącami gwarancji. Opisz, kto wchodzi w kartoteki — zaprojektujemy pod ryzyko, nie pod modę.
Najczęściej zadawane pytania
- Czy 2FA jest obowiązkowe w każdym panelu?
- Dla administratorów i księgowości — tak. Dla kont o niskich uprawnieniach bywa opcjonalne. Dobieramy metodę (TOTP, SMS, czasem klucz) do ryzyka, nie do mody.
- Czy SMS wystarczy jako drugi składnik?
- Bywa wygodny, ale kartę SIM da się przejąć. Przy fakturach i danych osobowych wolimy aplikację TOTP albo klucz sprzętowy. SMS zostawiamy, gdy zespół nie uniesie authenticatora.
- Co z aplikacją mobilną?
- Hasła nie trzymamy w apce. Android używa tokenów Sanctum, które da się unieważnić po zgubieniu telefonu albo odejściu pracownika. Panel webowy idzie na sesji / cookie, nie na tym samym tokenie.
- Wspólne konto biuro — jak szybko to zmienić?
- To pierwszy temat, jeszcze przed nowymi funkcjami. Rozdzielamy osoby, wymuszamy własne hasła i 2FA na adminie. Bez tego logi audytowe i RODO są fikcją.
- Czy Sanctum to JWT?
- Nie w modelu, którego używamy na co dzień. SPA pierwszej strony: sesja i cookie z CSRF, zgodnie z dokumentacją Sanctum. Mobilka: odwoływalne tokeny dostępowe. Nie pakujemy samopodpisanego JWT na lata bez revokacji, chyba że jest osobny, świadomy powód.
- Co jeśli ktoś zgubi telefon z TOTP?
- Kody zapasowe wydane przy aktywacji albo procedura recovery z weryfikacją tożsamości (nie mail wyślij kody jeszcze raz na tę samą skrzynkę). Admin może zresetować 2FA po potwierdzeniu poza pasmem.
- Czy menedżer haseł wystarczy zamiast 2FA?
- Menedżer zamyka ponowne użycie haseł i zgadywanie. Nie zamyka wycieku hasła z innej usługi ani podglądu przez ramię. Drugi składnik na kontach z pieniędzmi i eksportem zostaje.
- Czy to wchodzi w wycenę aplikacji?
- W nowym projekcie Laravel logowanie, role i 2FA tam gdzie trzeba są w zakresie, nie jako płatny dodatek bezpieczeństwo. Stary panel — osobny, krótki etap po audycie. 6 miesięcy gwarancji i Git jak zawsze.
Powiązana usługa:
Aplikacje Laravel i Vue.js
Opisz projekt