Pentru a crea un sistem securizat de înregistrare-autentificare folosindu-ne de simbioza PHP/MySQL cu opțiunea de „menține-mă logat”, vom folosi MySQLi pentru interacțiunile cu baza de date și tehnici de securitate precum utilizarea hash al parolelor și cookie-uri securizate pentru opțiunea de „menține-mă logat”.
Configurarea bazei de date
În vederea testării vom crea un tabel pentru utilizatori care să stocheze informațiile necesare: ID
, nume de utilizator
, parola
(hashuită) și token
-ul pentru „Reţine-mă”.
1 2 3 4 5 6 |
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(100) NOT NULL, password VARCHAR(255) NOT NULL, remember_token VARCHAR(255) DEFAULT NULL ); |
Înregistrare
Vom folosi password_hash()
pentru a securiza parola înainte de stocarea ei în baza de date.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php // register.php include 'config.php'; if ($_SERVER['REQUEST_METHOD'] == 'POST') { $username = $_POST['username']; $password = $_POST['password']; $confirm = $_POST['confirm']; if($password!==$confirm) { echo "Cele două câmpuri pentru parolă nu coincid!"; } else { $hashed_password = password_hash($password, PASSWORD_BCRYPT); // Hash-uim parola // Pregătim și executăm interogarea $stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)"); $stmt->bind_param("ss", $username, $hashed_password); $stmt->execute(); $stmt->close(); echo "Înregistrare completă!"; } } ?> <h1>Registration<h1> <form action="<?= $_SERVER['PHP_SELF'];?>" method="post"> <input type="text" name="username" placeholder="Username" required> <input type="text" name="password" placeholder="Password" required> <input type="text" name="confirm" placeholder="Confirm password" required> <button type="submit">Login</button> </form> |
Autentificare
Atunci când ne logăm vom avea de ales dacă să bifăm opţiunea „Reţine-mă”. Astfel, dacă este bifată această opţiune, va fi generat un token securizat pe care îl stocăm în baza de date și în cookie-uri.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?php // login.php session_start(); include 'config.php'; if ($_SERVER['REQUEST_METHOD'] == 'POST') { $username = $_POST['username']; $password = $_POST['password']; $rememberMe = isset($_POST['remember_me']); // Pregătim și executăm interogarea pentru a găsi utilizatorul $stmt = $conn->prepare("SELECT id, username, password FROM users WHERE username = ?"); $stmt->bind_param("s", $username); $stmt->execute(); $stmt->store_result(); $stmt->bind_result($id, $dbUsername, $dbPassword); if ($stmt->num_rows > 0) { $stmt->fetch(); // Verificăm parola if (password_verify($password, $dbPassword)) { $_SESSION['user_id'] = $id; // Setăm sesiunea pentru utilizator if ($rememberMe) { $token = bin2hex(random_bytes(16)); // Generăm un token securizat // Salvăm token-ul în baza de date $stmt = $conn->prepare("UPDATE users SET remember_token = ? WHERE id = ?"); $stmt->bind_param("si", $token, $id); $stmt->execute(); setcookie("rememberme", $token, time() + (86400 * 30), "/", "", true, true); // Salvăm token-ul în cookie-uri } echo "Autentificare reușită!"; header("Refresh:1;url=index.php"); } else { echo "Parola incorectă!"; } } else { echo "Utilizator inexistent!"; } $stmt->close(); } ?> <h1>Login</h1> <form action="<?= $_SERVER['PHP_SELF'];?>" method="post"> <input type="text" name="username" placeholder="Username" required> <input type="text" name="password" placeholder="Password" required> <br><input type="checkbox" id="remember_me" name="remember_me"> <small>Reţine-mă</small> <button type="submit">Login</button> </form> |
Verificarea autentificării prin sesiune sau cookie-uri
La fiecare accesare a paginii (de ex., index.php), verificăm dacă utilizatorul este autentificat fie prin sesiune, fie prin cookie-urile „reţine-mă”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php // index.php session_start(); include 'config.php'; if (isset($_SESSION['user_id'])) { echo "Bine ai venit! (utilizator autentificat)"; } elseif (isset($_COOKIE['rememberme'])) { $token = $_COOKIE['rememberme']; // Verificăm token-ul în baza de date $stmt = $conn->prepare("SELECT id FROM users WHERE remember_token = ?"); $stmt->bind_param("s", $token); $stmt->execute(); $stmt->store_result(); if ($stmt->num_rows > 0) { $stmt->bind_result($userId); $stmt->fetch(); $_SESSION['user_id'] = $userId; // Setăm sesiunea pentru utilizator echo "Bine ai venit! (utilizator autentificat prin cookie)"; } else { echo "Autentificare eșuată!"; } $stmt->close(); } else { echo "Nu ești autentificat."; } ?> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php // check_auth.php session_start(); include 'config.php'; if (isset($_SESSION['user_id'])) { // autentificare prin sesiune return; // continuarea execuției paginii } elseif (isset($_COOKIE['rememberme'])) { $token = $_COOKIE['rememberme']; // Verificăm token-ul în baza de date $stmt = $conn->prepare("SELECT id FROM users WHERE remember_token = ?"); $stmt->bind_param("s", $token); $stmt->execute(); $result = $stmt->get_result(); // Verificăm dacă token-ul este valid if ($result->num_rows > 0) { $row = $result->fetch_assoc(); $_SESSION['user_id'] = $row['id']; // Setăm sesiunea pentru utilizator return; // autentificare prin prin cookie } setcookie('rememberme', '', time() - 3600, "/", "", true, true); // Dacă token-ul nu e valid, ștergem cookie-ul $stmt->close(); } header("Location: login.php"); // Dacă nu există sesiune sau cookie valid, redirecționăm la pagina de login exit; ?> |
Astfel, în paginile unde dorim să avem această verificare a autentificării, vom include fişierul.
1 2 3 4 |
<?php include 'check_auth.php'; // Restul conținutului paginii care necesită autentificare ?> |
Aşadar, prin fişierul de autorizare a autentificării:
- Sesiunea este verificată prima: Dacă utilizatorul este deja autentificat, evităm interacțiuni suplimentare, inclusiv verificarea cookie-urilor, ceea ce limitează suprafața de atac.
- Cookie-ul „reţine-mă” este o soluție de backup: Dacă nu există o sesiune, verificăm cookie-ul, dar doar dacă există un token valid asociat în baza de date. Dacă token-ul nu este valid, cookie-ul este șters.
- Reînnoirea sesiunii după autentificare cu cookie: Dacă autentificarea prin cookie este reușită, se creează o sesiune nouă, deci se revine la un mecanism de securitate mai robust.
- Pe lângă micile diferenţe între cele două pagini (mesaje nenecesare la pagina de autorizare), am alternat cuplul
store_result() - fetch()
cuget_result() - fetch_assoc()
, diferind modul de extragere a datelor, dar şi cum ne raportăm la numărarea înregistrărilor.
Diferența dintre store_result() și get_result():
•store_result()
este necesar atunci când utilizăm metodele de legare (bind_result()
), care îți permit să extragi rezultatele direct din interogarea pregătită. Această metodă trebuie folosită dacă intenționăm să lucrăm cubind_result()
.
•get_result()
funcționează doar dacă serverul MySQL este configurat să utilizeze drivere native pentru MySQLi (mysqlnd
), dar este folosit de obicei pentru a prelua un set de rezultate sub formă de obiect. Acesta poate returna unmysqli_result
care oferă metode precumfetch_assoc()
.
• După utilizarea metodei get_result(), numărul de rânduri din este accesat din obiectul rezultat ($result
), nu din$stmt
. Metodanum_rows
nu este disponibilă pe$stmt
când folosesimget_result()
.