HS256 vs RS256 vs ES256: Benchmark JWT verification

Có tuần tôi gặp một issue khá khó chịu: API không chậm rõ ràng, p95 vẫn ổn, nhưng CPU của gateway cứ leo dần theo traffic.

Thoáng qua thì tôi nghĩ do log, do TLS, do upstream. Nhưng nhìn kỹ flamegraph thì thấy một phần khá wow: JWT verify chiếm CPU nhiều hơn tôi tưởng, nhất là khi traffic lên 20k~40k req/s và mọi request đều phải auth.

Tính vốn thích trả lời bằng các con số nên quyết định làm bài benchmark HS256 vs RS256 vs ES256. Chắc chắn là không cần độ chính xác như paper, mà cần câu trả lời cho production: cái nào hợp, cái nào đang làm mình trả tiền CPU vô ích.

b7ae75cd-9431-4a10-9e43-5b2d2ccf4165

Định lưu document đăng sau, mà thấy dạo này cũng ít bài viết chia sẻ benchmark (vì cơ bản rất tốn công và phải có bài toán còn chắc ít ai tự nhiên benchmark) nên chia sẻ luôn tới mọi người, anh em trên này đều chia sẻ thật tình, thực tế. Có những ông anh 15+ exp vẫn thích viết bài chia sẻ vì vậy mà anh em cộng đồng chia sẻ, lan tỏa thì sẽ giúp cộng đồng DevOps Việt Nam chúng ta phát triển hơn. Xàm thế thôi vào này.

1. Bối cảnh thực tế (vì sao tôi phải benchmark)

Đây là một hệ thống trong hệ sinh thái và mô hình khá phổ biến (cho anh em nào chưa biết thì trong hệ sinh thái nhiều services không nhất thiết tất cả phải đều giống nhau nhé vì có những services hàng 10~20 năm trước):

  • 1 API Gateway nhận request
  • Verify JWT (Authorization: Bearer)
  • Route vào backend

Traffic lúc bình thường khoảng 8k~15k rps, nhưng có lúc spike lên 30k rps (bot, partner gọi webhook, hoặc job nội bộ chạy dồn).

Cơ bản thì mọi người có thể hay nghe câu:

  • HS256 nhanh nhưng không enterprise
  • RS256 là chuẩn OIDC
  • ES256 hiện đại, token nhỏ

Mà chúng ta làm việc logic và nếu đặc biệt tại các vị trí ra quyết định thì có một lý do hoàn chỉnh để trả lời câu hỏi “cái nào phù hợp” thì phải benchmark thực tế thôi.

2. Setup benchmark tôi dùng

Tôi chỉ làm setup tối giản để đo đúng phần JWT:

Hạ tầng (giả định phổ biến)

  • 1 máy loadgen: 8 vCPU/16GB
  • 1 máy API test: 8 vCPU/16GB
  • Cùng LAN, latency thấp (đỡ nhiễu network)

API test làm gì?

API rất đơn giản:

  1. Đọc header Authorization
  2. Verify chữ ký JWT
  3. Parse claims cơ bản (sub, exp, scope)
  4. Trả JSON nhỏ (200B)

Không DB, không gọi upstream để kết quả tránh nhiễu những nghiệp vụ không liên quan.

Token chuẩn hóa

  • Cùng payload claims (khoảng 10-12 fields)
  • exp 15 phút
  • kid (để mô phỏng key rotation)

Tool đo

  • bắn tải kiểu keep-alive, concurrency 200~1000
  • lấy p95/p99 và CPU của server

3. Điều tôi muốn biết trước tiên: verify 1 token tốn bao nhiêu CPU?

Tôi tách một micro-benchmark để ra độ nặng tương đối của từng thuật toán (cùng payload).

Thời gian verify

Thuật toán Thời gian verify / request (median) Ghi chú
HS256 (HMAC-SHA256) ~2-6 µs cực rẻ, chủ yếu là hash
RS256 (RSA-2048 + SHA256) ~60-140 µs verify RSA đắt hơn hash nhiều
ES256 (ECDSA P-256 + SHA256) ~90-220 µs thường còn đắt hơn RSA verify một chút

Bạn có thể thấy là nếu gateway của mọi người đang chạy 30k rps:

  • HS256: 30k * 5µs ≈ 0.15 CPU-seconds/second (rất nhẹ)
  • RS256: 30k * 100µs ≈ 3 CPU-seconds/second (tức ~3 core chỉ để verify)
  • ES256: 30k * 160µs ≈ 4.8 core (chỉ verify)

Và đó là chưa tính JSON encode, routing, log, TLS, metrics :=D

4. Benchmark end-to-end: khi traffic tăng thì cái nào gục trước?

Tôi chạy 3 mức tải đại diện:

  • 10k rps (bình thường)
  • 30k rps (spike)
  • 50k rps (cố tình ép)

Kết quả ở 30k rps (concurrency ~800)

Thuật toán p95 latency tăng thêm p99 latency tăng thêm CPU server Error rate
HS256 ~0.2-0.4ms ~0.6-1.0ms ~35-45% ~0%
RS256 ~1.5-2.8ms ~3.5-6.0ms ~75-90% ~0-0.2%
ES256 ~2.2-3.8ms ~5.0-9.0ms ~90-100% ~0.2-0.6%

