JWT là gì? Cách hoạt động, ưu điểm và những cái bẫy cần tránh

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

JWT (JSON Web Token) xuất hiện trong hầu hết hướng dẫn về xác thực API hiện đại. Nó tiện, nhưng cũng bị dùng sai khá nhiều. Hiểu cấu trúc và giới hạn của JWT giúp bạn dùng nó đúng chỗ và tránh những lỗ hổng phổ biến.

Cấu trúc của một JWT

JWT là chuỗi gồm ba phần cách nhau bằng dấu chấm: header.payload.signature, mỗi phần mã hóa base64url.

  • Header: loại token và thuật toán ký, ví dụ {"alg":"HS256","typ":"JWT"}.
  • Payload: các "claim" như sub (chủ thể, thường là id người dùng), exp (thời điểm hết hạn), iat (thời điểm phát hành), cùng dữ liệu tùy chỉnh.
  • Signature: chữ ký tạo từ header, payload và khóa bí mật, đảm bảo nội dung không bị sửa.

Điểm hay bị hiểu lầm: payload chỉ được mã hóa base64, không được bảo mật. Bất kỳ ai có token đều đọc được nội dung. Vì vậy tuyệt đối không đặt mật khẩu hay thông tin nhạy cảm vào đó. Chữ ký chỉ đảm bảo toàn vẹn, không đảm bảo bí mật.

Luồng hoạt động

  1. Người dùng đăng nhập bằng email và mật khẩu.
  2. Server xác minh, tạo JWT chứa id người dùng và thời hạn, ký bằng khóa bí mật rồi trả về.
  3. Client gửi token trong header Authorization: Bearer <token> ở các request sau.
  4. Server kiểm tra chữ ký và thời hạn, không cần tra database phiên đăng nhập, rồi cho qua.

Vì server không cần lưu trạng thái phiên, JWT được gọi là xác thực phi trạng thái (stateless), thuận lợi khi hệ thống có nhiều server.

Ví dụ tạo và kiểm tra bằng Node.js

const jwt = require('jsonwebtoken');const SECRET = process.env.JWT_SECRET; // chuỗi dài, ngẫu nhiên// Tạo token sống 15 phútconst token = jwt.sign({ sub: user.id, role: user.role }, SECRET, {  algorithm: 'HS256',  expiresIn: '15m',});// Kiểm tra trong middlewarefunction auth(req, res, next) {  const raw = (req.get('Authorization') || '').replace('Bearer ', '');  try {    req.user = jwt.verify(raw, SECRET, { algorithms: ['HS256'] });    next();  } catch (e) {    res.status(401).json({ error: 'Token không hợp lệ hoặc đã hết hạn' });  }}

Lưu ý chỉ định rõ danh sách thuật toán được chấp nhận khi xác minh, để tránh tấn công đánh lừa thuật toán (ví dụ chấp nhận alg: none).

Access token và refresh token

Vấn đề lớn nhất của JWT: một khi đã phát hành, bạn khó thu hồi trước khi hết hạn vì server không lưu trạng thái. Cách làm phổ biến:

  • Access token sống ngắn (vài phút đến vài chục phút) dùng để gọi API.
  • Refresh token sống dài dùng để xin access token mới, được lưu phía server (hoặc băm) để có thể thu hồi khi cần, và nên xoay vòng sau mỗi lần dùng.

Nếu cần đăng xuất ngay hoặc khóa tài khoản tức thì, bạn có thể duy trì danh sách token bị thu hồi, nhưng như thế đã mất một phần lợi thế phi trạng thái.

Lưu token ở đâu trên trình duyệt?

Lưu ở localStorage dễ bị đánh cắp nếu trang dính lỗ hổng XSS. Cookie với cờ HttpOnly, Secure và SameSite an toàn hơn trước XSS nhưng cần cân nhắc bảo vệ CSRF. Không có lựa chọn hoàn hảo, quan trọng là hiểu đánh đổi và chống XSS thật tốt.

Các bẫy thường gặp

  • Dùng khóa bí mật yếu hoặc ghi cứng trong mã nguồn.
  • Đặt thời hạn quá dài hoặc không có thời hạn.
  • Nhét quá nhiều dữ liệu vào payload, làm token phình to.
  • Tin payload mà không xác minh chữ ký.
  • Dùng JWT cho phiên đăng nhập web đơn giản, nơi session cookie phía server thực ra đơn giản và an toàn hơn.

Khi nào nên và không nên dùng JWT?

JWT hợp với API được gọi bởi ứng dụng di động, SPA hoặc giữa các dịch vụ, nơi muốn xác thực không cần tra trạng thái. Với website truyền thống một server, session phía server thường đủ tốt, dễ thu hồi và ít rủi ro hơn. Đừng chọn JWT chỉ vì nó phổ biến.

Ví dụ payload thực tế và kiểm tra thủ công

Một payload gọn gàng thường chỉ chứa vài claim cần thiết, ví dụ id người dùng, vai trò, thời điểm phát hành và hết hạn. Hãy tránh kiểu nhét cả hồ sơ người dùng vào token; mỗi request đều mang theo token đó nên kích thước lớn gây lãng phí băng thông, và dữ liệu trong token sẽ cũ nếu hồ sơ thay đổi. Nếu cần thông tin chi tiết, hãy tra theo id khi xử lý request. Khi gỡ lỗi, bạn có thể giải mã phần payload bằng bất kỳ công cụ base64 nào để xem claim, nhưng chỉ làm với token của chính mình trong môi trường phát triển, và đừng dán token thật của hệ thống production lên các trang web bên thứ ba.

Kết luận

JWT là công cụ hữu ích khi hiểu đúng: ký để toàn vẹn chứ không để giấu, sống ngắn, kèm cơ chế làm mới và thu hồi hợp lý. Hãy tự tạo và giải mã một token trên trang công cụ debug JWT hoặc bằng code để thấy rõ ba phần của nó.

Nếu muốn luyện tập cùng bài tập thực tế, hãy ghé các khóa học tại codebem.com khi bạn sẵn sàng.

author_photo
Nguyễn Đình Cường

0 comment