JSON Web Token xuất hiện ở mọi tutorial về API authentication, nhưng đi kèm với đó là vô số cách implement sai có thể dẫn đến lỗ hổng bảo mật nghiêm trọng. Hãy hiểu đúng từ gốc.
JWT Là Gì — Cấu Trúc
JWT là một chuỗi gồm 3 phần cách nhau bởi dấu chấm:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. ← Header (Base64)
eyJ1c2VyX2lkIjoxLCJleHAiOjE3MDAwMDB9. ← Payload (Base64)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature
# Header — algorithm và type
{
"alg": "HS256", # HMAC-SHA256
"typ": "JWT"
}
# Payload — claims
{
"user_id": 1,
"email": "duc@example.com",
"exp": 1700000000, # Expiration (Unix timestamp)
"iat": 1699996400, # Issued at
"jti": "unique-token-id" # JWT ID — để revoke
}
# Signature
HMACSHA256(
base64url(header) + "." + base64url(payload),
secret_key
)
Verify JWT Đúng Cách
import jwt
from datetime import datetime, timedelta, timezone
SECRET_KEY = "your-very-secret-key-256-bits-minimum"
ALGORITHM = "HS256"
def create_token(user_id: int) -> str:
payload = {
"user_id": user_id,
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
"iat": datetime.now(timezone.utc),
}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
def verify_token(token: str) -> dict:
try:
# jwt.decode tự verify signature VÀ expiration
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
raise AuthError("Token đã hết hạn")
except jwt.InvalidTokenError:
raise AuthError("Token không hợp lệ")
Refresh Token Pattern
# Access token: thời hạn ngắn (15 phút - 1 giờ)
# Refresh token: thời hạn dài (7-30 ngày), lưu trong DB
def login(email: str, password: str):
user = authenticate(email, password)
access_token = create_token(user.id, expires_in=timedelta(hours=1))
refresh_token = create_refresh_token(user.id) # Lưu vào DB
return {
"access_token": access_token,
"refresh_token": refresh_token
}
def refresh_access_token(refresh_token: str):
# Verify refresh token từ DB
stored = RefreshToken.objects.get(token=refresh_token, revoked=False)
if stored.expires_at < timezone.now():
raise AuthError("Refresh token hết hạn")
# Cấp access token mới
return create_token(stored.user_id)
Những Sai Lầm Phổ Biến
1. Dùng algorithm "none"
# Cực kỳ nguy hiểm — hacker có thể forge token
# Luôn chỉ định algorithm khi verify
jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) # ✓
jwt.decode(token, SECRET_KEY, algorithms=["none"]) # Lỗi bảo mật! ✗
2. Lưu sensitive data trong payload
# Payload chỉ encoded (Base64), không encrypted!
# Ai cũng decode được mà không cần secret key
# Sai — đừng lưu thế này
{ "password": "hash...", "credit_card": "1234..." }
# Đúng — chỉ lưu ID, verify trong DB nếu cần thêm info
{ "user_id": 1 }
3. Secret key yếu
# Sai — quá ngắn, dễ brute force
SECRET_KEY = "secret"
# Đúng — ít nhất 256-bit random
import secrets
SECRET_KEY = secrets.token_hex(32) # 64 hex chars = 256 bits
JWT không có server-side state — không thể "logout" bằng cách xóa token phía server. Giải pháp: dùng token thời hạn ngắn + refresh token trong DB, hoặc maintain blacklist với Redis.
Kết Luận
JWT phù hợp cho stateless API authentication, đặc biệt microservices và mobile apps. Không phù hợp khi cần revoke token tức thì (ví dụ: user bị ban). Luôn dùng HTTPS, secret key đủ mạnh, và expiration hợp lý.
Chưa có bình luận. Hãy là người đầu tiên!