ducdev
Bảo mật / Bài viết

JWT Authentication — Cách Hoạt Động Và Những Sai Lầm Phổ Biến

JWT được dùng ở khắp nơi nhưng cũng bị implement sai ở khắp nơi. Hiểu cấu trúc, cách verify, và những pitfalls cần tránh khi dùng JWT cho authentication.

a
admin
06/06/2026 · 3 phút đọc · 0 lượt xem
Chia sẻ

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ý.

#JWT #Authentication #bao mat #backend #API
a
Tác giả
admin

Lập trình viên, yêu thích chia sẻ kiến thức về công nghệ và phát triển phần mềm.

Bình luận

Chưa có bình luận. Hãy là người đầu tiên!

Để lại bình luận

Bình luận sẽ được duyệt trước khi hiển thị.