Giới thiệu Json Web Token (JWT)

Authentication là phần không thể thiếu trong bất kỳ hệ thống nào. Phương pháp authentication đơn giản và hay được sử dụng trong các ứng dụng web đó là user gửi thông tin username và password lên server. Sau khi server chứng thực thành công sẽ tạo ra một chuỗi session_id và lưu vào session hay database ở phía server. Sau đó, gửi session_id này về client và được client lưu trên cookie. Nhưng với các ứng dụng trên mobile và các ứng dụng web SPA (Single Page Application) thì cần có cơ chế authentication tốt hơn khi mà chúng ta phải thiết kế các RESTful api (stateless) thì server không thể đảm nhiệm việc lưu trạng thái phiên làm việc của user. Một trong những phương pháp tốt để giải quyết vấn đề này là sử dụng JSON Web Token (JWT).

1. JSON Web Token (JWT) là gì?

JSON Web Token (JWT) là 1 tiêu chuẩn mở (RFC 7519), định nghĩa cách thức truyền tin an toàn giữa các ứng dụng bằng một đối tượng JSON. Dữ liệu truyền đi sẽ được mã hóa và chứng thực, có thể được giải mã để lấy lại thông tin và đánh dấu tin cậy nhờ vào “chữ ký” của nó. Phần chữ ký của JWT sẽ được mã hóa lại bằng HMAC hoặc RSA.

JWT có 2 đặc điểm:

  • Gọn nhẹ (compact): JWT có thể được truyền đi thông qua URL, hoặc qua giao thức POST, hay gán vào bên trong phần HTTP Header. Kích thước nhỏ hơn ứng với công việc truyền tải sẽ nhanh hơn. Dưới đây là cách thức truyền token vào trong HTTP Header sử dụng Bearer Schema:

Authorization: Bearer <token>

  • Tự đóng gói (self-contained): Payload của JWT đã chứa các thông tin cần thiết về user (thay vì phải truy vấn cơ sở dữ liệu nhiều lần).

2. JSON Web Token (JWT) gồm những thành phần nào?

JWT bao gồm 3 phần, ngăn cách nhau bởi dấu “.“, gồm:

  • Header
  • Payload
  • Signature

Chuỗi JWT: header.payload.signature

Ví dụ chuỗi JWT:

Trong đó:

– Header: là Base64URL encoded của json.

– Payload: là Base64URL encoded của json.

– Signature: là kết quả của function Algorithm:

2.1. Header

Gồm 2 phần: type của token, giá tri là JWT, và phương thức mã hóa (HMAC SHA256 hay RSA).

JSON object này sau đó được mã hóa Base64URL.

2.2. Payload

Payload chức các thành phần gọi là claim. Các claim này chứa các thông tin về đối tượng (thường là user), và các meta data của token. Server sẽ dùng các thông tin từ các claim này để có thể thực hiện authentication. Có 3 loại claim: reserved, public và private.

Reserved claim

Là những thông tin đã được quy định ở trong IANA JSON Web Token Claims registry. Các claim này là không bắt buộc phải có trong token, nhưng có thể cung cấp các thông tin hữu ích. Các claim này chỉ gồm 3 ký tự vì mục đích giảm kích thước của Token.

  • iss (issuer): tổ chức phát hành token.
  • sub (subject): chủ đề của token.
  • aud (audience): đối tượng sử dụng token.
  • exp (expired time): thời điểm token sẽ hết hạn.
  • nbf (not before time): token sẽ chưa hợp lệ trước thời điểm này.
  • iat (issued at): thời điểm token được phát hành, tính theo UNIX time.
  • jti: JWT ID.

Public claim

Là những claim được định nghĩa 1 cách công khai bởi những bên sử dụng JWT. Chúng nên được quy định ở trong IANA JSON Web Token Registry hoặc là 1 URI có chứa không gian tên không bị trùng lặp. Ví dụ:

{
"https://gpcoder.com/jwt_clams/user_role" : "admin"
}

Private claim

Là các claim tự định nghĩa và chỉ các thành phần liên quan của hệ thống hiểu được. Ví dụ:

{
"email":"gpcodervn@gmail.com",
"name":"gpcoder"
}

2.3. Signature

