JWT là gì?
JWT (JSON Web Token) là một phương pháp mạnh mẽ dùng để truyền tải thông tin giữa các bên dưới dạng đối tượng JSON một cách an toàn. Thông tin trong JWT có thể được xác minh và tin cậy vì nó được ký điện tử, đảm bảo dữ liệu không bị thay đổi và chỉnh sửa trên đường đi. JWT có thể được ký bằng một secret (với thuật toán HMAC) hoặc một cặp public/private key khi sử dụng thuật toán RSA hoặc ECDSA. JWT chủ yếu được sử dụng để xác thực và ủy quyền trong các hệ thống web.
Ứng dụng JWT
Một số trường hợp sử dụng JWT:
- Authentication/Authorization: đây là trường hợp phổ biến nhất sử dụng JWT. Sau khi người dùng đăng nhập, mỗi yêu cầu tiếp theo sẽ bao gồm JWT cho phép người dùng truy cập đến các endpoint, services và resources(tài nguyên) mà JWT có quyền. Single Sign On là mộ tính năng sử dụng JWT rất rộng rãi do hiện nay chi phí hoạt động thấp và khả năng sử dụng dễ dàng trên các domains khác nhau.
- Information Exchange(trao đổi thông tin): JWT là một cách tốt để truyền thông tin một các an toàn giữa các bên vì JWT được signed(ký - sử dụng các cặp private/public keys) đảm bảo rằng người gửi gửi đúng người nhận và các thông tin không bị thay đổi hay chỉnh sửa hay giả mạo.
JWT được sử dụng phổ biến trong các tiêu chuẩn như Oauth2 và OpenID Connnect.
Cấu trúc JWT
Trước khi đi vào cấu trúc của JWT (JSON Web Token), mình sẽ nói một chút về JSON. JSON là một định dạng trao đổi dữ liệu nhẹ, dễ đọc và viết đối với con người và đơn giản đối với máy móc để phân tích và khởi tạo. Nó là xương sống của JWT vì JSON là nơi lưu trữ dữ liệu (payload) muốn truyền đi của JWT.
JWT có cấu trúc gồm 3 phần: Header, Payload, Signature. Mỗi phần đều sẽ được mã hóa base64 và cách nhau bằng dấu chấm (.
). Do đó JWT thường có dạng như sau: xxxxx.yyyyy.zzzzz
.
Header
Header chứa thông tin bao gồm 2 phần: token type là JWT và thuật toán ký(signed) đang được sử dụng để mã hóa như HMAC SHA256 hoặc RSA.
Ví dụ Header của JWT:
{ "alg": "HS256", "typ": "JWT"
}
Payload
Payload của JWT là nơi bạn lưu trữ các claims. Claims là một statement về một thực thể (thông thường là user) và các dữ liệu additional. Có 3 loại claims đó là: registered, public và private
- Registered claim: là một tập hợp các claims được xác định trước, không bắt buộc nhưng được khuyến khích để cung cấp một tập hợp các claims có ích như: iss (issuers), exp (expiration time), sub (subject), aud (audience), ...
- Public claim: có thể được xác định theo ý muốn của người sử dụng. Nhưng để tránh xung đột, nó phải được xác định trong IANA JSON Web Token Registry hoặc được xác định với URI chưa namespace để chống xung đột.
- Private claim: là các claims tùy chỉnh theo ý muốn của người sử dụng để chia sẻ thông tin giữa các bên đồng ý sử dụng chúng và không phải là các claim registered hoặc public.
Ví dụ Payload của JWT:
{ "sub": "1234567890", "name": "Simon", "admin": true
}
Trong khi JWT payload được mã hóa bằng JSON Web Encryption (JWE), hầu hết khi triển khai JWT đều được signed để chống thay đổi thông tin/giả mạo nhưng nó không được mã hóa nên bất kỳ ai cũng có thể đọc được dữ liệu này nên không nên lưu thông tin nhạy cảm trong JWT trừ khi nó đã được mã hóa từ trước.
Signature
Việc signed(ký) trong JWT giống như ta niêm phong một phong bì bằng tem sáp để đảm bảo rằng nó không bị thay đổi hay giả mạo. Để tạo phần chữ ký (signed), trước tiên phải có các thông tin sau: encoded header và encoded payload, secret và một thuật toán muốn sử dụng. Có hai loại thuật toán signed(ký) chính:
- Summetric algorithms (thuật toán tổng hợp) như HMAC SHA256 sử dụng khóa bí mật dùng chung cho cả việc signed và xác minh nó.
- Asymmetric algorithms (thuật toán bất đối xứng) như RSA, sử dụng cặp kháo public/private key trong đó private key dùng để signed và public key dùng để verify nó.
Trong khi lựa chọn thuật toán để triển khai, hãy xem xét nhu cầu của bản thân để lựa chọn thuật toán phù hợp. Symmetric keys(khóa đối xứng) được thực hiện nhanh và đơn giản nhưng secret key (khóa bí mật) phải được chia sẻ giữa các bên. Asymmetric keys (khóa bất đối xứng) cho phép xác minh người tạo mà không cần chia sẻ private key nhưng lại chậm hơn.
Ví dụ khi muốn sử dụng thuật toán HMAC SHA256, signature sẽ được tạo như sau:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Signature được sử dụng để xác minh dữ liệu truyền đi không bị thay đổi trong quá trình truyền đi và trong trường hợp sử dụng private key, nó có thể xác định rằng người gửi JWT chính là người được xác nhận.
Khi kết hợp 3 phần header, payload, signature ta sẽ có một JWT tương tự như sau:
JWT hoạt động như thế nào?
Dưới đây là luồng hoạt động đơn giản của JWT:
Khi client gửi thông tin credentials để đăng nhập.Server tiến hành xác thực credentials. Sau khi xác thực xong sẽ tiến hành khởi tạo JWT đã được signed với thông tin chi tiết của người dùng và gửi lại. Client nhận token sẽ tiến hành lưu trữ token để sử dụng cho các requets tiếp theo. Sau khi đã có token, client sẽ thêm header cho các request sau này Authorization: Bearer <token/JWT>
rồi gọi đến các resource đã được cấp quyền lên server. Server xác thực signature và trả về dữ liệu yêu cầu của client.
Một vài lưu ý khi sử dụng JWT
Dưới đây là một vài lưu ý khi sử dụng JWT:
- Mặc định payload trong JWT không được mã hóa nên nó không nên chứa những dữ liệu nhạy cảm mà không được mã hóa từ trước.
- Nếu sử dụng các thuật toán mã hóa yếu thì attacker hoàn toàn có thể sử dụng
brute force
để lấy được token signature. - Để giảm thiểu rủi ro khi sử dụng JWT, một vài best practice có thể thực hiện là: chỉ lưu trữ những thông tin/claims cần thiết của người dùng, giữ thời gian hết hạn của token ngắn nhất có thể, lưu trữ token một cách an toàn và sử dụng các thuật toán mã hóa mạnh
Tổng kết
Qua bài viết này mình đã giới thiệu JWT, mặc dù kiến thức đã khá cũ nhưng nếu các bạn đọc bài viết này giúp các bạn có thể ôn lại những kiến thức căn bản hoặc hiểu rõ hơn về JWT khi sử dụng. Hy vọng bài viết có thể giúp ích được các bạn phần nào. Hẹn gặp lại các bạn ở những bài viết khác và cảm ơn các bạn đã theo dõi đến hết bài viết ❤️.