0

jwt token

JWT, Access Token, Refresh Token – Tất cả những gì mình học được khi chuẩn bị phỏng vấn Fullstack

Khi bắt đầu ôn phỏng vấn Fullstack, mình nghĩ JWT chỉ đơn giản là:

  • Login
  • Trả về JWT
  • Frontend lưu token
  • Gọi API

Sau khi tìm hiểu sâu hơn, mình nhận ra phía sau JWT có rất nhiều bài toán như Session, Logout, Role, Refresh Token, Rotation, OAuth, Forgot Password...

Bài viết này tổng hợp toàn bộ kiến thức mình đã hệ thống lại.


1. Session và JWT khác nhau như thế nào?

Đây là câu hỏi gần như chắc chắn sẽ xuất hiện khi phỏng vấn.

Session

Client
    │
 Session ID
    │
 Backend
    │
 Session Store (Redis/DB)

Backend lưu toàn bộ trạng thái đăng nhập.

Mỗi request đều phải tìm Session trong Redis hoặc Database.

Ưu điểm

  • Logout rất dễ.
  • Thu hồi quyền ngay lập tức.
  • Dễ kiểm soát.

Nhược điểm

  • Stateful.
  • Khó scale hơn.
  • Phải có Session Store dùng chung nếu có nhiều server.

JWT

Client
    │
 JWT
    │
 Backend
    │
 Verify Signature

Backend không lưu Access Token.

Chỉ cần kiểm tra:

  • Chữ ký
  • Thời gian hết hạn

Nếu hợp lệ thì cho phép truy cập.

Ưu điểm

  • Stateless.
  • Không cần lưu Session.
  • Scale rất tốt.

Nhược điểm

  • Logout khó.
  • Không thu hồi Access Token ngay được.

2. JWT Stateless nghĩa là gì?

Đây là điều mình từng hiểu sai.

Stateless nghĩa là:

Backend không lưu trạng thái của Access Token.

Ví dụ

Frontend gửi

Authorization:
Bearer xxx

Backend chỉ làm

Verify Signature

↓

Token hết hạn chưa?

↓

OK

Không cần

SELECT ...

UPDATE ...

Redis ...

Đó mới là Stateless.


3. Access Token là gì?

Access Token chỉ có một nhiệm vụ.

Dùng để gọi API.

Ví dụ

GET /profile

Authorization:
Bearer xxx

Thường sống

  • 5 phút
  • 10 phút
  • 15 phút

Không nên quá dài.

Nếu bị hacker lấy được thì thiệt hại chỉ trong khoảng thời gian ngắn.


4. Refresh Token là gì?

Refresh Token không dùng để gọi API.

Nó chỉ có nhiệm vụ:

Xin Access Token mới.

Ví dụ

Access Token

15 phút

Hết hạn.

Frontend gửi

POST /refresh

Refresh Token

Backend kiểm tra Refresh Token.

Nếu hợp lệ

Sinh Access Token mới.

Refresh Token thường sống

  • 7 ngày
  • 30 ngày
  • 90 ngày

5. Tại sao phải tách Access Token và Refresh Token?

Nếu chỉ có một JWT sống 30 ngày.

Hacker lấy được.

Dùng được 30 ngày.

Nếu tách thành

Access Token

15 phút

+

Refresh Token

30 ngày

Thì nếu Access Token bị lộ.

Hacker chỉ dùng được tối đa khoảng 15 phút.

Đó là lý do Access Token luôn có thời gian sống ngắn.


6. Nếu Role thay đổi thì sao?

Giả sử

JWT

role = Admin

5 phút sau.

Admin khác sửa Database

Role = User

Access Token cũ vẫn chứa

Admin

đến khi hết hạn.

Đây là nhược điểm của việc lưu Role trong JWT.


7. Có những cách nào để xử lý Role?

Cách 1

Lưu Role trong JWT.

Ưu điểm

  • Không cần query Database.

Nhược điểm

  • Role cập nhật chậm.

Cách 2

JWT chỉ lưu UserId.

Mỗi request

Query Database

Lấy Role.

Ưu điểm

  • Role luôn đúng.

Nhược điểm

  • Request nào cũng query Database.

Cách 3

JWT lưu

userId

tokenVersion

Database cũng lưu tokenVersion.

Nếu khác nhau.

Reject.

Đây là cách nhiều hệ thống sử dụng để thu hồi token.


8. Nếu mỗi request đều query Database thì còn lợi ích gì của JWT?

Đây là câu hỏi rất hay.

Nếu request nào cũng

Verify JWT

↓

Query Database

thì lợi ích Stateless gần như mất đi.

Tuy nhiên JWT vẫn còn lợi ích:

  • Không cần lưu Session.
  • Dễ scale.
  • Không cần Redis Session.

Đó là lý do nhiều hệ thống vẫn dùng JWT kết hợp Database.


9. Logout hoạt động như thế nào?

Đây là câu hỏi mình từng trả lời sai.

Khi Logout.

Client sẽ

  • Xóa Access Token.
  • Xóa Refresh Token.

Sau đó gọi API

POST /logout

Backend

Revoke hoặc xóa Refresh Token.

Backend không cần xóa Access Token.

Vì backend không lưu Access Token.


10. Nếu hacker lấy được Access Token thì sao?

Giả sử

Access Token

15 phút

Người dùng Logout.

Refresh Token đã bị xóa.

Nhưng hacker vẫn giữ Access Token.

Backend không biết điều đó.

Vì JWT là Stateless.