Signature được tạo ra bằng cách dùng phương pháp mã hóa được chỉ định ở header để mã hóa nội dung encode của header, payload, cùng với chuỗi khóa bí mật.

Phương thức mã hóa có thể là HMAC hay RSA:

  • HMAC: đối tượng khởi tạo JWT (token issuer) và đầu nhận JWT (token verifier) sử dụng chung 1 mã bí mật để mã hóa và kiểm tra.
  • RSA: sử dụng 1 cặp key, đối tượng khởi tạo JWT sử dụng Private Key để mã hóa, đầu nhận JWT sử dụng Public Key để kiểm tra.

Như vậy với HMAC, cả 2 phía đều phải chia sẻ mã bí mật cho nhau, và đầu nhận JWT hoàn toàn có thể khởi tạo 1 mã JWT khác hợp lệ dựa trên mã bí mật đó. Còn với RSA, đầu nhận sử dụng Public Key để kiểm tra nhưng không thể khởi tạo được 1 JWT mới dựa trên key đó. Vì vậy mã hóa sử dụng RSA giúp cho việc bảo mật chữ ký tốt hơn khi cần chia sẻ JWT với nhiều đối tượng khác nhau.

Ví dụ: nếu header chỉ định dùng thuật toán mã hóa HMAC SHA256, và phía server dùng chuỗi khóa bị mật là ‘secret’ thì signature sẽ được tạo ra như sau:

HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload), "secret"
)

Signature được dùng để chứng thực là token JWT là đúng và nội dung của token đã không được thay đổi.

3. JSON Web Token (JWT) hoạt động như thế nào?

  • Application hoặc client gửi thông tin chứng thực lên server (có thể là username/ password).
  • Khi đã chứng thực thành công, authorization server sẽ trả về một access token (JWT) cho client.
  • Phía Client sẽ gửi token này kèm theo request để truy cập resource. Thông thường token được gửi trong header: Authorization: Bearer <token>

4. Khi nào nên dùng JSON Web Token?

Dưới đây là một vài kịch bản thích hợp với JWT:

  • Chứng thực: Đây là kịch bản phổ biến nhất cho việc sử dụng JWT. Một khi người dùng đã đăng nhập vào hệ thống thì những request tiếp theo từ phía người dùng sẽ chứa thêm mã JWT, cho phép người dùng quyền truy cập vào các đường dẫn, dịch vụ, và tài nguyên mà cần phải có sự cho phép nếu có mã Token đó. Phương pháp này không bị ảnh hưởng bởi Cross-Origin Resource Sharing (CORS) do nó không sử dụng cookie.
  • Trao đổi thông tin: JWT là 1 cách thức không tồi để truyền tin an toàn giữa các thành viên với nhau, nhờ vào phần “chữ ký” của nó. Phía người nhận có thể biết được người gửi là ai thông qua phần chữ ký. Ngoài ra, chữ ký được tạo ra bằng việc kết hợp cả phần header, payload lại nên thông qua đó ta có thể xác nhận được chữ ký có bị giả mạo hay không.

5. Tại sao sử dụng JWT?

  • Performance : JWT gọn nhẹ và đơn giản. Chuỗi token sinh ra ngắn gọn đủ để chứa các thông tin cần thiết (nếu session thì cần đọc ở storage hoặc database). Và bất kỳ ai cũng có thể dễ dàng hiểu, áp dụng được JWT
  • Stateless: Rất phù hợp để áp dụng xây dựng stateless API, bởi token đã chứa đầy đủ các thông tin cần thiết để thực thi authentication.
  • Việc apply ở phía client rất đơn giản, cho dù nền tảng web hay mobile.
  • Dễ dàng mở rộng server hơn. Điều này là nhờ tính stateless của JWT, server không cần phải lưu session state, nên trong trường hợp phía server sử dụng cơ chế load balancing, bất kỳ máy server nào cũng có thể handle request và vẫn có được state của user thay vì chỉ server mà user đã login.
  • Security: Vì JWT không sử dụng cookie nên ta không cần phải lo về vấn đề CSRF. Nhưng JWT tốt nhất cần được sử dụng kèm với HTTPS.
  • Reusable: Chúng ta có thể có nhiều system chạy các nền tảng khác nhau có thể cùng sử dụng 1 JWT để thực hiện authenticate user. Điều này là khó hoặc không thể nếu state được lưu giữ phía server.