Korisnik se žalio da admin prijava ne radi u Safariju — unese ispravan PIN, dobije grešku "Sesija istekla ili pogrešan PIN". U Chromeu radi bez problema. Klasičan bug koji je izgledao banalno ali je trajao sedmicama.
Šta se zapravo dešava
2FA flow ima dva POST zahtjeva:
- Korak 1: email + lozinka → server generiše PIN, sprema
$_SESSION['2fa_user_id'], šalje email - Korak 2: unos PIN-a → server čita session, verificira PIN
Između koraka 1 i 2 kod je pozivao session_regenerate_id(true) — standardna sigurnosna praksa. Problem: Safari s SameSite=Lax cookie politikom tretira POST→redirect sekvence kao cross-site u određenim konfiguracijama. Rezultat: Safari šalje stari session ID na korak 2, novi session ID nema 2fa_user_id, prijava pada.
Pogrešno rješenje (koje smo probali)
Dodali smo login_token koji se čuva u sesiji i šalje kao hidden field u formi. Ideja: ako token matchuje, sesija je ispravna. Nije pomoglo jer token je u novoj sesiji, ali form submita staru.
Ispravno rješenje
Pomjeriti session_regenerate_id(true) na jedino pravo mjesto — neposredno prije postavljanja admin session varijabli, nakon što je PIN verificiran:
// Pogrešno — regeneracija između dva POST zahtjeva:
// Korak 1: session_regenerate_id(true); $_SESSION['2fa_user_id'] = $uid;
// Ispravno — regeneracija tek na uspješnoj prijavi:
if ($pin_row) {
unset($_SESSION['2fa_user_id']); // Obriši 2FA podatke PRIJE regeneracije
session_regenerate_id(true); // Sad regeneriši
$_SESSION['admin_id'] = $user['id']; // Postavi admin sesiju
}
Još jedan Safari-specifičan bug
Auto-submit forme na 6-cifreni PIN koristio je setTimeout(200). Safari na iPhoneu još zatvara keyboard kad se forma submita s 200ms kašnjenjem, što može prekinuti request. Rješenje: povećati na 600ms i dodati zaštitu od dvostrukog submita.
Zaključak
Safari nije problematičan browser — on striktnije primjenjuje web standarde od Chromea u nekim oblastima, posebno oko cookie politike i session managementa. Kad 2FA radi u Chromeu a ne u Safariju, uvijek provjerite: session_regenerate_id poziciju, SameSite cookie atribut i timing asinhronih operacija.