Nếu coi việc xác thực (Authentication) là bốt bảo vệ hầm gửi xe, thì JWT (JSON Web Token) chính là chiếc vé vào cửa mà anh em Developer chúng ta phát cho khán giả (User).
Chúng ta dùng nó hàng ngày, gửi nó đi muôn nơi trong mỗi request Authorization: Bearer <token>. Nhưng có bao giờ bạn thực sự dừng lại để “soi” xem bên trong chuỗi ký tự dài ngoằng đó chứa đựng những gì không? Hay chúng ta chỉ copy-paste code và tin rằng “nó an toàn”?
Hôm nay, hãy cùng tôi “mổ xẻ” hai phần đầu tiên của một JWT: Header và Payload, để thấy rằng đôi khi sự nguy hiểm nằm ngay ở những nơi ta chủ quan nhất.
1. Header (Cái nhãn mác)
Hãy tưởng tượng JWT là một phong bì thư. Thì Header chính là những thông tin được ghi bên ngoài vỏ phong bì để bưu điện (Server) biết cách xử lý nó.
Đây là phần mở đầu, thường rất ngắn gọn và chứa metadata (siêu dữ liệu). Nó bao gồm 2 thông tin cốt lõi:
typ: Khẳng định “Tôi là ai?”. Ở đây luôn là"JWT".alg: Khẳng định “Tôi được khóa bằng cách nào?”. Đây là thuật toán dùng để tạo chữ ký (Signature) ở phần đuôi (sẽ bàn ở bài sau). Thường làHS256(dùng chung một chìa khóa bí mật) hoặcRS256(dùng cặp khóa công khai/bí mật).
Ví dụ một cái Header trần trụi:
JSON
{
"alg": "HS256",
"typ": "JWT"
}
Đơn giản vậy thôi, không có gì bí hiểm cả.
2. Payload (Phần ruột gan – Nơi chứa dữ liệu)
Nếu Header là vỏ phong bì, thì Payload chính là lá thư bên trong. Đây là nơi chứa những thông tin (chúng ta gọi là Claims) mà bạn muốn truyền tải về user.
Trong thế giới JWT, có 3 loại “tin tức” bạn có thể viết vào lá thư này:
- Registered Claims (Hàng chính hãng): Các thông tin tiêu chuẩn được khuyên dùng (nhưng không bắt buộc). Ví dụ:
iss(Issuer): Ai phát hành vé này?exp(Expiration time): Vé này bao giờ hết hạn? (Cực quan trọng để tránh token sống vĩnh viễn).sub(Subject): Token này đại diện cho ai?
- Public Claims: Những thông tin được định nghĩa rộng rãi trong cộng đồng nhưng không nằm trong chuẩn cốt lõi.
- Private Claims (Hàng nội bộ): Đây là chỗ anh em mình hay dùng nhất. Những thông tin riêng tư mà Client và Server tự quy ước với nhau. Ví dụ:
userId,role,username…
Một chiếc Payload điển hình sẽ trông thế này:
JSON
{
"sub": "1234567890", // ID của user
"name": "Nguyen Van A", // Tên user
"role": "admin", // Quyền hạn (Private claim)
"iat": 1516239022 // Thời điểm tạo token (Issued At)
}
3. Cảnh báo đỏ: Sự nhầm lẫn chết người! 🚨
Đây là phần quan trọng nhất bài viết này. Hãy đọc kỹ.
Khi bạn nhìn thấy một chuỗi JWT (eyJhbGciOiJIUzI1NiIsIn...), nó trông rất “nguy hiểm”, rất “mật mã”. Nhưng thực tế, cả Header và Payload chỉ được MÃ HÓA (Encoded) bằng Base64Url, chứ KHÔNG HỀ ĐƯỢC MÃ HÓA BẢO MẬT (Encrypted).
Sự khác biệt là gì?
- Encrypted: Cần chìa khóa mới mở được xem nội dung.
- Encoded: Giống như dịch tiếng Việt sang tiếng Anh. Bất kỳ ai, nhắc lại là BẤT KỲ AI, chỉ cần copy chuỗi token của bạn và ném vào trang
jwt.io(hoặc dùng hàm decode base64 đơn giản), họ sẽ nhìn thấy toàn bộ nội dung Header và Payload bên trong rõ mồn một.
Quy tắc sống còn: Tuyệt đối không bao giờ để lộ thông tin nhạy cảm trong Payload.
Đừng bao giờ, dù chỉ là lỡ tay, đưa password, số thẻ tín dụng, hay thông tin cá nhân tuyệt mật vào Payload. Làm vậy chẳng khác nào bạn đi gửi thư, viết hết bí mật lên đó rồi bỏ vào một cái phong bì trong suốt và gửi đi giữa bàn dân thiên hạ.
Tạm kết
Vậy là chúng ta đã bóc tách xong phần “thân xác” của JWT. Chúng ta biết nó chứa gì và biết nó “hớ hênh” như thế nào trước mắt thiên hạ.
Nhưng khoan đã, nếu ai cũng đọc được nội dung, thậm chí sửa đổi nội dung (ví dụ sửa role: user thành role: admin), thì cái Token này có tác dụng quái gì? Làm sao Server biết được Token này có bị làm giả hay không?
Câu trả lời nằm ở mảnh ghép cuối cùng và quyền lực nhất: Signature (Chữ ký). Hẹn gặp lại anh em ở bài viết sau, chúng ta sẽ bàn về “cái dấu mộc” thần thánh này.