HTTP không nhớ gì giữa hai yêu cầu. Cookie và session là cách để website nhớ người dùng: đăng nhập, giỏ hàng, ngôn ngữ. Bài này học cách tạo, đọc, xóa cookie và session, xây dựng đăng nhập/đăng xuất và các thiết lập bảo mật.
- Hiểu HTTP không trạng thái và vai trò cookie/session
- Tạo, đọc, xóa cookie với tùy chọn bảo mật
- Dùng session để đăng nhập, lưu giỏ hàng, thông báo flash
- Bảo vệ trang yêu cầu đăng nhập; chống chiếm phiên
- Cookie
- Mẩu dữ liệu nhỏ lưu ở trình duyệt, gửi kèm mọi request.
- Session
- Dữ liệu lưu ở máy chủ, định danh bằng session ID trong cookie.
- Flash message
- Thông báo chỉ hiện một lần sau chuyển hướng.
- Session hijacking
- Đánh cắp session ID để giả mạo người dùng.
Khái niệm và lý thuyết
Cookie do máy chủ đặt qua header, trình duyệt lưu và gửi lại ở mọi request tới cùng tên miền; người dùng xem và sửa được, nên chỉ lưu thứ không nhạy cảm (ngôn ngữ, giao diện). Session lưu dữ liệu trên máy chủ, trình duyệt chỉ giữ một cookie PHPSESSID ngẫu nhiên; thích hợp cho trạng thái đăng nhập, giỏ hàng.
Cả setcookie() và session_start() gửi header HTTP nên phải gọi trước mọi output (kể cả khoảng trắng trước <?php), nếu không gặp lỗi “headers already sent”.
Cookie
<?php
// Đặt cookie 30 ngày, chỉ gửi qua HTTPS, JS không đọc được
setcookie("ngon_ngu", "vi", [
"expires" => time() + 30 * 24 * 3600,
"path" => "/",
"secure" => true, // tắt khi thử localhost http
"httponly" => true,
"samesite" => "Lax",
]);
echo $_COOKIE["ngon_ngu"] ?? "chưa có"; // có giá trị từ request sau
setcookie("ngon_ngu", "", time() - 3600, "/"); // xóa: hết hạn trong quá khứSession: đăng nhập và đăng xuất
<?php
// dang-nhap.php
session_start();
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$email = trim($_POST["email"] ?? "");
// Lấy người dùng từ CSDL theo email (prepared statement)...
$nguoi_dung = ["id" => 1, "email" => "Địa chỉ email này đang được bảo vệ từ spam bots, bạn cần kích hoạt Javascript để xem nó. Bạn cần kích hoạt Javascript để xem nó. ", "ten" => "An",
"hash" => password_hash("matkhau123", PASSWORD_DEFAULT)];
if ($email === $nguoi_dung["email"] && password_verify($_POST["mat_khau"] ?? "", $nguoi_dung["hash"])) {
session_regenerate_id(true); // chống cố định phiên
$_SESSION["user"] = ["id" => $nguoi_dung["id"], "ten" => $nguoi_dung["ten"]];
$_SESSION["flash"] = "Đăng nhập thành công";
header("Location: index.php", true, 303); exit;
}
$loi = "Sai email hoặc mật khẩu"; // không nói rõ sai cái nào
}
// dang-xuat.php
session_start();
$_SESSION = [];
setcookie(session_name(), "", time() - 3600, "/");
session_destroy();
header("Location: dang-nhap.php"); exit;Bảo vệ trang và thông báo flash
<?php
// auth.php: require_once ở đầu mọi trang cần đăng nhập
session_start();
if (empty($_SESSION["user"])) {
$_SESSION["flash"] = "Vui lòng đăng nhập";
header("Location: dang-nhap.php"); exit;
}
// Hết hạn sau 30 phút không hoạt động
if (isset($_SESSION["last"]) && time() - $_SESSION["last"] > 1800) {
session_unset(); session_destroy(); header("Location: dang-nhap.php"); exit;
}
$_SESSION["last"] = time();
// Hiển thị flash một lần (trong header.php)
if (!empty($_SESSION["flash"])) {
echo "<div class='flash'>" . htmlspecialchars($_SESSION["flash"]) . "</div>";
unset($_SESSION["flash"]);
}Giỏ hàng bằng session
<?php
session_start();
$_SESSION["gio"] ??= [];
if (isset($_GET["them"])) {
$id = (int)$_GET["them"];
$_SESSION["gio"][$id] = ($_SESSION["gio"][$id] ?? 0) + 1;
}
if (isset($_GET["xoa"])) unset($_SESSION["gio"][(int)$_GET["xoa"]]);
echo "Giỏ có ", array_sum($_SESSION["gio"]), " sản phẩm";Trong php.ini đặt session.cookie_httponly = 1, session.use_strict_mode = 1; khi lên HTTPS thêm session.cookie_secure = 1.
Không lưu mật khẩu hay toàn bộ bản ghi người dùng trong session; chỉ lưu id và vài thông tin hiển thị. Gọi session_regenerate_id(true) ngay sau khi đăng nhập.
- Làm chức năng ghi nhớ giao diện sáng/tối bằng cookie.
- Xây dựng đăng nhập, đăng xuất, trang “Hồ sơ” chỉ xem được khi đăng nhập.
- Thêm thông báo flash sau khi đăng nhập, đăng xuất, lưu dữ liệu.
- Làm giỏ hàng session với thêm, giảm, xóa và tổng tiền.
- Cookie ở trình duyệt (không nhạy cảm), session ở máy chủ.
- setcookie/session_start gọi trước mọi output.
- Đăng nhập: password_verify + session_regenerate_id; đăng xuất: xóa session và cookie.
- auth.php bảo vệ trang; flash cho thông báo một lần.