Hoàn cảnh ra đời của JWT
Tổng quan
Trong mọi ứng dụng web, di động hay desktop, việc tạo tài khoản và đăng nhập để truy cập các tính năng là điều không thể thiếu Hành động này được gọi là xác thực người dùng (Authentication).
Để xác thực người dùng, chúng ta sẽ xem xét mô hình cơ bản mà nhiều trang web phổ biến đã áp dụng, đó là sử dụng Session và Cookie để thực hiện quá trình xác thực.
Hình 1: Mô hình sử dụng Session - Cookie để Authenticate
Mô hình xác thực Session – Cookie hoạt động bằng cách khi người dùng đăng nhập vào trang web, server sẽ tạo một Session (phiên làm việc) cho người dùng và lưu trữ trên server ID của Session sẽ được trả về và lưu trong Cookie trên trình duyệt của người dùng Session này có thời hạn sử dụng do các lập trình viên chỉ định, ví dụ như 1 ngày; sau thời gian này, người dùng cần đăng nhập lại để tạo một Session mới.
Khi người dùng vẫn đang đăng nhập và Session còn hạn, Cookie sẽ được tự động đính kèm vào mọi yêu cầu gửi lên Server Server sẽ so sánh Session ID từ Cookie với thông tin Session lưu trữ trên server, như RAM hoặc Database, để xác minh danh tính người dùng và gửi lại phản hồi tương ứng.
Tại sao JWT lại xuất hiện và dần được ưa chuộng hơn Session?
Lý do chính cho việc “Extend Platform” là mở rộng nền tảng hệ thống Khi có một trang web hoạt động ổn định với Session, việc phát triển thêm các ứng dụng di động Native cho hệ điều hành iOS hoặc Android và sử dụng chung một cơ sở dữ liệu với ứng dụng web hiện tại sẽ gặp khó khăn Trong trường hợp này, việc xác thực người dùng trên các ứng dụng Native không thể thực hiện qua Session vì chúng không hỗ trợ Cookie, điều này chỉ có ở trình duyệt.
- Ngoài ra, có một số lí do ta nên sử dụng JWT như:
+ JWT dễ bảo trì và debug
+ JWT có khả năng tạo ra RESTful services thực thụ
+ JWT tích hợp thời gian hết hạn
JWT ra đời để giải quyết vấn đề xác thực và bảo mật thông tin, hiện nay nó trở thành lựa chọn ưu tiên khi bắt đầu các dự án mới.
JWT nên được sử dụng trong trường hợp nào?
After the user logs in, the client receives a response that includes a JWT For each subsequent request, this JWT is sent along, allowing the server to verify the token With a valid token, the user can access authorized resources and routes.
JWT là một phương tiện an toàn để trao đổi thông tin giữa các bên, nhờ vào khả năng ký kết bằng cặp khóa riêng/công khai Điều này đảm bảo rằng người gửi có thể được xác thực và nội dung thông điệp là chính xác Hơn nữa, với cơ chế xác minh qua header và payload, JWT giúp đảm bảo rằng thông tin không bị giả mạo.
JWT là một cơ chế xác thực hiệu quả mà không cần sử dụng cơ sở dữ liệu Nhờ vào việc dữ liệu trong JWT được gửi đến client một cách an toàn, server có thể hoạt động mà không phụ thuộc vào cơ sở dữ liệu.
1.3.4 ủy quyền hoạt động trên các server
Khi đăng nhập vào server 1, người dùng sẽ được chuyển hướng đến server 2 để thực hiện các hoạt động cần thiết Trong tình huống này, server 1 có thể cấp một JWT cho phép truy cập vào server 2, giúp hai server không cần sử dụng thêm phương thức xác thực nào khác.
Xác thực phân quyền JSON Web Token
Khái quát về Token-based authentication
Token-based authentication là một phương thức xác thực sử dụng chuỗi mã hóa Hệ thống này cho phép người dùng nhập thông tin đăng nhập (user/password) để nhận một mã token Mã token này được sử dụng để xác minh quyền truy cập vào tài nguyên mà không cần nhập lại thông tin đăng nhập.
Định nghĩa JWT
JWT (JSON Web Token) là một tiêu chuẩn mở (RFC 7519) cho phép truyền tin an toàn giữa các bên thông qua đối tượng JSON Thông tin trong JWT có thể được xác thực và đảm bảo tính tin cậy nhờ vào chữ ký, được mã hóa bằng HMAC và RSA Do đó, bảo mật JWT trở thành một phương pháp hiệu quả để xác thực quyền truy cập.
Các thuật ngữ liên quan
JSON Web Token (JWT) là một chuỗi đại diện cho các xác nhận quyền sở hữu dưới dạng đối tượng JSON, được mã hóa trong JWS hoặc JWE JWT cho phép các xác nhận quyền sở hữu này được ký kỹ thuật số hoặc sử dụng MAC, đồng thời có thể được mã hóa để bảo mật thông tin.
- JWT Claims Set: Một đối tượng JSON có chứa các xác nhận quyền sở hữu được truyền tải bởi JWT
- Claim: Phần tên của đại diện xác nhận quyền sở hữu
- Claim value: Phần giá trị của đại diện xác nhận quyền sở hữu Giá trị xác nhận quyền sở hữu có thể là bất kỳ giá trị JSON nào
JWT lồng nhau là một phương pháp trong đó việc ký và mã hóa được thực hiện lồng ghép Trong cấu trúc này, JWT được sử dụng như là trọng tải hoặc giá trị bản rõ cho JWS (JSON Web Signature) hoặc JWE (JSON Web Encryption) bao quanh, tùy thuộc vào mục đích sử dụng.
- Unsecured JWT: JWT mà claim không được mã hóa hoặc bảo vệ
StringOrURI là một giá trị chuỗi JSON, yêu cầu rằng các giá trị chuỗi tùy ý có thể được sử dụng, nhưng bất kỳ giá trị nào chứa ký tự ":" phải tuân theo định dạng URI [RFC3986] Giá trị StringOrURI sẽ được so sánh dưới dạng chuỗi phân biệt chữ hoa chữ thường mà không cần áp dụng bất kỳ phép biến đổi hoặc chuẩn hóa nào.
Cấu trúc của một JWT
- Dưới đây là một JSON Web Token:
Hình 3: Ví dụ về JSON Web Token
- JWT gồm 3 phần (theo ví dụ trên):
+ Header (eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9)
+ Payload (eyJzdWIiOiJuaHMzMTA4IiwiZXhwIjoxNTU4MDYzODM3fQ)
+ Signature (449KVmOFWcpOUjnYGm-f1QWhY8N-DerKDfTK0JQm1Nc)
- Cấu trúc theo format như sau:
Hình 4: Cấu trúc của JWT
+ Alg – Thuật toán đã dùng để mã hóa (HMAC SHA256 – HS256 hoặc RSA)
+ Typ – Loại token (mặc định là JWT – Thông tin này cho biết đây là Token JWT)
Hình 5: Ví dụ về Header
- Đoạn JSON trên sau khi được mã hóa base64url sẽ trở thành như sau:
Hình 6: Header sau khi mã hóa base64url
- Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Phần thứ hai của token là Payload, nơi chứa thông tin (claim) quan trọng Nội dung truyền đi có thể là mô tả về một thực thể, chẳng hạn như người dùng, hoặc thông tin bổ sung cho phần header Payload được phân loại thành ba loại chính: Reserved, Public và Private.
Thông tin Reserved trong IANA JSON Web Token Claims Registry không bắt buộc, nhưng tùy thuộc vào từng ứng dụng, có thể thiết lập yêu cầu bắt buộc cho các thông tin cần thiết.
+ 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
Khóa trong JWT có thể được định nghĩa theo ý muốn của người sử dụng, tuy nhiên để tránh trùng lặp, nên quy định khóa trong IANA JSON Web Token Registry hoặc sử dụng một URI chứa không gian tên độc nhất.
Hình 7: Ví dụ về Public
+ Private: Phần thông tin thêm dùng để truyền qua giữa các máy thành viên, ví dụ:
Hình 8: Ví dụ về Private
- Ta có ví dụ cho phần Payload như sau:
Hình 9: Ví dụ về Payload
- Đoạn JSON trên sau khi được mã hóa base64url sẽ trở thành như sau:
String payload = "{"sub":"nhs3108","exp":1558063837}";
System.out.println(Base64.getUrlEncoder().encodeToString(payload. getBytes()));
- Output: eyJzdWIiOiJuaHMzMTA4IiwiZXhwIjoxNTU4MDYzODM3fQ
Phần chữ ký được tạo ra bằng cách kết hợp hai thành phần header và payload, sau đó mã hóa chúng bằng một thuật toán mã hóa phức tạp như HMAC SHA256.
- Với signature là phần kết hợp giữa header và payload, ta có:
String header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
Base64.getUrlEncoder().encodeToString(header.getBytes());
String payload = "{"sub":"nhs3108","exp":1558063837}";
Base64.getUrlEncoder().encodeToString(payload.getBytes());
String encodedSignature = HMACSHA256.encode(signature, scretKey); System.out.println(encodedSignature);
- Tổng lại sau 3 phần ta có JWT như sau: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJuaHMzMTA4IiwiZXhwIjoxNTU4MDYzODM3fQ.449KVmOFWcpOUjnYGm-f1QWhY8N-DerKDfTK0JQm1Nc
Luồng xử lý của 1 hệ thống sử dụng bảo mật JWT
2.5.1 Sơ lược về luồng xử lý
- Nhìn vào sơ đồ, ta có thể thấy luồng đi như sau:
1 User thực hiện login bằng cách gửi id/password hay sử dụng các tài khoản mạng xã hội lên phía Authentication Server (Server xác thực)
2 Authentication Server tiếp nhận các dữ liệu mà User gửi lên để phục vụ cho việc xác thực người dùng Trong trường hợp thành công, Authentication Server sẽ tạo một JWT và trả về cho người dùng thông qua response
3 Người dùng nhận được JWT do Authentication Server vừa mới trả về làm
"chìa khóa" để thực hiện các "lệnh" tiếp theo đối với Application Server
4 Application Server trước khi thực hiện yêu cầu được gọi từ phía User, sẽ verify JWT gửi lên Nếu OK, tiếp tục thực hiện yêu cầu được gọi.
2.5.2 Hệ thống Verify chuỗi JWT như thế nào
- Chuỗi JWT có cấu trúc H.P.S được Client gửi lên Server sẽ làm tương tự như sau:
+ Set S2 = HMAC(SHA256 (H.P) với secret key của hệ thống)
(Giả sử hệ thống sử dụng encryption algorithms SHA256)
Nếu chữ ký S1 và S2 khớp nhau, điều này có nghĩa là chữ ký hợp lệ Khi đó, hệ thống sẽ tiếp tục giải mã payload và kiểm tra các dữ liệu bên trong, chẳng hạn như trường exp (ngày hết hạn).
2.5.3 Các thuật toán được sử dụng
- Hầu hết các JWT chỉ được ký sử dụng các thuật toán phổ biến nhất là:
- Đây là thuật toán phổ biến nhất cho các JWT đã ký
Mã xác thực tin nhắn dựa trên hàm băm (HMAC) cho phép người dùng kết hợp bí mật với hàm bảo mật để tạo ra mã xác thực (MAC) Điều này giúp chứng minh rằng chỉ những người sở hữu khóa MAC mới có khả năng tạo ra mã xác thực này.
- Thuật toán được triển khai và xác thực HMAC được cung cấp trong RFC2104
HMAC SHA-256, được phát triển theo tiêu chuẩn RFC2104, sử dụng thuật toán băm SHA-256 với đầu vào là giá trị “text” cùng với một khóa chia sẻ Giá trị đầu ra của HMAC này chính là chữ ký JWS.
- Các giá trị Header Parameter được sử dụng để chỉ ra rằng JWS Signature là một giá trị HMAC được tính bằng cách sử dụng thuật toán tương ứng
Hình 11: HMAC được tính bằng cách sử dụng thuật toán tương ứng
- HMAC SHA-256 MAC cho JWS được xác thực bằng cách tính HMAC giá trị trên mỗi RFC2104, sử dụng SHA-256 làm thuật toán băm
Đầu vào JWS Signing nhận dạng dưới dạng văn bản và sử dụng khóa công khai Giá trị HMAC được tính toán và sau đó so sánh với kết quả của việc giải mã base64url giá trị JWS Signature đã được mã hóa.
Để ngăn chặn các cuộc tấn công định thời, việc so sánh giá trị HMAC đã tính toán với giá trị JWS Signature cần được thực hiện liên tục trong suốt quá trình.
Giá trị HMAC được tính toán có thể được mã hóa bằng base64url và so sánh với giá trị chữ ký JWS đã nhận, đảm bảo quá trình so sánh không thay đổi theo thời gian Việc so sánh này cho kết quả tương tự như so sánh các giá trị chưa được mã hóa; nếu các giá trị khớp, HMAC sẽ được xác thực Bên cạnh đó, phương pháp RSASSA-PKCS1-v1_5 cũng được áp dụng trong quy trình này.
Chữ ký số RSASSA-PKCS1-v1_5 SHA256 được tạo ra bằng cách sử dụng phương pháp RSASSA-PKCS1-v1_5 SIGN kết hợp với hàm băm SHA256 để ký điện tử đầu vào JWS Signing, với mục đích bảo vệ khóa riêng Giá trị này chính là chữ ký JWS.
The "alg" (algorithm) Header Parameter values are used to indicate that the JWS Signature is an electronic signature calculated using the corresponding algorithms.
Hình 12: RSASSA-PKCS1-v1_5 được tính toán sử dụng các thuật toán tương ứng
- Chữ ký số RSASSA-PKCS1-v1_5 SHA256 cho JWS xác thực như sau:
Để xác thực chữ ký JWS, cần gửi đầu vào bao gồm chữ ký JWS và khóa công khai tương ứng với khóa cá nhân, sử dụng thuật toán RSASSA-PKCS1-v1_5-VERIFY với SHA256 làm hàm băm.
Ký số được thực hiện với RSASSA-PKCS1-v1_5 SHA-384 và RSASSA-PKCS1-v1_5 SHA512 theo quy trình giống như RSASSA-PKCS1-v1_5 SHA256, chỉ cần thay thế bằng các thuật toán băm tương ứng.
Thuật toán chữ ký số đường cong Elliptic (ECDSA) sử dụng mật mã đường cong Elliptic, mang lại bảo mật tương đương với RSA nhưng với kích thước khóa ngắn hơn và tốc độ xử lý nhanh hơn Điều này có nghĩa là chữ ký điện tử ECDSA có thể nhỏ hơn đáng kể về độ dài so với RSA nhưng vẫn đảm bảo sức mạnh bảo mật tương đương.
Đặc điểm kỹ thuật này quy định việc sử dụng ECDSA với các đường cong P-256, P-384 và P-512, kết hợp với các hàm băm mật mã SHA-256, SHA-384 và SHA-512 tương ứng.
- Chữ ký số ECDSA P-256 SHA-256 được tạo như sau:
• Tạo chữ ký điện tử của đầu vào JWS Signing bằng ECDSA P-256 SHA-
256 với khóa riêng mong muốn Đầu ra sẽ là cặp (R, S) trong đó R, S là các số nguyên không dấu 256 bit
• Biến R và S thành các chuỗi octet theo thứ tự big-endian với mỗi mảng dài 32 octet
• Ghép hai dãy octet theo thứ tự R và S (Lưu ý rằng nhiều triển khai ECDSA sẽ trực tiếp tạo ra nối làm đầu ra của chúng)
• Chuỗi 64 octet kết quả là giá trị của JWS Signature
The "alg" (algorithm) Header Parameter values indicate that the JWS Signature is an electronically calculated signature derived from corresponding algorithms.
Hình 13: ECDSA được tính toán sử dụng các thuật toán tương ứng
- Chữ ký số ECDSA p-256 cho JWS được xác thực như sau:
• Giá trị chữ ký JWS phải là một chuỗi 64-octet Nếu nó không phải một chuỗi 64-octet, quá trình xác thực không thành công
Tách chuỗi 64-octet thành hai chuỗi 32-octet, trong đó chuỗi octet đầu tiên đại diện cho R và chuỗi thứ hai đại diện cho S Các giá trị R và S được chuyển đổi thành chuỗi octet thông qua phương pháp Integer-to-OctetString Conversion.
• Gửi đầu vào JWS, R, S và khóa công khai (x, y) tới trình xác thực ECDSA P-256 SHA-256
JWT và Session
- No Session: Với JWT, ta không cần phải keep session data trên server để xác thực user Luồng đi sẽ kiểu như thế này:
+ Người dùng gọi authentication service, thường là gửi username/password + Authentication service phản hồi cho người dùng mã JWT, cái này sẽ định nghĩa xem user là ai
+ Người dùng yêu cầu truy cập một dịch vụ được bảo mật bằng việc gửi token lên
+ Lớp bảo mật sẽ check chữ ký trên token và nếu đó là quyền truy cập hợp lệ thì được tiếp tục truy cập
Việc không sử dụng session trong ứng dụng giúp loại bỏ nhu cầu lưu trữ session, điều này trở nên quan trọng khi ứng dụng mở rộng trên nhiều server Khi chạy ứng dụng trên nhiều server, việc sử dụng session có thể gây ra gánh nặng, vì ta sẽ cần một server riêng để lưu session, chia sẻ không gian đĩa giữa các server, hoặc sử dụng sticky session trên load balancer Tuy nhiên, tất cả những vấn đề này đều không cần thiết nếu ứng dụng không sử dụng session để xác thực người dùng.
Không cần xử lý các session rác vì JWT có thể tự quản lý thời hạn hết hạn của nó Mỗi JWT đi kèm với expiry date và dữ liệu người dùng, giúp giảm thiểu việc phải xóa các session không cần thiết.
Security check xác thực của JWT, nó có thể check expiry time của token và đơn giản là từ chối truy cập
Dịch vụ RESTful thực sự chỉ có thể được tạo ra khi không sử dụng sessions, vì một dịch vụ RESTful được định nghĩa là phải không trạng thái (stateless) Với kích thước nhỏ, JWT có thể được gửi kèm theo mọi yêu cầu giống như cookie phiên (session cookie) Tuy nhiên, khác với cookie phiên, JWT không cần phải tham chiếu đến bất kỳ dữ liệu nào được lưu trữ trên máy chủ, vì bản thân JWT đã chứa dữ liệu.
Before the emergence of token-based authentication, cookie-based and server-based authentication methods were commonly used It's important to note that the HTTP protocol is stateless, meaning that if a user is authenticated with a username and password, the system does not retain that information for subsequent requests As a result, additional authentication is necessary for each new request to identify the user.
1 lần nữa Vì thế cần đảm bảo rằng sau khi người dùng đã đăng nhập, trạng thái xác thực vẫn được verified trong mỗi request tiếp sau
Hình 14: Các luồng thực thi
Thông tin đăng nhập của người dùng được gửi đến server qua POST request, nơi server sẽ xác thực thông tin Nếu thông tin hợp lệ, hệ thống sẽ gửi lại một cookie chứa SESSION ID (trong Java Framework gọi là JSESSIONID) để nhận diện người dùng Session của người dùng sẽ được lưu trữ trên server thông qua file hoặc database Khi người dùng gửi request tới server, request đó sẽ kèm theo SESSION ID trong header, và server sẽ sử dụng SESSION ID này để kiểm tra trạng thái xác thực và các thông tin liên quan.
Hình 15: Các luồng thực thi của Cookie-based Authentication và Token-based Authentication
Khi ứng dụng phát triển và số lượng người dùng gia tăng, việc mở rộng theo chiều ngang hoặc chiều dọc trở nên cần thiết Dữ liệu phiên được lưu trữ trong bộ nhớ máy chủ qua tệp hoặc cơ sở dữ liệu Để mở rộng theo chiều ngang, cần sao chép máy chủ và thiết lập một hệ thống lưu trữ phiên trung tâm để tất cả máy chủ ứng dụng có thể truy cập Nếu không, nhược điểm của session-store sẽ cản trở khả năng mở rộng Một giải pháp là sử dụng sticky-session, nơi tất cả yêu cầu được gửi đến cùng một máy chủ vật lý Tuy nhiên, các giải pháp này không thực sự hiệu quả cho các ứng dụng lớn hiện đại và có thể phát sinh chi phí.
Việc sử dụng JWT giúp loại bỏ nhu cầu lưu trữ thông tin người dùng trong session, nhờ vào cơ chế xác thực dựa trên token stateless Điều này không chỉ giúp ứng dụng dễ dàng mở rộng mà còn cho phép truy cập tài nguyên từ nhiều máy chủ khác nhau mà không cần lo lắng về việc người dùng đã đăng nhập vào máy chủ cụ thể nào Hơn nữa, việc này cũng tiết kiệm chi phí vì không cần máy chủ chuyên dụng để lưu trữ session.
Hiện nay, JSON Web Token (JWT) thường được lưu trữ trong bộ nhớ web như local/session storage hoặc cookies Việc mất token, bao gồm cả JWT và SessionID, có thể dẫn đến việc người khác có quyền truy cập vào hệ thống Để tăng cường bảo mật, người dùng nên giảm thời gian hiệu lực của token (expiration date), giúp hạn chế khả năng bị lạm dụng.
To prevent token theft, it is crucial to use HTTPS/SSL, ensuring that Cookies and JWTs are always encrypted during client-server exchanges This measure helps protect against eavesdropping tools like Wireshark, which could potentially monitor and read requests.
Phân tích việc mã hóa thông tin trong JWT là rất quan trọng, vì khi gửi yêu cầu từ client đến server, nếu chứa nhiều thông tin, nó sẽ tạo ra khối lượng dữ liệu lớn trong mỗi yêu cầu HTTP Ngược lại, với session, chỉ có SESSION ID, một chuỗi mã hóa duy nhất, được sử dụng để định danh mà không kèm theo thông tin bổ sung nào khác.
- Một JWT chứa 5 thông tin như sau:
Hình 16: Thông tin của một JWT
Khi JWT được mã hóa, kích thước của nó lớn hơn nhiều so với SESSION_ID, chỉ dùng để định danh, dẫn đến việc tạo ra JWT sẽ tiêu tốn nhiều băng thông hơn ở mỗi yêu cầu Trong khi đó, với SESSION, thông tin được lưu trữ trên server và được truy xuất dựa vào SESSION ID mà người dùng gửi lên.
JWT lưu trữ thông tin trực tiếp trên client dưới dạng chuỗi mã hóa, cho phép server dễ dàng giải mã và đọc thông tin Điều này giúp giảm thiểu việc truy vấn liên tục vào cơ sở dữ liệu cho mỗi yêu cầu, vì hệ thống chỉ cần kiểm tra chữ ký và một số thông tin trong claims để xác thực.
Ứng dụng xác thực phân quyền JWT trong doanh nghiệp Fintech
- Hệ thống JWT xác thực phân quyền của TPBank sau khi người dùng nhập thông tin đăng nhập
Hình 17: Đăng nhập thông tin đăng nhập
- Tại thời điểm này cookie chưa lưu trạng thái đăng nhập nên chỉ có các thông tin cơ bản
Hình 18: Thông tin cơ bản lưu trong Local Storage
- Sau khi gói tin được server xử lý Client view nhận lại một JWT
Hình 20: Client view nhận lại một JWT
- Tại Client view cũng nhận được cookie mới lưu trạng thái Authorization của token
Hình 21: Client view nhận được cookie mới
- Hiển thị giao diện và click vào 1 chức năng
Hình 22: Vào giao diện chính và click vào 1 chức năng
- Gói tin lưu trạng thái đăng nhập với JWT Header Authorization là Bearer Token được đẩy lên server để filter tại server check trạng thái Token
Hình 23: Bearer Token được đẩy lên server
DEMO Ứng dụng authentication và authorization với ReactJS và
authorization với ReactJS và Spring Boot sử dụng JWT
- View Client sử dụng reactJS, khi người dùng click và gọi đăng nhập lên server
Hình 24: View client sử dụng reactJS
- Tại reactJS thông tin đăng nhập sẽ được đóng gói vào đối tượng và gửi theo request Login
Hình 25: Thông tin đăng nhập được gửi theo LoginRequest
- Call API tới server method post với LoginRequest attach
Hình 26: Call API tới server với LoginRequest
- Tại server khởi tạo một Authentication object đưa vào FORM_LOGIN_FILTER.
Hình 27: Khởi tạo một Authentication object
- Tại AuthenticationManager sử dụng AuthenticationProvider để thực hiện validate thông tin người dùng
- AuthenticationProvider sẽ gọi phương thức loadUserByUsername để tìm thông tin Userdetail và so sánh với các Username và Password trong Authentication vừa gửi lên trong request
- Nếu mà Authentication not null , có nghĩa Authentication là tồn tại thì đưa vào SecurityContext và tạo JWT
Hình 30: Đưa vào SecurityContext và tạo JWT
- Hàm tạo JWT với các thành phần Header, PayLoad và Signature
- Sau khi Login thành công, hệ thống trả lại JWT, client lưu vào LocalStorage và set Authentication và header
Hình 34: Đăng nhập thành công và chuyển đến giao diện chính
- Mỗi lần hệ thống gọi chức năng tiếp theo header sẽ gắn Authentication với token và call tới server
Hình 36: Header gắn Authentication với token
- Server sẽ lọc JWT để xác thực token cũng như cấp quyền truy cập tài nguyên server
DEMO tấn công JWT Token
Một số cách tấn công mã thông báo JWT
- Khi người dùng thực hiện đăng nhập, sử dụng công cụ Burpsuite để thực hiện chặn bắt
- Yêu cầu đăng nhập của người dùng bị chặn
- Khi người dùng đăng nhập lại, yêu cầu thứ 2 chứa jwt để truy cập tài khoản
Tiến hành thay đổi thuật toán bằng cách loại bỏ thông tin trong phần tiêu đề, đổi tên thành "quản trị" và để trống chữ ký, sau đó gửi yêu cầu lên máy chủ.
- Kẻ tấn công đăng nhập thành công với vai trò quản trị
Cách 2: RS256 to HS256 Vulnerability
- HS256 sử dụng cùng một khóa để ký mã thông báo và xác minh thông báo
- RS256 sử dụng khóa riêng để ký mã thông báo và sử dụng khóa công khai để xác minh
- Khóa RS256 có thể khả dụng cho người dùng
Việc chuyển đổi từ thuật toán RS256 sang HS256 tạo điều kiện cho kẻ tấn công có khả năng chiếm đoạt một token, ký nó bằng khóa công khai, từ đó tạo ra một chữ ký hợp lệ.
- Khi người dùng thực hiện đăng nhập, sử dụng công cụ BurpSuite để thực hiện chặn bắt
- Yêu cầu đăng nhập của người dùng bị chặn
- Khi người dùng đăng nhập lại thì yêu cầu thứ 2 chứa jwt hợp lệ để truy cập tài khoản
- Kẻ tấn công cần jwt hợp lệ để có thể đăng nhập
- Kẻ tấn công thực hiện thay đổi thuật toán thành HS256 và sử dụng khóa công khai để tạo mã thông báo mới
- Loại bỏ jwt thu được, thực hiện tạo jwt mới
- Trước hết các thư viện mật mã được nhập, sau đó đọc tệp khóa công khai
- Sau đó tạo header với HS256 và payload với vai trò người quản trị
- Mã hóa header và payload với base64, sau đó nối với nhau
- Sử dụng khóa công khai để tạo chữ ký hợp lệ và in mã thông báo jwt
- Khi đã có mã thông báo hợp lệ, mở bộ Burp và thay thế mã thông báo json trước đó bằng mã đã tạo mới, chuyển tiếp yêu cầu
- Kẻ tấn công đăng nhập thành công với vai trò admin
Khi làm việc với mã thông báo JWT, việc sử dụng khóa bí mật yếu có thể khiến mã thông báo dễ bị tấn công Chúng ta có thể sử dụng Hashcat để brute force khóa bí mật này, từ đó bảo vệ an toàn cho hệ thống.
- Khi người dùng thực hiện đăng nhập, sử dụng công cụ BurpSuite để thực hiện chặn bắt
- Sao chép mã thông báo và lưu mã thông báo vào một tệp văn bản
- Sử dụng hashcat để bruteforce
- Sau khi chữ ký bí mật của mã thông báo đã được crack thành công
- Tạo mã thông báo mới payload vai trò quản trị viên
- Sao chép mã thông báo và dán nó vào bộ lặp trong Burp và chuyển tiếp yêu cầu
- Đăng nhập thành công với vai trò quản trị viên
Kết luận, JWT là một phương thức an toàn để truyền tải thông tin, tuy nhiên, một số triển khai không đảm bảo an toàn khi lưu trữ dữ liệu nhạy cảm Việc cho phép thay đổi thuật toán ký hoặc sử dụng khóa yếu trong chữ ký có thể làm tăng nguy cơ tấn công cho ứng dụng web.
Demo tấn công
- Dựng Proxy trong Browser FoxyProxy Options
- Bật Proxy trong Browser tại server NodeJS
Hình 40: Bật proxy trong browser
Hình 41: Điền username và password
Hình 42: Burpsuite bắt được gói tin
- Send To Repeater để thêm gói và gửi Packet đến Server
Hình 43: Chuyển gói tin bắt được sang repeater
- Response của gói tin nhận được JWT Token
- Phân tích nội dung của JWT với Base64
Hình 45: Sử dụng decoder để phân tích nội dung JWT
- Sửa đổi thành phần trong JWT
Hình 46: sửa đổi thành phần
- Từ Access Token ban đầu: eyJhbGciOiJIUzUxMiJ9.eyJmdWxsTmFtZSI6InR1bmdsYSIsImlkIjoiMiIsImV4cCI6MT YzODExNjA5OSwiaWF0IjoxNjM4MTE1Nzk5LCJ1c2VybmFtZSI6IjEyMzEyM0Bnb WFpbC5jb20ifQ.o-VY0jAA4kubfTUb1C8pLWfRqDpQlcGweijp18ozewY-
Yl8ZRHYxXF6flcXZUToY47_gcV1UlzDHBgjjNb69QQ
Hình 47: Thay đổi nội dung fullname
Để thay đổi nội dung fullName trong JWT, chúng ta cần tạo một JWT mới với thông tin đã được cập nhật Ví dụ, JWT mới có thể có nội dung như sau: eyJhbGciOiJIUzUxMiJ9.eyJmdWxsTmFtZSI6InR1bmdsYTIwMjk5IiwiaWQiOiIyIiwiZ (tiếp tục với phần còn lại của JWT) Sau khi thực hiện, chúng ta sẽ nhận được JWT với nội dung mới, cho phép xác thực người dùng với thông tin đã chỉnh sửa.
- Thực thi chức năng xóa với JWT mới vào gói packet được bắt trong Burp Suite
Hình 48: Thực thi chức năng xóa
- Response phản hồi Invalid Username and Invalid Password => Server đã lọc JWT với Signature và phản hồi JWT Invalid
- Server phản hồi Expired JWT token.
Hình 50: Server phản hồi JWT hết hạn
- Tấn công JWT với chức năng thêm mới
Hình 52: Burpsuite bắt gói tin khi mình gửi request tạo project mới
- Hệ thống phản hồi lại JWT Invalid
Hình 53: Hệ thống phản hồi lại invalid JWT
- Do Time Out JWT quá hạn
- Thử lại với JWT đúng của hệ thống
Hình 55: Save thành công hệ thống với JWT được tạo cho request của client
JSON Web Tokens (JWTs) là một giải pháp nhẹ và linh hoạt cho việc xác thực ủy quyền trên nhiều nền tảng và ngôn ngữ Chúng cung cấp một phương thức thông minh để xác thực mà không cần duy trì phiên làm việc Có nhiều thư viện JWT có sẵn để hỗ trợ việc ký và xác minh mã thông báo Việc sử dụng mã thông báo mang lại nhiều lợi ích, và Auth0 có thể giúp triển khai xác thực mã thông báo một cách dễ dàng Tuy nhiên, không có một phương pháp duy nhất nào phù hợp với tất cả, mà điều này phụ thuộc vào kiến trúc ứng dụng và các trường hợp sử dụng cụ thể.
Kết thúc học phần Chuyên Đề An Ninh Mạng, nhóm 13 chúng em đã hoàn thành báo cáo
Kết quả, báo cáo đã đạt được những nội dung cụ thể như sau:
- Nắm được khái quát về Token-based authentication
- Nắm được khái niệm về JWT, cấu trúc của 1 JWT
- Nắm được luồng xử lý của 1 hệ thống bảo mật sử dụng JWT
- Nắm được sự khác biệt giữa JWT và Cookie
- Tìm hiểu về ứng dụng trong Fintech sử dụng JWT
- Tiến hành demo ứng dụng authentication và authorization với ReactJS và Spring Boot sử dụng JWT
- Tìm hiểu và tiến hành demo tấn công JWT
Mặc dù nhóm 13 đã nỗ lực hết mình, chúng em vẫn không thể tránh khỏi một số sai sót trong bài báo cáo Vì vậy, chúng em rất mong nhận được sự chỉ bảo và ý kiến đóng góp từ thầy/cô để cải thiện báo cáo của mình.