Do đó hacker vẫn gọi API được cho đến khi Access Token hết hạn.


11. Muốn Logout ngay lập tức thì sao?

Có thể dùng

  • Blacklist
  • Token Version
  • Redis

Flow

Verify JWT

↓

Check Redis

↓

Reject

Nhưng lúc này.

JWT không còn Stateless hoàn toàn nữa.

Đây là Trade-off giữa hiệu năng và bảo mật.


12. Blacklist và Whitelist

Blacklist

Danh sách Token bị cấm.

Request

Verify JWT

Check Blacklist

Reject

Thường dùng cho Access Token.


Whitelist

Danh sách Token hợp lệ.

Request

Có trong Database?

OK

Không có

401

Thường dùng cho Refresh Token.


13. Nếu hacker lấy được Refresh Token thì sao?

Refresh Token nguy hiểm hơn Access Token.

Ví dụ

Refresh Token sống 30 ngày.

Nếu hacker lấy được.

Họ có thể liên tục gọi

POST /refresh

để xin Access Token mới.

Cho đến khi Refresh Token hết hạn hoặc bị Revoke.


14. Refresh Token Rotation

Không Rotation

RT-1

↓

Refresh

↓

RT-1

↓

Refresh

↓

RT-1

Một Refresh Token dùng suốt 30 ngày.


Có Rotation

RT-1

↓

Refresh

↓

RT-2

↓

Refresh

↓

RT-3

Refresh Token cũ bị Revoke.

Client phải lưu Refresh Token mới.

Rotation không làm Refresh Token mất chức năng.

Nó chỉ giúp giảm rủi ro nếu Refresh Token bị lộ.


15. Rotation có nghĩa Refresh Token hết hạn sau 15 phút không?

Không.

Đây là điều mình từng nhầm.

Rotation chỉ có nghĩa:

Refresh Token thay đổi.

Không có nghĩa:

Refresh Token chỉ sống 15 phút.

Thời gian hết hạn của Refresh Token là quyết định của hệ thống.

Có hai cách.

Fixed Expiration

Refresh Token đổi liên tục.

Nhưng ngày hết hạn vẫn giữ nguyên.

Ví dụ

01/07

↓

31/07

Sliding Expiration

Mỗi lần Refresh.

Gia hạn thêm 30 ngày.


16. Revoke là gì?

Revoke không nhất thiết là Delete.

Có thể

DELETE

hoặc

revoked = true

Mục tiêu đều giống nhau.

Token không còn sử dụng được.


17. Nhiều thiết bị đăng nhập

Một người dùng có thể đăng nhập

  • Điện thoại
  • Laptop
  • Tablet

Database thường lưu

  • user_id
  • refresh_token_hash
  • device
  • ip
  • expired_at

Nhờ đó có thể Logout từng thiết bị riêng.


18. Forgot Password

Flow

Email

↓

Reset Token

↓

Email

↓

Reset Password

↓

Delete/Revoke Token

Không bao giờ gửi Password qua Email.

Chỉ gửi Reset Token.

Reset Token phải:

  • Có thời hạn.
  • Chỉ dùng một lần.

Sau khi đổi Password.

Nên Revoke toàn bộ Refresh Token để buộc tất cả thiết bị đăng nhập lại.


19. Email Verification

Flow

Register

↓

Inactive

↓

Verify Token

↓

Click Email

↓

Active

Mục tiêu

  • Xác minh Email tồn tại.
  • Đảm bảo người đăng ký sở hữu Email đó.

Verify Token cũng nên:

  • Có thời hạn.
  • Chỉ dùng một lần.

20. Rate Limit

Ví dụ

Một IP hoặc một tài khoản.

Sai Password

5 lần/phút.

429 Too Many Requests.

Hoặc

Khóa tạm thời 15 phút.

Rate Limit giúp chống Brute Force Attack.


21. OAuth Login (Google, GitHub...)

Flow

Login Google

↓

Authorization Code

↓

Backend

↓

Google Access Token

↓

Google Profile

↓

User Database

↓

JWT của hệ thống

Điều quan trọng cần nhớ là có hai loại Access Token:

  • Access Token của Google.
  • Access Token của hệ thống.

Sau khi lấy Profile từ Google.

Backend vẫn phải tạo JWT riêng để quản lý quyền và nghiệp vụ của ứng dụng.


22. JWT hay Session?

Không có đáp án đúng tuyệt đối.

Nếu hệ thống cần

  • Scale tốt.
  • Microservice.
  • API.

JWT thường phù hợp.

Nếu hệ thống cần

  • Thu hồi quyền ngay.
  • Bảo mật rất cao.
  • Quản lý Session tập trung.

Session hoặc JWT kết hợp Database/Redis sẽ phù hợp hơn.


Kết luận

Điều quan trọng nhất mình học được sau khi tìm hiểu JWT không phải là cách tạo Token.

Mà là hiểu rằng mọi giải pháp đều có Trade-off.

  • Muốn Stateless thì chấp nhận Access Token không thể thu hồi ngay.
  • Muốn thu hồi ngay thì phải lưu trạng thái ở Redis hoặc Database.
  • Muốn người dùng ít phải đăng nhập lại thì cần Refresh Token.
  • Muốn Refresh Token an toàn hơn thì dùng Rotation.
  • Muốn bảo mật hơn nữa thì kết hợp HttpOnly Cookie, Rate Limit, MFA, Revoke và quản lý thiết bị.

Không có công nghệ nào hoàn hảo.

Điều quan trọng là chọn giải pháp phù hợp với yêu cầu của hệ thống.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí