Problem
Sau khi login, client cần lưu token để gửi kèm mỗi request.
Câu hỏi: lưu ở đâu vừa an toàn vừa tiện?
Không có lựa chọn “perfect” → chỉ có trade-off giữa XSS vs CSRF vs UX vs scale
3 cách phổ biến
1/ LocalStorage / SessionStorage
Cách hoạt động
-
Lưu JWT trong
localStorage
-
Gửi qua header:
Ưu điểm
-
Dễ dùng (SPA, React, mobile web)
-
Không bị CSRF (không auto gửi)
-
Phù hợp API / microservices
Nhược điểm
-
Dễ bị XSS → attacker có thể đọc token
-
Token leak = full account takeover
-
Không có built-in protection
Use case
-
SPA (React / Next.js)
-
Mobile app
-
Internal tools (low risk)
2/ Cookie (HttpOnly + Secure)
Cách hoạt động
-
Browser tự gửi cookie mỗi request
Ưu điểm
-
✔ Không bị XSS đọc được (HttpOnly)
-
✔ Tự động gửi → đơn giản cho SSR/web app
-
✔ Phù hợp session-based auth
Nhược điểm
-
Dễ bị CSRF
-
Phải xử lý:
-
Khó dùng với API cross-domain
Use case
-
Web app truyền thống (SSR)
-
Banking / security-sensitive app
-
Cookie-based auth
In-memory (JS memory)
Cách hoạt động
-
Token chỉ lưu trong biến JS
-
Reload → mất token
Ưu điểm
-
Không persistent → giảm risk leak
-
Khó bị XSS steal lâu dài
Nhược điểm
-
Refresh page → logout
-
UX kém
-
Cần refresh token flow
Use case
-
High-security apps
-
Kết hợp refresh token
So sánh nhanh
| Storage | XSS | CSRF | Persistence | DX | Use case |
| LocalStorage / SessionStorage | High risk | Safe | yes | Easy | SPA |
| Cookie HttpOnly | Safe | Risk | yes | Medium | Web app |
| Memory | Low | Safe | no | Hard | High security |
Threat Matrix
Case Study (Thực tế)
Case 1 — SPA (React + API)
Chọn:
-
Access token → memory / localStorage
-
Refresh token → HttpOnly cookie
Login → set refresh cookie
→ access token in memory
→ expire → call refresh
Balance:
-
XSS risk giảm (access token short-lived)
-
CSRF controlled (refresh endpoint)
Case 2 — Banking App
Chọn:
-
Session-based
-
Cookie HttpOnly + SameSite Strict
-
Security > DX
-
Có thể revoke ngay
-
Không chấp nhận token leak
Case 3 — Microservices API
Chọn:
-
JWT trong header (localStorage / mobile secure storage)
-
Stateless
-
Cross-domain
-
Scale tốt
Best Practice (Production)
Recommended Hybrid
-
Access Token:
-
Refresh Token:
Security Checklist
-
Use
HttpOnly, Secure, SameSite
-
Rotate refresh token
-
Use CSP (Content Security Policy)
-
Sanitize input (prevent XSS)
-
Use CSRF token nếu dùng cookie
Final Takeaway
Không có nơi nào “an toàn tuyệt đối”
-
LocalStorage / SessionStorage → dễ nhưng rủi ro XSS
-
Cookie → an toàn XSS nhưng rủi ro CSRF
-
Memory → an toàn hơn nhưng UX kém
Hybrid approach + short-lived tokens + defense in depth