Cách giải mã và gỡ lỗi JWT
JSON Web Token thực chất chỉ là ba phần được mã hóa Base64URL. Khi đã đọc được chúng, phần lớn lỗi “invalid token” sẽ tự giải thích trong vài giây. Hướng dẫn này chỉ cách giải mã JWT, kiểm tra các claims hay gây lỗi nhất, và những sai lầm biến JWT thành lỗ hổng bảo mật.
Cấu trúc của JWT
JWT có dạng xxxxx.yyyyy.zzzzz. Ba phần, ngăn cách bằng dấu chấm, gồm:
- Header — JSON chứa thuật toán ký (
alg) và loại token, ví dụ{"alg":"HS256","typ":"JWT"}. - Payload — JSON chứa các claims: token này dành cho ai và có hiệu lực khi nào.
- Signature — giá trị băm của hai phần đầu, được tạo bằng khóa bí mật (HMAC) hoặc khóa riêng (RSA, ECDSA).
Hai phần đầu chỉ được mã hóa, không được mã hóa bảo mật. Ai có token cũng đọc được, vì vậy đừng bao giờ đặt mật khẩu hay dữ liệu cá nhân vào payload của JWT.
Giải mã một token
Dán token vào JWT Decoder, công cụ sẽ hiển thị header, payload và mọi mốc thời gian dưới dạng ngày tháng dễ đọc. Công cụ chạy trực tiếp trên trình duyệt của bạn nên token thật không bị gửi đi đâu. Nếu muốn làm thủ công, hãy giải mã Base64URL từng phần — thay - bằng +, _ bằng /, rồi thêm padding — hoặc dùng Node:
const [header, payload] = token.split('.').slice(0, 2)
.map(part => JSON.parse(Buffer.from(part, 'base64url').toString()));
console.log(header, payload);
Các claims hay gây lỗi nhất
| Claim | Ý nghĩa | Lỗi thường gặp |
|---|---|---|
exp | Thời điểm hết hạn, tính bằng giây Unix | “jwt expired” — hãy kiểm tra cả đồng hồ của server và client |
nbf | Chưa có hiệu lực trước thời điểm này | Token bị dùng sớm vài giây vì đồng hồ của bên phát hành chạy nhanh |
iat | Thời điểm phát hành | Bị từ chối với lỗi “issued in the future” sau khi đồng hồ bị lệch |
aud | Đối tượng nhận mà token hướng đến | Token của API này bị gửi sang API khác — bên xác thực phải kiểm tra trường này |
iss | Bên phát hành (issuer) | Nhầm lẫn issuer của môi trường dev và production |
Mốc thời gian tính bằng giây, không phải mili giây. Nếu thời hạn hết hạn hiển thị là năm 55.000, nghĩa là ai đó đã viết Date.now() thay vì Math.floor(Date.now() / 1000). Unix Timestamp Converter sẽ giúp bạn kiểm tra nhanh. Hầu hết thư viện đều cho phép một clock tolerance nhỏ (30–60 giây) để bù độ lệch giữa các server.
Vì sao báo “invalid signature”?
- Sai khóa bí mật hoặc khóa công khai — nguyên nhân phổ biến nhất. Hãy kiểm tra biến môi trường trên server xác thực, không chỉ trên server ký.
- Sai thuật toán — token ký bằng RS256 nhưng được xác thực bằng HS256, hoặc ngược lại.
- Token đã bị chỉnh sửa — thay đổi chỉ một ký tự trong payload là đủ làm hỏng chữ ký, đó chính là mục đích của chữ ký.
- Xoay vòng khóa (key rotation) — khóa của bên phát hành đã thay đổi. Với JWKS, hãy đối chiếu
kidtrong header với khóa đã công bố.
Để kiểm tra token HS256 thủ công, hãy tính HMAC-SHA256 của header.payload với khóa bí mật bằng HMAC Generator, rồi so sánh kết quả đã mã hóa Base64URL với phần thứ ba.
Các lỗi bảo mật cần tránh
- Giải mã không đồng nghĩa với xác thực. Đừng bao giờ tin claims từ
jwt.decode(); hãy dùng hàm verify của thư viện với danh sách thuật toán được phép được khai báo rõ ràng. - Từ chối
alg: nonevà cố định thuật toán — đừng bao giờ để token quyết định cách nó được kiểm tra. - Giữ token có thời hạn ngắn (vài phút với access token) và dùng refresh token cho phiên đăng nhập dài; JWT không thể bị thu hồi trước khi hết hạn nếu không có thêm trạng thái trên server.
- Không lưu token trong localStorage khi có lỗ hổng XSS có thể đọc được chúng; với ứng dụng web, cookie HttpOnly, Secure, SameSite an toàn hơn.
Cần một token thử nghiệm với claims cụ thể? Hãy tạo bằng JWT Encoder.