Pri prijavi uporabnik v obrazec vnese uporabniško ime in geslo. Aplikacija nato preveri, ali tak uporabnik obstaja in ali vneseno geslo ustreza shranjenemu geslu v podatkovni zbirki.
Prijava je občutljiv del spletne aplikacije, ker določa, kdo lahko dostopa do zaščitenih strani. Zato mora biti postopek prijave izveden z varnim shranjevanjem gesel, pripravljenimi stavki, pravilno uporabo sej in splošnimi sporočili ob napakah.
Pomni: Gesel v podatkovni bazi ne shranjujemo v navadnem besedilu. Shranimo zgoščeno vrednost gesla, pri prijavi pa pravilnost preverimo s funkcijo password_verify().
Po uspešni prijavi je priporočljivo izvesti session_regenerate_id(true). S tem zmanjšamo tveganje zlorabe starega identifikatorja seje.
Vsebina strani
Osnovna pravila
Pri prijavi uporabnika preverimo identiteto uporabnika in ob uspehu ustvarimo prijavno stanje v seji. Pri tem ne smemo razkriti, ali je napačno uporabniško ime ali geslo.
- Prijavni obrazec naj uporablja metodo
POST. - Uporabniško ime in geslo preberemo iz obrazca.
- Obe polji morata biti izpolnjeni.
- Uporabnika poiščemo s pripravljeno poizvedbo.
- Geslo preverimo s funkcijo
password_verify(). - Po uspešni prijavi regeneriramo ID seje.
- V
$_SESSIONshranimo samo potrebne podatke. - Uporabnika preusmerimo na zaščiteno stran.
Pozor: Gesla nikoli ne preverjamo z neposredno primerjavo, kot je $vnesenoGeslo === $gesloIzBaze, če je v bazi pravilno shranjen hash. Za preverjanje uporabimo password_verify().
Prijava uporabnika v aplikacijo
Prijava je pomemben del spletne aplikacije, saj z njo omejimo dostop do zaščitenih strani. Po uspešni prijavi uporabnika navadno shranimo v sejo in mu omogočimo dostop do administrativnega ali uporabniškega dela aplikacije.
Pri prijavi običajno izvedemo naslednje korake:
- uporabnik izpolni obrazec za prijavo,
- aplikacija preveri, ali sta polji izpolnjeni,
- aplikacija poišče uporabnika v tabeli
users, - preveri se pravilnost gesla,
- po uspešni prijavi se nastavijo podatki v seji,
- uporabnik se preusmeri na zaščiteno stran.
Gesel v bazi ne shranjujemo v navadnem besedilu, ampak kot varne zgoščene vrednosti. Pri prijavi zato uporabimo funkcijo za preverjanje gesla, ne pa neposredne primerjave z navadnim nizom.
Pomni: Sporočilo ob napačni prijavi naj bo splošno, na primer Uporabniško ime ali geslo ni pravilno. Tako ne razkrivamo, ali uporabniško ime obstaja.
Osnovni primer z mysqli
Spodnji zgled prikaže osnovno prijavo uporabnika z uporabo mysqli, seje in preverjanja gesla s funkcijo password_verify().
<?php
session_start();
define('DB_SERVER', 'localhost');
define('DB_USER', 'uporabnik');
define('DB_PASS', 'skritoGeslo');
define('DB_NAME', 'knjiznica');
$connection = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
if (!$connection) {
die(
'Povezava s podatkovno zbirko ni vzpostavljena: ' .
mysqli_connect_error() .
' (' . mysqli_connect_errno() . ')'
);
}
$napaka = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$passwordAttempt = trim($_POST['password'] ?? '');
if ($username === '' || $passwordAttempt === '') {
$napaka = 'Vpišite uporabniško ime in geslo.';
} else {
$stmt = mysqli_prepare(
$connection,
"SELECT id, username, password FROM users WHERE username = ?"
);
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
if (!$user || !password_verify($passwordAttempt, $user['password'])) {
$napaka = 'Uporabniško ime ali geslo ni pravilno.';
} else {
session_regenerate_id(true);
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['logged_in'] = time();
header('Location: admin.php');
exit();
}
mysqli_stmt_close($stmt);
}
}
mysqli_close($connection);
?>
Pri mysqli uporabnika poiščemo s pripravljeno poizvedbo. Vrednost $username je vezana s funkcijo mysqli_stmt_bind_param(), zato je ne vstavljamo neposredno v poizvedbo.
Osnovni primer s PDO
Tudi z vmesnikom PDO prijavo izvedemo s pripravljeno poizvedbo, nato pa geslo preverimo s funkcijo password_verify().
<?php
session_start();
$streznik = 'localhost';
$baza = 'knjiznica';
$uporabnik = 'uporabnik';
$geslo = 'skritoGeslo';
try {
$pdo = new PDO("mysql:host=$streznik;dbname=$baza;charset=utf8mb4", $uporabnik, $geslo);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$napaka = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$passwordAttempt = trim($_POST['password'] ?? '');
if ($username === '' || $passwordAttempt === '') {
$napaka = 'Vpišite uporabniško ime in geslo.';
} else {
$stmt = $pdo->prepare(
"SELECT id, username, password
FROM users
WHERE username = :username"
);
$stmt->bindValue(':username', $username, PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user || !password_verify($passwordAttempt, $user['password'])) {
$napaka = 'Uporabniško ime ali geslo ni pravilno.';
} else {
session_regenerate_id(true);
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['logged_in'] = time();
header('Location: admin.php');
exit();
}
}
}
}
catch (PDOException $e) {
$napaka = 'Prijava trenutno ni mogoča.';
}
?>
Pri PDO uporabimo poimenovani parameter :username. Gesla ne iščemo neposredno v poizvedbi, ampak najprej pridobimo shranjeni hash in ga nato preverimo s funkcijo password_verify().
Varno shranjevanje in preverjanje gesel
Gesla se v podatkovni bazi ne smejo shranjevati kot navadno besedilo. Ob registraciji ali ustvarjanju uporabnika geslo pretvorimo v hash s funkcijo password_hash().
$hash = password_hash($geslo, PASSWORD_DEFAULT);
Pri prijavi vnesenega gesla ne hashiramo ročno za neposredno primerjavo. Namesto tega uporabimo funkcijo password_verify(), ki preveri, ali vneseno geslo ustreza shranjeni zgoščeni vrednosti.
if (password_verify($passwordAttempt, $user['password'])) {
// geslo je pravilno
}
| Funkcija | Namen |
|---|---|
password_hash() |
Ustvari varno zgoščeno vrednost gesla za shranjevanje v podatkovno bazo. |
password_verify() |
Preveri, ali vneseno geslo ustreza shranjeni zgoščeni vrednosti. |
session_regenerate_id(true) |
Po uspešni prijavi ustvari nov identifikator seje. |
Pozor: Tehničnih napak pri prijavi ne prikazujemo neposredno uporabniku. Podrobnosti napake lahko zabeležimo v dnevnik, uporabniku pa pokažemo splošno sporočilo.
Seje in preusmeritev po prijavi
Po uspešni prijavi v sejo shranimo podatke, ki jih aplikacija potrebuje za prepoznavanje prijavljenega uporabnika. Običajno shranimo identifikator uporabnika in čas prijave.
session_regenerate_id(true);
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['logged_in'] = time();
Po nastavitvi seje uporabnika preusmerimo na zaščiteno stran.
header('Location: admin.php');
exit();
Pomni: Po klicu header('Location: ...') uporabimo exit(), da se izvajanje skripte ustavi.
Primerjava pristopov mysqli in PDO
Oba pristopa omogočata varno prijavo s pripravljenimi stavki. Razlika je predvsem v zapisu poizvedb, vezavi parametrov in obravnavi napak.
| Korak | mysqli |
PDO |
|---|---|---|
| Priprava poizvedbe | mysqli_prepare() |
$pdo->prepare() |
| Parameter za uporabniško ime | ? |
:username |
| Vezava vrednosti | mysqli_stmt_bind_param() |
$stmt->bindValue() |
| Branje uporabnika | mysqli_fetch_assoc() |
$stmt->fetch() |
| Preverjanje gesla | password_verify() |
password_verify() |
Aplikacija Knjige
📘Aplikacija Knjige
V priloženi aplikaciji je prijava izvedena v datoteki 15_login.php. Datoteka najprej zažene sejo in preveri, ali je uporabnik že prijavljen. Če je, ga takoj preusmeri na administrativno stran.
Ob oddaji obrazca aplikacija prebere uporabniško ime in geslo, nato pa s pripravljeno poizvedbo PDO poišče uporabnika v tabeli users. Če uporabnik ne obstaja ali geslo ni pravilno, se izpiše splošno sporočilo o napačni prijavi.
Po uspešni prijavi aplikacija regenerira ID seje, v sejo shrani identifikator uporabnika in čas prijave, nato pa uporabnika preusmeri na zaščiteno administrativno stran. Na strani sta prikazani tudi povezavi za registracijo in za zamenjavo gesla.
15_login.phpNavodila za izdelavo prijave
- Najprej pripravimo obrazec za uporabniško ime in geslo.
- Ob oddaji obrazca preberemo oba podatka.
- Preverimo, ali sta obe polji izpolnjeni.
- S pripravljeno poizvedbo v bazi poiščemo uporabnika po uporabniškem imenu.
- Geslo preverimo s funkcijo password_verify().
- Ob napačni prijavi uporabniku prikažemo splošno sporočilo.
- Po uspešni prijavi regeneriramo ID seje.
- V
$_SESSIONshranimo potrebne podatke. - Uporabnika preusmerimo na zaščiteno stran aplikacije.
Pozor: Pred uporabo funkcije header() ne sme biti izpisa v brskalnik. Če je izpis že izveden, preusmeritev morda ne bo delovala.
Priporočila
- Gesla shranjuj kot rezultat funkcije
password_hash(). - Pri prijavi geslo preveri s funkcijo
password_verify(). - Za iskanje uporabnika uporabi pripravljen stavek.
- Po uspešni prijavi uporabi
session_regenerate_id(true). - V sejo shrani samo nujne podatke.
- Ob napaki prikaži splošno sporočilo.
- Po preusmeritvi uporabi
exit(). - Za zaščitene strani preverjaj, ali je uporabnik prijavljen.
Pogoste napake
- Gesla so shranjena v navadnem besedilu.
- Geslo se preverja z neposredno primerjavo nizov.
- Poizvedba za uporabnika ni pripravljena poizvedba.
- Po uspešni prijavi se ne izvede
session_regenerate_id(true). - V sejo se shranjuje preveč podatkov.
- Sporočilo ob napaki razkrije, ali uporabniško ime obstaja.
- Po klicu
header()manjkaexit(). - Zaščitena stran ne preverja, ali je uporabnik prijavljen.
Prijava ni samo preverjanje gesla. Varna prijava vključuje tudi pravilno shranjevanje gesel, pripravljene poizvedbe, varno ravnanje s sejami in nadzor dostopa do zaščitenih strani.