HTTP là giao thức không trạng thái (stateless): mỗi request là một cuộc gặp độc lập, server không tự nhớ request trước là của ai. Vậy làm sao một website biết bạn đã đăng nhập, giữ giỏ hàng của bạn qua nhiều trang? Câu trả lời là cookie — cơ chế đơn giản nhưng là nơi tập trung nhiều lỗ hổng bảo mật nhất nếu dùng sai. Bài này đo thật luồng session bằng curl, rồi mổ ba thuộc tính cookie quyết định an toàn: HttpOnly, Secure, SameSite.
Cookie vá tính không-trạng-thái thế nào
Luồng cơ bản gồm hai chiều:
- Server → client: gửi header
Set-Cookie: session=abc123; ...trong response (thường sau khi đăng nhập). - Client → server: từ đó về sau, trình duyệt tự động đính
Cookie: session=abc123vào mọi request tới cùng site — không cần code gì thêm.
session=abc123 thường chỉ là một session id ngẫu nhiên; dữ liệu phiên thật (bạn là ai, quyền gì) lưu ở phía server, id chỉ là chìa khóa tra cứu. Nhờ vậy request sau server biết đây là ai.
Set-Cookie: session=abc123; Max-Age=3600; Path=/; HttpOnly; SameSite=Lax
Cookie: session=abc123 # client tự gửi lại

Hình 1: Server gửi Set-Cookie, client tự gửi lại Cookie ở mọi request sau — vá tính không-trạng-thái. Ba thuộc tính HttpOnly/Secure/SameSite quyết định an toàn.
Đo thật: luồng session
Dựng một server có /login (cấp session), /me (đọc cookie để nhận diện), /logout (xóa cookie). Dùng curl với cookie jar (-c lưu, -b gửi):

Hình 2: Chưa cookie thì /me trả 401. Sau /login, server gửi Set-Cookie: session=9a9cd579ea41 kèm HttpOnly; SameSite=Lax; curl lưu vào jar (đánh dấu #HttpOnly_). Gửi lại cookie thì /me nhận diện an_nguyen. /logout gửi Max-Age=0 để xóa.
Kết quả xác nhận đúng luồng: request /me không cookie → 401. Sau /login, server trả Set-Cookie: session=9a9cd579ea41; Max-Age=3600; Path=/; HttpOnly; SameSite=Lax, curl lưu vào jar (nó ghi cả cờ #HttpOnly_). Gửi lại cookie đó, /me trả Xin chao an_nguyen — server tra session id và biết đây là ai. /logout gửi Set-Cookie: session=; Max-Age=0 — Max-Age=0 bảo trình duyệt xóa cookie ngay.
Ba thuộc tính bảo mật sống còn
Cookie chứa session id là chìa khóa danh tính, nên trộm được cookie là chiếm được phiên. Ba thuộc tính bảo vệ nó:
HttpOnly: JavaScript không đọc được cookie (document.cookiekhông thấy nó). Đây là phòng tuyến chống XSS: kể cả kẻ tấn công chèn được script vào trang, nó cũng không đọc và gửi cookie session đi được. Cookie phiên gần như luôn nên cóHttpOnly.Secure: cookie chỉ được gửi qua HTTPS. Không có nó, một request HTTP (hoặc bị hạ cấp) sẽ để lộ cookie trên đường truyền cho ai nghe lén.SameSite: kiểm soát cookie có được gửi kèm khi request đến từ site khác hay không.Strict(không gửi với mọi request chéo site),Lax(gửi với điều hướng cấp cao như click link, mặc định của trình duyệt hiện đại),None(gửi mọi lúc — bắt buộc kèmSecure). Đây là phòng tuyến chống CSRF: kẻ tấn công ở site khác không kích được request "thay bạn" mang theo cookie.
Đánh đổi và lưu ý
Session ở server vs JWT không trạng thái. Mẫu trên (session id + dữ liệu ở server) dễ thu hồi (xóa session ở server là đăng xuất mọi nơi) nhưng cần lưu trữ phiên (bộ nhớ/Redis/DB). Lựa chọn khác là token tự chứa (JWT) đặt trong cookie: không cần tra server nhưng khó thu hồi trước hạn. Chọn theo nhu cầu thu hồi và quy mô.
Kích thước và số lượng cookie. Cookie đi kèm mọi request tới site, nên cookie to = tốn băng thông mỗi lần. Giới hạn thực tế ~4KB mỗi cookie. Đừng nhét dữ liệu lớn vào cookie; để id, dữ liệu để server.
SameSite=Lax là mặc định nhưng đừng ỷ lại. Trình duyệt hiện đại mặc định Lax khi không khai, nhưng khai tường minh vẫn tốt hơn (rõ ý, tránh khác biệt giữa trình duyệt). Và SameSite không thay thế được token CSRF cho các luồng nhạy cảm — nó là một lớp, không phải tất cả.
Ba ý mang về
- Cookie vá tính không-trạng-thái của HTTP: server gửi
Set-Cookie, client tự đínhCookieở mọi request sau — đo thật,/mekhông cookie trả 401, sau/logincó session thì nhận diện được user; session id là chìa khóa trỏ tới dữ liệu phiên ở server. - Ba thuộc tính bảo mật là bắt buộc với cookie phiên:
HttpOnly(JS không đọc được → chống XSS trộm cookie),Secure(chỉ HTTPS),SameSite(chống CSRF request chéo site). - Biết đánh đổi: session-ở-server dễ thu hồi nhưng cần lưu trữ, JWT không trạng thái thì ngược lại; cookie đi kèm mọi request nên giữ nhỏ (id thôi);
SameSite=Laxlà mặc định nhưng vẫn nên khai rõ và không thay hẳn token CSRF.
Nguồn
- MDN — Using HTTP cookies (Set-Cookie, HttpOnly, Secure, SameSite): https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
- RFC 6265 (HTTP State Management Mechanism) và bản cập nhật SameSite: https://www.rfc-editor.org/rfc/rfc6265
Phần sau ta quay lại tối ưu băng thông: nén nội dung — Accept-Encoding, Content-Encoding, so gzip với brotli và đo tỉ lệ nén thật trên một trang HTML.