JWT đã và đang là một thành phần vô cùng quan trọng trong việc bảo mật API. Tuy nhiên không phải ai cũng hiểu rõ JWT là gì. Trong bài viết này, hãy cùng Vietnix tìm hiểu những kiến thức cơ bản và lý do nên sử dụng JWT.
JWT là gì?
JWT (JSON Web Token) là một tiêu chuẩn mở được sử dụng để chia sẻ thông tin bảo mật giữa client và server. Mỗi JWT đều chứa các đối tượng JSON được mã hoá và một bộ quyền sở hữu (claim). JWT được ký bằng thuật toán cryptographic để đảm bảo các quyền sở hữu không thể bị thay đổi sau khi phát hành token.
Ý nghĩa của từng thành phần trong tên JWT
Để hiểu rõ hơn JWT là gì, hãy cùng phân tích một chút về các thành phần ở trong cái tên này: JSON và Token.
JSON là gì?
JSON (JavaScript Object Notation) là một định dạng dựa trên văn bản được sử dụng để truyền dữ liệu qua ứng dụng web. Cơ chế lưu trữ của JSON cho phép máy tính và cả các developer truy cập thông tin dễ dàng hơn. Bên cạnh đó, JSON còn có thể được dùng làm định dạng dữ liệu bởi bất kỳ ngôn ngữ lập trình nào.
Kể từ khi ra mắt vào những năm đầu tiên của thập niên 2000, JSON đã nhanh chóng phát triển rộng rãi, vượt qua cả XML để trở thành cú pháp phổ biến nhất cho các API.
Token là gì?
Nói một cách đơn giản thì token là một chuỗi dữ liệu mang tính đại diện, chẳng hạn như dùng để biểu diễn danh tính cho thực thể nào đó. Trong quá trình xác thực, một token (không dựa trên JWT) là một chuỗi các ký tự cho phép người nhận xác thực danh tính của người gửi.
Mặt khác, JWT bao gồm thêm một bộ claim, dùng để truyền thông tin giữa client và server. Các claim này có thể cho biết người phát hành token, thời gian có hiệu lực, các quyền mà token được cấp,… nói chung sẽ phụ thuộc chủ yếu vào mục đích sử dụng.
Cấu trúc của JSON Web Token
JWT là một chuỗi được tạo bởi ba thành phần, ngăn cách nhau bởi dấu chấm (.) và được tuần tự hoá (serialize) bằng base64. Nếu xét định dạng tuần tự đơn giản nhất thì JWT sẽ có dạng: xxxxx.yyyyy.zzzzz.
Sau khi giải mã thì ta sẽ nhận được hai chuỗi JSON:
- Một chuỗi gồm header và payload.
- Chuỗi còn lại là signature.
Trong phần này, hãy cùng tìm hiểu rõ hơn ba thành phần chính của JSON Web Token:
Để minh hoạ cho cấu trúc, giả sử ta có một JWT như sau:
eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9lIENvZGVyIn0.5dlp7GmziL2QS06sZgK4mtaqv0_xX4oFUuTDh1zHK4U
Dựa trên cách phân chia ở trên, ta có thể thấy được ba thành phần như sau:
eyJhbGciOiJIUzI1NiJ9 # header
.
eyJuYW1lIjoiSm9lIENvZGVyIn0 # payload
.
5dlp7GmziL2QS06sZgK4mtaqv0_xX4oFUuTDh1zHK4U #signature
Header
Có tên đầy đủ là JOSE (JSON Object Signing and Encryption) header, thành phần này có nhiệm vụ chứa kiểu token (JWT) và thuật toán của chữ ký. Trong đó một số thuật toán phổ biến gồm có HMAC, SHA256 hay RSA.
Mã hoá theo base64-url ví dụ ở trên:
{"alg":"HS256"} # header
Payload
Payload là thành phần chứa các claim trong JWT, được hiển thị dưới dạng một chuỗi JSON và thường chỉ chứa một số lượng trường vừa đủ để giữ cho JWT nhỏ gọn (thường là dưới 10). Thông tin này chủ yếu được server sử dụng để xác minh xem người dùng có quyền thực hiện hành động đã yêu cầu hay không.
Claim trong JWT là một cặp key/value trong một đối tượng JSON. Giá trị của một claim có thể là bất kỳ đối tượng JSON nào. Có ba loại claim chủ yếu, gồm: registered, public và private.
Registered claim không bắt buộc phải được sử dụng, nhưng nếu có thì phải được xác nhận trước. Lấy ví dụ, một JWT có thể chứa các trường date-time biểu diễn thời hạn hợp lệ của token.
- Issued At (iat): Thời gian tạo JWT
- Expiration Time (exp): Thời gian JWT hết hạn
- Not Before (nbf): Thời gian sớm nhất mà JWT bắt đầu có hiệu lực
Ngoài ra, public claim là những claim được cộng đồng công nhận và sử dụng rộng rãi. Còn private claim là các claim tự định nghĩa (không được trùng với registered claim và public claim), được tạo ra để chia sẻ thông tin giữa các bên client-server.
Mã hoá theo base64-url ví dụ ở trên:
{"name":"Joe Coder"} # payload
Trong đó, "name: "Joe Coder" là một claim với key là name, còn value là Joe Coder.
Signature
Signature (chữ ký) là một chuỗi được mã hoá bởi header, payload cùng một chuỗi bí mật, có nhiệm vụ đảm bảo tính toàn vẹn của token trong quá trình truyền tải. Bên tạo JWT sẽ ký header và payload với chuỗi bí mật (cả người phát hành và người nhận token đều biết) hoặc bằng một private key mà chỉ người gửi biết. Khi token được sử dụng, phía nhận token sẽ xác thực xem header và payload có khớp với signature hay không.
Tương tự, ta có kết quả mã hoá cho ví dụ trên:
5dlp7GmziL2QS06sZgK4mtaqv0_xX4oFUuTDh1zHK4U #signature
Lưu ý rằng signature có dữ liệu dạng nhị phân nên nếu giải mã thì kết quả trả về sẽ hiển thị một chuỗi các ký tự như sau:
��i�i��KN�f�֪�O�_R��\�+
Khi đó bạn có thể dùng các công cụ như hexdump để xem nội dung của signature:
$ echo "5dlp7GmziL2QS06sZgK4mtaqv0_xX4oFUuTDh1zHK4U=" | gbasenc -d --base64url | hexdump
0000000 e5 d9 69 ec 69 b3 88 bd 90 4b 4e ac 66 02 b8 9a
0000010 d6 aa bf 4f f1 5f 8a 05 52 e4 c3 87 5c c7 2b 85
Những lý do nên sử dụng JWT
Nói chung, JSON Web Token chủ yếu được sử dụng như một phương thức an toàn để xác thực người dùng và truyền dẫn thông tin. Thường thì một private key sẽ được người phát hành sử dụng để ký JWT. Sau đó người nhận xác thực chữ ký để đảm bảo tính toàn vẹn của token. Việc “đoán” ra khoá và thay đổi claim bên trong JWT gần như là không thể, vì vậy JSON Web Token giúp đảm bảo tính bảo mật của quá trình truyền dẫn thông tin.
Mặt khác, không phải thuật toán ký nào cũng như nhau. Chẳng hạn có vài thuật toán sử dụng một giá trị bí mật mà chỉ người phát hành và bên xác thực mới biết. Một số thuật toán khác lại dùng cặp public và private key. Trong đó private key chỉ có người phát hành biết, còn public key sẽ được phân bố đến nhiều bên. Tất nhiên chỉ có private key mới có thể tạo được chữ ký, còn việc xác thực sẽ được thực hiện bởi public key. Thuật toán này được xem là an toàn hơn vì private key chỉ tồn tại ở một nơi duy nhất.
Ngoài ra, đặc điểm này giúp server không cần phải lưu trữ một cơ sở dữ liệu thông tin cho mục đích xác thực người dùng. Đồng thời server phát hành JWT và server xác thực cũng không cần phải giống nhau – một ưu điểm cực kỳ tiện lợi cho các developer!
Khi nào nên sử dụng JWT?
Trong phần cuối cùng của bài viết, hãy cùng tìm hiểu những trường hợp phổ biến nhất mà ta nên sử dụng JWT:
Xác thực
Quá trình xác thực (authentication) diễn ra sau khi client chứng minh được danh tính của mình thông qua một login endpoint. Nếu thành công thì server sẽ tạo một JSON Web Token rồi phản hồi lại client. Sau đó, client sẽ sử dụng JWT này trên mọi request với những tài nguyên được bảo vệ.
Uỷ quyền
Một server sử dụng JSON Web Token cho mục đích uỷ quyền (authorization) sẽ tạo một JWT khi client đăng nhập. Đồng thời, JWT này cũng đã được ký nên các bên khác sẽ không có quyền thay đổi nó.
Mỗi khi client có quyền truy cập vào tài nguyên được bảo vệ, server sẽ xác thực xem chữ ký JWT có khớp với payload và header hay không để kiểm tra tính hợp lệ của JWT. Sau khi xác minh thành công, token này có thể gán (hoặc từ chối) quyền truy cập vào tài nguyên.
Trao đổi dữ liệu
JWT cũng là một giải pháp trao đổi dữ liệu vô cùng an toàn. Bên cạnh đó, người dùng còn có thể xác thực tính hợp lệ của token (gồm chữ ký, cấu trúc hay các tiêu chuẩn được yêu cầu trong JWT).
Câu hỏi thường gặp
JWT là gì?
JWT là viết tắt từ JSON Web Token. Đây là một phương tiện đại diện cho các bên yêu cầu chuyển giao giữa server và client.
Thư viện để xác thực cho JWT ở đâu?
Nếu bạn đang tìm danh sách các thư viện cho JWT nhằm phục vụ cho công việc của mình thì có thể truy cập vào Libraries for Token Signing/Verification để có thể tìm được thư viện phù hợp: .NET, Python, Node.js, Java, JavaScript, Perl, Ruby,.. cho đến Go, Erlang, Haskell,…
Lời kết
Qua bài viết này, hy vọng bạn đọc đã trang bị được những kiến thức cơ bản về khái niệm, cấu trúc và cách thực hoạt động của JWT để ứng dụng vào việc bảo mật API. Chúc các bạn thành công!