Cảm nhận của tôi:

  • HS256 gần như “vô hình” trong tổng latency.
  • RS256 bắt đầu làm CPU nhảy rõ rệt, p99 xấu lên nhưng còn chịu được.
  • ES256 đẹp ở mặt “hiện đại”, nhưng verify đắt, nên khi spike thì nó làm gateway ngộp nhanh hơn.

Anh em nào chưa hiểu các con số p95, p99 là gì thì xem bài chọn p95 hay p99 hay p99.9 khi benchmark? của anh Long Bùi nhé siêu hot lúc ấy (mà giờ vẫn thế) cộng đồng share liên tục :=D

Kết quả khi tôi ép lên 50k rps

Thuật toán Trạng thái
HS256 vẫn giữ được, CPU ~65-75%, p99 ~2-4ms (phần auth)
RS256 bắt đầu timeout lác đác nếu không scale, p99 auth ~8-15ms
ES256 dễ chạm trần CPU, error tăng nếu không scale, p99 auth ~12-25ms

Ở mức này, tôi không còn nhìn cái nào hơn 10% nữa. Tôi nhìn kiểu SRE hơn:

Cái nào khiến gateway chạm trần CPU sớm hơn -> Cái nào làm tôi phải scale sớm hơn -> Cái nào làm tôi tốn tiền hơn.

5. Một cái bẫy cực phổ biến: JWKS fetch/key lookup làm benchmark là đổ sông đổ biển

Tôi phải nhắc đoạn này vì tôi thấy có khi nhiều team dính:

Trường hợp tốt (đúng)

  • RS256/ES256: fetch JWKS mỗi vài phút
  • Cache public key theo kid
  • Verify local

Trường hợp tự bắn vào chân

  • Mỗi request lại đi fetch JWKS hoặc gọi auth server để lấy key/validate

Nếu làm sai kiểu này thì:

  • p99 không tăng vài ms nữa, mà tăng vài chục ms đến vài trăm ms
  • Gateway chết vì network dependency, không còn là câu chuyện thuật toán

Trong benchmark của tôi, tôi giả định cache key đúng cách, vì theo tôi đó mới là cách nên làm.

6. Vậy cuối cùng tôi chọn gì?

Sau khi nhìn số liệu chúng ta chốt theo thực tế bài toán gặp phải thôi:

HS256

Tôi chọn HS256 nếu:

  • Token chỉ dùng nội bộ trong một trust domain (cùng công ty/cùng hệ)
  • Quản lý secret chặt (vault/kms), rotate được
  • Cần hiệu năng cao và muốn gateway xử lý nhẹ nhàng hơn

Tuy nhiên, điểm yếu mà mọi người phải chấp nhận: HS256 dùng shared secret -> service nào verify được thì cũng có thể sign nếu bị lộ secret. Vì vậy HS256 hợp nội bộ, không hợp kiểu nhiều bên cùng tham gia.

RS256

Tôi chọn RS256 nếu:

  • Dùng OIDC/IdP tiêu chuẩn, JWKS rotation rõ ràng
  • Muốn tách quyền sign (private key) và quyền verify (public key)
  • Chấp nhận tốn CPU hơn một chút để có mô hình bảo mật sạch

Trong benchmark của tôi: RS256 là điểm cân bằng, đắt hơn HS256 rõ rệt, nhưng vẫn dễ sống nếu scale hợp lý.

ES256

Tôi chọn ES256 khi:

  • Thật sự quan tâm token size (mobile/edge/network đắt)
  • Muốn crypto hiện đại
  • Và tôi đủ tài nguyên hoặc auth không phải bottleneck lớn

Nhưng không ảo tưởng: ES256 verify thường đắt hơn RS256, nên nếu gateway của mọi người đang ở ngưỡng CPU cao, ES256 có thể làm mọi người scale sớm hơn.

7. 3 bài học mà tôi thấy ai cũng nên biết

  1. Đừng chọn thuật toán vì “nghe chuẩn”, thời đại AI mọi người có thể có thông tin nhanh chóng, nhưng mọi người dùng AI cũng biết rồi, nếu thực sự không truyền đủ bối cảnh vào thì câu trả lời của nó sẽ rất chung chung và áp dụng có khi đi cả hệ thống nên chọn vì: mô hình trust + chi phí CPU + cách triển khai key cache.

  2. Trong hệ traffic lớn, auth không còn là “feature”, nó là “hạ tầng”: 1-2ms thêm vào p99 ở gateway là đủ để bạn phải scale thêm vài node.

  3. Key cache quan trọng không kém thuật toán RS/ES mà cache key tệ thì p99 sẽ xấu kinh khủng, và benchmark thuật toán không còn ý nghĩa.

8. Nếu mọi người muốn benchmark nhanh cho hệ của mình

Nên làm tối thiểu 2 bài test:

  • 10k rps và 30k rps (TLS on, keep-alive)
  • Đo p99 + CPU gateway
  • test thêm 1 lần cache miss key (giả lập rotate) xem p99 nhảy bao nhiêu

Vậy là đủ ra quyết định rất nhanh rồi.

Thông tin nổi bật

Sự kiện phát trực tiếp​

Event Thumbnail

Báo cáo quan trọng

Article Thumbnail
Article Thumbnail

Sự kiện đang hiện hành

Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận

Tiêu điểm chuyên gia