Vai trò của JWT trong bảo mật các dịch vụ RESTFul API

RESEARCH CREW
12:58 21/06/2019

  I.  Giới thiệu về JWT

JSON Web Token (JWT) là một tiêu chuẩn mở (RFC 7519) định nghĩa cách thức truyền tin an toàn giữa các bên bằng JSON object. Thông tin token được xác thực và đánh dấu dựa trên Signature của nó.

Phần Signature này được mã hoá bằng HMAC SHA-256 hoặc RSA.

Ví dụ mẫu về token của JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.

BSgTbdPIxfESPo8jmkxqWRBwfwp-8BFWqwg9cnO2kxk

Các bạn có thể lên trang chủ của JSON Web Token để thấy rõ hơn.

Hình 1 : Thành phần của chuỗi JWT

Mục đích chính của JWT để xác thực user và xác thực hành vi của user đó. Cụ thể về mục đích và tại sao nên sử dụng JWT sẽ nói rõ trong phần sau.

  II. Các thành phần của JWT

Các thành phần của JWT

Hình 2: Cấu trúc của JWT

Khi các bạn search trên mạng từ khoá JWT thì sẽ thấy hình trên khá là quen, bởi vì nó mô tả đầy đủ cấu trúc của JWT.

JSON Web Token bao gồm 3 phần, được ngăn cách nhau bởi dấu chấm (.):

  1. Header
  2. Payload
  3. Signature (chữ ký)

Tổng quát thì nó có dạng như sau:

Header.Payload.Signature

  1. Header

Phần header bao gồm 2 phần:

  1. "typ": "JWT" => Khai báo rằng loại token là JWT
  2. "alg": "HS256" => Signature của nó sử dụng thuật toán mã hóa HMAC SHA-256, ở đây có thể thay là RSA nếu chúng ta muốn dùng thuật toán mã hoá là RSA

Sau đó đoạn header này sẽ được mã hoá Base64Url. Kết quả ta được phần đầu tiên của JWT là:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

  1. Payload

Trong payload sẽ chứa các khai báo (claim). Các claim này sẽ bao gồm dữ liệu mà ta muốn truyền đi, và có thể cả các thông tin khác về token của ta nữa. Chia nhỏ ra thì claim bao gồm các loại như sau:

a.  Registered (Reserved) claims

Đây là một tập hợp các Claims được định nghĩa sẳn bởi JWT tuy không bắt buộc nhưng được khuyến khích sử dụng, vì các claims này chứa các thông tin về token như là ngày hết hạn, ngày phát hành hay tổ chức tạo token, …

Những claims này đã được quy định ở trong IANA JSON Web Token Claims registry, các bạn có thể tham khảo chi tiết tại Registered Claim Names :

b. Public claims

Các claim này do ta tùy ý định nghĩa để truyền tải thông tin mà mình muốn. Tuy nhiên, để cho dễ hiểu và tránh tình trạng conflict, các claim dạng này nên được khai báo tại 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.

c. Private claims

Các claims này do chúng ta tự định nghĩa để chứa những thông tin mà ta muốn truyền đi (không được trùng với Reserved Claims và Public Claims). Thông thường trong ứng dụng thì chúng ta hay dùng loại này để chứa dữ liệu mà chúng ta cần như là user ID, để khi decode token ra thì biết token này của user gữi lên.

Bên trên là tổng quan về phần thứ 2 (Payload) của JWT. Decode thữ đoạn thứ 2 của một token của account đã login vào web chat realtime thì được như hình 3.

Hình 3: Decode thữ phần payload của JWT

Trong payload chứa các thông tin như:

Private claims: _id, username.

Registered (Reserved) claims: iat, exp

Các bạn có thể tham khảo hoặc lấy get một cái token thực tế qua ứng dụng chat realtime. Đăng ký tài khoản và đăng nhập thành công, sau đó vào Inspect => Application => Local Storage => URL thì sẽ thấy được token của ứng dụng này được lưu vào local storage của browser.

Hình 4: JWT lưu trữ trong local storage

  1. Signature

Phần chữ ký được tạo bằng cách kết hợp 2 phần Header + Payload đã encode bằng Base64Url, rồi mã hóa nó với secrect key với thuật toán được chỉ định rõ ở phần header.

Như nãy là thuật toán được sử dụng là HS256 thì signature sẽ được tạo ra như sau:

Với cách thực hiện này thì signature được sử dụng để xác minh message không bị thay đổi trên đường đi và trong trường hợp token được ký bằng secret key, nó cũng có thể xác minh rằng người gửi token là ai.

Thì đó là phần thứ 3 (Signature) của JWT.

Tổng quát lại thì JWT có 3 dạng như hình, các bạn có thể lên trang của JWT tham khảo thêm

