Lưu ý: Bài viết này nói trong khuôn khổ OAuth 2.0 được mô tả tại rfc6749.
Xin chào, chuyện là mấy ngày hôm nay tôi có theo dõi cuộc tranh luận của nhiều người trên mạng về vấn đề sử dụng access token và refresh token như thế nào thì hợp lý. Đọc một lúc thì nhận ra có 2 trường phái rõ rệt: Người thì bảo chỉ cần triển khai access token thôi là đủ. Người thì bảo không ổn, cần phải thêm cả refresh token nữa mới bảo mật. Nhưng nổi trội hơn cả là câu hỏi: Cứ cho là có cả access token và refresh token đi! Thì nên lưu chúng ở đâu trong trình duyệt để đảm bảo độ an toàn tuyệt đối?
Thế là sau nhiều giờ ngồi đọc hết bình luận của mọi người tôi đã tổng hợp được khá nhiều kiến thức. Cả những điều trước mà trước làm không tối ưu cho lắm. Vậy nên chúng ta hãy ngồi lại, cùng nhau đi tìm câu trả lời nhé!
Access token không cần phải giới thiệu gì nhiều nữa, access token là một chuỗi ký tự được dùng để xác minh hoặc đại diện cho một người dùng trong hệ thống. Một ví dụ dễ nhận thấy nhất là khi đăng nhập vào trang web nào đó, hệ thống gửi lại một đoạn mã, nhiệm vụ là lưu lại đoạn mã đó cho mỗi lần gọi API sau đó.
Ngày trước, khi PHP còn thịnh hành, các trang web xây dựng bằng PHP chủ yếu được kết xuất (render) phía máy chủ. Thứ mà chúng ta nghe nhắc đến nhiều hơn lúc đó là Session & Cookie. Máy chủ trả về trạng thái xác định một phiên đăng nhập cho trình duyệt, thường là một giá trị của Cookie. Khi truy vấn được gửi đi, Cookie kèm theo giúp máy chủ xác định được lại được phiên của người dùng. Ngày nay, nhờ sự phát triển của nhiều thư viện kết xuất phía máy khách như React, Vue, Svelte... mà chúng ta dần chuyển sang một cơ chế xác nhận phiên mới, đó là sử dụng access token.
Vì vậy về cơ bản, access token cũng chỉ là một đoạn mã được máy chủ trả về nhằm xác định một phiên cho máy khách. Access token có thể lưu lại bất kỳ đâu có thể lưu trữ như Cookie, LocalStorage, SessionStorage... hay trong một tệp tin nào đó. Thế còn refresh token thì sao? Nó có vai trò gì trong hệ thống? Hiểu một cách đơn giản, refresh token được dùng để xin cấp lại một access token mới.
Chờ đã, tại sao lại phải cấp access token mới? Vậy là nó có thời hạn sử dụng phải không? Chúc mừng, bạn đã hiểu đúng vấn đề. Nhưng tại sao phải rối rắm như vậy, chẳng phải chỉ cần như Session & Cookie như trên là đã đủ rồi sao? Nếu muốn biết lý do, trước hết chúng ta hãy tìm hiểu về bài toán uỷ quyền.
Đối với nhiều trang web, tính năng đăng ký/đăng nhập là điều không thể thiếu vì đó là cách tốt nhất để xác định được một người dùng trong hệ thống. Để đăng nhập thì cần thông tin đăng nhập như email, số điện thoại... kèm theo mật khẩu. Nhưng hàng ngày chúng ta không chỉ dùng một trang web mà có thể là rất nhiều, mỗi trang lại yêu cầu thông tin đăng nhập khác nhau, dần dần nó nhiều đến mức không thể nhớ được hết. Thật tuyệt vời nếu như chỉ cần có một tài khoản mà đăng nhập được ở tất cả mọi nơi. Đó là lúc mà Google, Facebook... xuất hiện. Đã bao giờ bạn để ý đến các nút với lời tựa "Đăng nhập bằng tài khoản Google" chưa? Bấm vào, trang web chuyển hướng sang Google, xác nhận cấp quyền cho phép truy cập thế là quá trình đăng nhập hoàn tất, quá nhanh và nguy hiểm phải không nào! Hầu hết hình thức đăng nhập như thế đều dựa trên đặc tả của OAuth 2.
Với OAuth 2, bạn có thể dễ dàng sử dụng nền tảng khác để đăng nhập vào một trang web có hỗ trợ, đó gọi là uỷ quyền. Uỷ quyền cho một trang web đọc được thông tin tài khoản như họ tên, địa chỉ email, số điện thoại... từ đó biết được bạn là ai và thiết lập một phiên sử dụng mới. Về mặt kỹ thuật, sau khi xác minh được danh tính, máy chủ sẽ quay về luồng xử lý như đăng nhập bằng email với mật khẩu thông thường, chỉ khác là không cần nhập bất kỳ thông tin gì nữa, chỉ cần xác nhận với bên uỷ quyền.
Access token xuất hiện trong đặc tả của OAuth 2. Access token được trả về ngay sau khi uỷ quyền. Tức là ngay khi bấm nút cho phép quyền truy cập, access token được trả về cho trang web đang đăng nhập. Dựa vào đó mà trang web xác thực được bạn là một người dùng trong hệ thống của họ. Tương tự refresh token cũng xuất hiện trong đặc tả của OAuth 2. Access token thường có thời gian tồn tại (TTL) rất ngắn, trong khi refresh token thì thường dài hơn. Khi access token hết hạn, cần phải dùng refresh token để xin một access token mới. Cứ như thế cho đến khi refresh token bị vô hiệu hoá thì không thể xin cấp thêm được nữa.
Ngày nay có rất nhiều trang web xây dựng hệ thống đăng nhập theo đặc tả của OAuth 2 hoặc ít nhất là gần giống. Ngay khi đăng nhập thành công, hệ thống trả về refresh token và access token. Việc của lập trình viên là phải lưu lại 2 thông tin quan trọng này trong trình duyệt để sử dụng cho cuộc gọi mỗi lần sau đó. Đồng thời họ phải xử lý hết hạn, làm mới, vô hiệu hoá... mã truy cập.
Do đó nắm được access token gần như là có luôn tài khoản trong thống nào. Việc bị lộ mã access token là rất nguy hiểm bởi vì kẻ tấn công có thể mạo danh người dùng tương tác với hệ thống mà không hay biết. Chính vì lý do đó access token cần được lưu trữ hoặc bảo vệ một cách nghiêm ngặt nhất có thể. Vậy làm sao để bảo vệ nó tốt nhất? Trước tiên hãy phân tích xem access token có cấu tạo như thế nào.
Access token có thể là một chuỗi ký tự bất kỳ. Nhưng hầu hết chúng thường là mã JWT (Json Web Token). Nó là một tiêu chuẩn để truyền thông tin an toàn giữa các bên với dữ liệu là một đối tượng JSON. Thông tin này có tính tin cậy cao bởi vì nó đã được ký (sign) bằng các thuật toán như HMAC, RSA hoặc ECDSA.
Một đối tượng JWT gồm có 3 phần là header
, payload
và signature
. Trong đó header
giữ các thông tin cơ bản về chuỗi JWT như thuật toán dùng để ký, hạn sử dụng... payload
là một đối tượng JSON đã được base64 encode. Cuối cùng signature
là phần mã hóa của header
và payload
kết hợp với khoá bí mật cùng thuật toán mã hóa đã được chỉ định ở header
.
Để tìm hiểu kỹ hơn về JWT, bạn đọc tham khảo Introduction to JSON Web Tokens.
Nội dung trong payload
có thể giống như ở dưới đây:
{
"id" : 1,
"username": "hoaitx"
}
Khi access token được gửi lên, máy chủ xác nhận tính hợp lệ và bắt đầu đọc thông tin trong payload
. Nhờ thế mà biết được người dùng đang sử dụng là ai.
Có một điều đáng chú ý là thông tin trong payload
thường chỉ được mã hóa bằng base64
, điều đó có nghĩa từ mã JWT có thể trích xuất được thông tin có trong payload
. payload
chỉ nên chứa thông tin đủ để xác định người dùng, payload
không nên chứa thông tin nhạy cảm của người dùng như mật khẩu, số tài khoản...
Như đã nói, access token thường có thời hạn ngắn (để giảm rủi ro bảo mật nếu bị lộ). Khi access token hết hạn, thay vì yêu cầu người dùng đăng nhập lại, ứng dụng có thể dùng refresh token để xin một access token mới một cách tự động và an toàn, giúp trải nghiệm người dùng liền mạch hơn.
Access token mang đầy đủ dữ liệu bên trong, máy chủ không cần lưu trạng thái phiên. Khi nhận được mã truy cập, máy chủ chỉ cần kiểm tra chữ ký và giải mã để lấy thông tin trong payload
, không cần cơ sở dữ liệu nào tham gia vào quá trình này. Đây chính là đặc tính không trạng thái (stateless), giúp giảm tải bộ nhớ và truy vấn cho máy chủ, dễ dàng mở rộng quy mô ứng dụng.
Tuy nhiên, điểm mạnh cũng chính là điểm yếu của hệ thống thiết kế theo kiểu không trạng thái: không thể bị thu hồi hoặc thay đổi sau khi đã cấp phát. Nếu một access token bị rò rỉ, kẻ tấn công có thể dùng nó đó để truy cập tài nguyên cho đến khi hết hạn, máy chủ lúc này không thể can thiệp. Để giảm rủi ro này, access token thường được thiết kế chỉ tồn tại trong thời gian ngắn, từ vài phút cho đến vài giờ.
Nhưng nếu access token ngắn hạn như vậy, khi nó hết hạn, người dùng phải đăng nhập lại liên tục, gây ra trải nghiệm rất tệ. Đây chính là lý do cần đến refresh token. Refresh token đóng vai trò như một "vé làm mới" — cho phép ứng dụng xin cấp lại một access token mới một tự động, không cần yêu cầu người dùng đăng nhập lại. Khác với access token, refresh token thường được máy chủ quản lý trạng thái (ví dụ lưu trong cơ sở dữ liệu), vì vậy máy chủ có thể chủ động thu hồi hoặc vô hiệu hóa refresh token bất cứ lúc nào nếu cần (chẳng hạn khi người dùng đăng xuất hoặc có dấu hiệu bị đánh cắp).
Sau khi đã hiểu đặc tả OAuth 2, về cấu tạo vào chức năng của access token, cũng như sự cần thiết của refresh token. Chúng ta sẽ tiếp tục tìm hiểu cách lưu trữ chúng như thế nào cho an toàn và bảo mật nhé!
5 bài học sâu sắc
Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!
Đăng ký nhận thông báo bài viết mới
Bình luận (4)