Gần như mọi ứng dụng web đều cần trả lời câu hỏi: "Người đang gửi request này là ai?". Sau khi người dùng đăng nhập thành công, server phải có cách nhận diện họ ở các request tiếp theo, vì HTTP vốn không lưu trạng thái. Hai cách tiếp cận phổ biến nhất là session phía server và JWT (JSON Web Token). Trên các diễn đàn, cuộc tranh luận "JWT hay session" chưa bao giờ hạ nhiệt, và không ít đội ngũ chọn JWT chỉ vì nó "hiện đại" hơn. Bài viết này giúp bạn hiểu bản chất từng cách để chọn đúng cho dự án của mình.

Xác thực bằng session hoạt động thế nào?

  1. Người dùng gửi tên đăng nhập và mật khẩu.
  2. Server kiểm tra, tạo một bản ghi phiên (session) lưu ở bộ nhớ, database hoặc Redis, kèm một session ID ngẫu nhiên, khó đoán.
  3. Server gửi session ID về trình duyệt trong cookie.
  4. Ở mỗi request sau, trình duyệt tự động gửi cookie; server tra cứu session ID để biết người dùng là ai.

Điểm mấu chốt: trạng thái nằm ở server. Cookie chỉ là "chiếc chìa khoá" không mang thông tin gì.

res.cookie('sid', sessionId, {
  httpOnly: true,   // JavaScript không đọc được
  secure: true,     // chỉ gửi qua HTTPS
  sameSite: 'lax',  // giảm rủi ro CSRF
  maxAge: 30 * 60 * 1000
});

JWT hoạt động thế nào?

JWT là một chuỗi gồm ba phần ngăn cách bởi dấu chấm: header, payload và signature, mỗi phần được mã hoá base64url. Payload chứa các claim như mã người dùng (sub), thời điểm hết hạn (exp), vai trò...

  1. Người dùng đăng nhập, server tạo JWT chứa thông tin cần thiết rồi ký bằng khoá bí mật.
  2. Client lưu token và gửi kèm trong mỗi request, thường qua header Authorization: Bearer <token>.
  3. Server chỉ cần xác minh chữ ký và thời hạn, không phải tra cứu cơ sở dữ liệu.

Lưu ý quan trọng: JWT thông thường chỉ được ký, không được mã hoá. Bất kỳ ai có token đều có thể giải mã base64 và đọc payload, nên tuyệt đối không đặt mật khẩu hay dữ liệu nhạy cảm vào đó.

So sánh hai cách tiếp cận

Thu hồi quyền truy cập

Đây là khác biệt lớn nhất. Với session, muốn đăng xuất người dùng hay khoá tài khoản, bạn chỉ cần xoá bản ghi phiên. Với JWT, token vẫn hợp lệ cho đến khi hết hạn vì server không lưu gì. Muốn thu hồi sớm, bạn phải duy trì danh sách chặn (denylist), tức quay lại việc lưu trạng thái.

Khả năng mở rộng

JWT thường được quảng bá là "stateless nên dễ mở rộng". Điều này đúng ở chỗ nhiều service có thể tự xác minh token mà không gọi về một kho chung. Tuy nhiên, với phần lớn ứng dụng, một kho session dùng chung như Redis cũng đáp ứng tốt việc mở rộng nhiều server.

Kích thước và hiệu năng

Session ID rất ngắn. JWT lớn hơn đáng kể và được gửi trong mọi request. Đổi lại, JWT giúp tránh một lần tra cứu kho session mỗi request.

Bảo mật phía client

  • Session lưu trong cookie HttpOnly nên JavaScript không đọc được, giảm thiệt hại khi bị XSS. Cần phòng chống CSRF bằng SameSite và CSRF token khi cần.
  • JWT nếu lưu trong localStorage sẽ bị đánh cắp dễ dàng khi trang có lỗ hổng XSS. Bạn hoàn toàn có thể đặt JWT trong cookie HttpOnly, nhưng khi đó nó hoạt động gần giống session cookie.

Độ phức tạp khi triển khai

JWT đòi hỏi bạn tự xử lý nhiều chi tiết: chọn thuật toán ký, kiểm tra exp, iss, aud, xoay vòng khoá, cơ chế refresh token. Các thư viện session của framework thường đã lo sẵn phần lớn việc này.

Khi nào nên chọn session?

  • Ứng dụng web truyền thống hoặc SPA chạy cùng tên miền với backend.
  • Cần đăng xuất tức thì, khoá tài khoản ngay khi phát hiện bất thường.
  • Đội ngũ muốn giải pháp đơn giản, ít rủi ro cấu hình sai.

Khi nào JWT phát huy lợi thế?

  • Hệ thống nhiều service cần xác minh danh tính mà không gọi về một dịch vụ trung tâm cho mỗi request.
  • Ứng dụng di động hoặc API cho bên thứ ba, nơi cookie không phải lựa chọn tự nhiên.
  • Tích hợp với các chuẩn như OAuth 2.0 và OpenID Connect, nơi JWT được dùng làm access token hoặc ID token.

Nếu dùng JWT, hãy làm đúng cách

  • Đặt thời hạn ngắn cho access token (vài phút), kết hợp refresh token có thể thu hồi được và lưu an toàn.
  • Luôn chỉ định rõ thuật toán được chấp nhận khi xác minh; không bao giờ chấp nhận alg: none.
  • Kiểm tra đầy đủ exp, iss, aud.
  • Dùng khoá bí mật đủ mạnh, hoặc cặp khoá bất đối xứng (RS256, ES256) khi nhiều service cần xác minh.
  • Ưu tiên lưu token trong cookie HttpOnly, Secure thay vì localStorage với ứng dụng trình duyệt.

Kết luận

Không có lựa chọn nào tốt hơn tuyệt đối. Session đơn giản, dễ thu hồi và an toàn mặc định, rất phù hợp với phần lớn ứng dụng web. JWT mạnh khi bạn cần xác minh phân tán giữa nhiều service hoặc làm việc với hệ sinh thái OAuth/OpenID Connect, nhưng đi kèm nhiều trách nhiệm hơn. Hãy chọn dựa trên kiến trúc và yêu cầu thực tế, chứ không phải vì xu hướng. Và dù chọn cách nào, HTTPS, cookie an toàn cùng phòng chống XSS vẫn là nền tảng bắt buộc.

Nguồn tham khảo