Hình 5: Cấu trúc của JWT trong trang chủ jwt

III. Khi nào nên sử dụng JWT

Authorization: Đây là ứng dụng phổ biến nhất để sử dụng JWT. Khi người dùng đã đăng nhập, mỗi yêu cầu tiếp theo sẽ bao gồm JWT. Điều này cho phép người dùng được cấp quyền truy cập vào các url, service, và resource mà mã Token đó cho phép.

Information Exchange: JSON Web Token là 1 cách thức khá hay để truyền thông tin an toàn giữa các thành viên với nhau, nhờ vào phần signature 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 signature. Và 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.

IV. Cách thức hoạt động của JWT

Ví dụ cụ thể trong ứng dụng chat realtime khi nãy thì trong phần login khi xác thực sử dụng JWT để Authenticate user. Khi user đăng nhập thành công (Browser sẽ post username và password về Server), Server sẽ trả về “accessToken” và “refreshToken” là các chuỗi JWT về Browser, và Token JWT này cần được lưu lại trong Browser của người dùng (thường là LocalStorage hoặc Cookies), thay vì cách truyền thống là tạo một session trên Server và trả về Cookie.

Bất cứ khi nào mà User muốn thực hiện các hành vi nào mà chỉ có User đã đăng nhập mới được phép thì Client sẽ gửi request kèm theo token JWT (để đâu tuỳ theo người code). Ở đây trong hình để ở trong phần Request Headers mục x-access-token:

Hình 6: Chèn token vào header.

Sơ đồ mô tả quá trình này như Hình 7:

Hình 7: Sở đồ mô tả quá trình hoạt động của JWT

accessToken là token JWT mà cái này dùng để chứng thực user và hành vi của user đó. Còn refreshToken dùng để gữi lên xin accessToken mới cho user. Với cách làm này thì khi accessToken của user khi gần hết hạn thì sẽ tự động xin lại và user sẽ không cần phải đăng nhập lại, điều này duy trì sự đăng nhập của user.

  V.  Ví dụ thực tế về ứng dụng của JWT

Jwt là một cách thức xác thực user nên chúng ta có thể hoàn toàn tự code ra được. Nhưng jwt hỗ trợ thư viện hầu hết các ngôn ngữ nên chúng ta có thể import vào sử dụng một cách sẽ dàng. Dưới đây là code ví dụ sử dụng JWT trong ứng dụng chat realtime đề cập ở trên.

Hình 8: Source code mô tả phần login trong ứng dụng chat realtime

Luồng thực thi của phần login có sử dụng JWT:

  1. Kiểm tra các tham số của req xem đã đủ chưa như là username, password. Nếu chưa đủ thì return fail
  2. Nếu đủ thì tìm user đó trong mongodb, nếu không có thì return fail, nếu có thì kiểm tra password, nếu sai thì return fail
  3. Nếu đúng password thì tiến hành tạo payload (các thông tin mình cần gữi, payload này là phần thứ 2 trong cấu trúc của jwt, trong lúc encode phần này thì JWT tự động thêm các trường được quy định sẳn như exp, iat, … sau đó mới Base64url Encoding) để tạo accessToken(3 days) và refreshToken(30 days) (số thời gian tuỳ vào coder qui định) bằng hàm createToken(hàm này sẽ show code ở hình dưới)
  4. Sau đó gữi về cho frontend lưu vào local storage hoặc một chổ nào đó trên browser.

Hình 9: Source code hàm createToken

Để ý hàm createToken thì hàm này dùng để tạo token với tham số truyền vào là thời gian hết hạn token. Hàm sign để tạo token của thư viện jsonwebtoken.

jwt.sign(user, config.secrect, { expiresIn: expire});

Các tham số:

Hình 10: Verify JWT trong middleware

Khi user thực hiện các hành vi hay sử dụng service nào cần có sự xác thực thì sẽ đi qua middleware. Middleware này dùng để chèn giữa các request gữi lên server dùng để kiểm tra token của user đó có hợp lệ hay không, nếu không có token gữi lên hay token không hợp lệ thì return fail.

Hàm jwt.verify dùng để decode token đó với secret key lúc nãy,  sau khi decode xong thì kết quả sẽ trả về trong hàm callback, lúc đó result(decoded) chưa phần payload (thông tin chúng ta truyền vào khi tạo token). Chúng ta có thể lấy các thông tin này xác thực user nào đã gọi lên service.

Như vậy bài viết đã nói lên tổng quát về JWT, các cấu trúc của nó và cách sử dụng. Hi vọng giúp các bạn phần nào hiểu được các thức xác thực khá hay này.

Bài viết - Hai Nguyen

Tài liệu tham khảo:

[1] JWT Introduction

[2] RFC7519

[3] Chat realtime app source code