Tại sao cần Refresh Token? Bạn đã biết cách lưu trữ Refresh Token và Access Token an toàn trong trình duyệt chưa? Phần 2

Tại sao cần Refresh Token? Bạn đã biết cách lưu trữ Refresh Token và Access Token an toàn trong trình duyệt chưa? Phần 2

Những mẩu tin ngắn hàng ngày dành cho bạn
  • smee.io là một cách đơn giản để tạo một địa chỉ webhook và ánh xạ nó về địa chỉ localhost trong máy tính của bạn.

    $ npm install --global smee-client $ smee -u https://smee.io/eu4UoW8vrKSZtTB

    » Xem thêm
  • javascript.tm được Ryan Dahl - cha đẻ của Node.js tạo ra với mục tiêu trình bày và kêu gọi cộng đồng ủng hộ việc Oracle "giải phóng" cái tên JavaScript, trả lại nó cho cộng đồng.

    Anh cho rằng Oracle sau nhiều thương vụ mua bán đã gián tiếp sở hữu cái tên JavaScript, nhưng họ lại không sử dụng cái tên này. Một điều nữa là JavaScript đã trở thành tên gọi chung, cộng đồng sử dụng rất nhiều mà ít ai biết rằng nó thuộc về Oracle.

    Việc Oracle nắm giữ thương hiệu này gây ra sự nhầm lẫn không cần thiết. Anh kêu gọi Oracle từ bỏ quyền sở hữu, trả nó về với cộng đồng. Nếu được như vậy, nhiều hiểu lầm được xoá bỏ và mọi người sẽ có nhiều quyền hạn với cái tên này. Ví dụ, chúng ta sẽ có một "Hội nghị về JavaScript" trong tương lai mà không phải lo lắng về vấn đề pháp lý.

    Thông tin mới nhất, Vào ngày 22 tháng 11 năm 2024: Anh đã nộp đơn lên USPTO. Chúng ta cùng chờ xem kết quả ra sao nhé! Ngoài ra bạn cũng có thể bày tỏ sự ủng hộ bằng cách truy cập vào địa chỉ ở trên và tiến hành "ký" vào lá đơn 😇

    » Xem thêm
  • Nhớ mấy ngày đầu đi phỏng vấn, có một câu hỏi là JavaScript có phải là Java không? Thì mình trả lời ngay là có 😆. Buồn cười thật, vì đúng ra JavaScript và Java là hai ngôn ngữ khác nhau hoàn toàn.

    Sự nhầm lẫn này mà mới đây đã dẫn đến vụ kiện đòi Oracle trả lại cái tên JavaScript cho cộng đồng. Oracle? Đúng vậy, cái tên liên quan rất mật thiết đến Java, nhưng tại sao lại kiện họ với JavaScript cơ chứ?

    Hic, hôm nay làm việc mệt mỏi quá nên xin phép ngày mai mình kể chi tiết hơn nha vì câu chuyện khá dài đấy 😅

    » Xem thêm

Vấn đề

Xem phần trước: Tại sao cần Refresh Token? Bạn đã biết cách lưu trữ Refresh Token và Access Token an toàn trong trình duyệt chưa?.

Ở bài viết trước chúng ta đã biết tại sao nên dùng Refresh Token. Vấn đề đặt ra là nếu Access Token bị đánh cắp thì khả năng Refresh Token "không cánh mà bay" cũng là rất cao. Vậy thì việc sinh ra Refresh Token đâu có ý nghĩa gì nữa khi cơ hội đánh cắp cả hai là như nhau?

Để tìm ra câu trả lời cho vấn đề trên, có rất nhiều cuộc tranh luận đã được nổ ra và dường như chưa có hồi kết bởi vì ai cũng có ý đúng. Tôi đóng vai trò như là một khán giả ngồi trên khán đài của sân bóng, tiếp thu ý kiến của họ để từ đó tổng hợp lại và đưa ra câu trả lời mà tôi thấy là tâm đắc nhất. Dĩ nhiên nó không hoàn toàn chính xác và tôi hy vọng bạn đọc tìm ra được điều bất hợp lý trong này.

Refresh Token lưu ở đâu?

Refresh Token lưu ở đâu?

Câu trả lời cuối cùng của tôi là "trong trình duyệt". Tùy thuộc vào mức độ bảo mật mà bạn hướng tới để lựa chọn ra phương án phù hợp nhất giúp tiết kiệm thời gian và cả chi phí. Bởi bên trong trình duyệt cung cấp một số cách lưu trữ như là Cookie, localStorage, sessionStorage hay thậm chí có thêm một cách lưu trữ phía máy chủ nữa là Session. Mỗi cách đều có logic nghiệp vụ riêng, việc khéo léo tổ chức lưu trữ Access Token và Refresh Token trong đó giúp giảm thiểu được phần nào tính nghiêm trọng của cuộc tấn công XSS.

Một sự thật là nếu trang web của bạn bị tấn công XSS thì phải nói rằng dù có lưu trữ ở đâu thì khả năng Access Token và Refresh Token bị rò rỉ là rất lớn. Để cho dễ hình dung, hãy mổ xẻ bản chất của cuộc tấn công XSS. Tấn công XSS xảy ra khi kẻ tấn công có thể chạy mã JavaScript trên trang web của bạn. Điều này có nghĩa là kẻ tấn công có thể lấy mã thông báo truy cập mà bạn đã lưu trữ bất kì đâu mà mã Javascript có quyền truy cập. Một cuộc tấn công XSS có thể xảy ra từ mã Javascript của bên thứ ba có trong trang web của bạn như React, Vue, jQuery, Google Analytics, v.v... Những thư viện càng phổ biến càng là tâm điểm của sự chú ý, một khi chúng bị tấn công thì thiệt hại phải nói là vô cùng lớn.

Rõ ràng là việc không sử dụng bất kì một thư viện của bên thứ ba nào trong trang web của bạn là rất khó. Mỗi thư viện được thêm vào đồng nghĩa với rủi ro tăng lên. Kẻ tấn công cũng có thể khai thác XSS dựa vào các form/input thông tin trên trang web của bạn. Ví dụ tính năng chat công khai, kẻ tấn công có thể nhập vào một đoạn mã Javascript đánh cắp dữ liệu. Nếu không xử lý đúng cách phần hiển thị nội dung chat, những người dùng khác khi truy cập vào rất dễ bị dính mã độc. Một cuộc tấn công dò tìm MIME cũng có khả năng gây ra một cuộc tấn công XSS.

Song song với XSS là một cuộc tấn công CSRF (Cross Site Request Forgery) nhằm vào Access Token. Nó được hiểu là một cuộc tấn công buộc người dùng phải thực hiện một yêu cầu ngoài ý muốn. Kẻ tấn công không trực tiếp đánh cắp Access Token hay Refresh Token mà chúng muốn lợi dụng Access Token được đính kèm lên máy chủ trong mỗi request của bạn.

Ví dụ trang web cho phép thay đổi email người dùng thông qua API:

POST /email/change HTTP/1.1 
Host: yoursite.com 
Content-Type: application/x-www-form-urlencoded 
Cookie: sessionID=qwerty 

[email protected]

Kẻ tấn công tạo một form tự động gửi yêu cầu POST đến /email/change đính kèm email của hắn. Lúc đó session sẽ được tự động thêm vào yêu cầu vì bạn đang trực tiếp truy cập vào trang web, chỉ có điều bạn không biết sự có mặt của form ẩn đó. Tuy nhiên, điều này có thể được giảm thiểu bằng cách sử dụng `sameSite`.

Từ đó suy ra nếu muốn không bị rò rỉ Access Token và Refresh Token thì phải lưu trữ chúng ở nơi mà mã Javascript không có quyền truy cập. Trong trình duyệt có nơi nào như thế không? Xin thưa là có đấy, đó chính là `httpOnly` cookie. Nhưng liệu httpOnly có phải là phương án cuối cùng? Trước khi trả lời, tôi muốn các bạn hiểu rõ hơn về các cơ chế lưu trữ phổ biến trong trình duyệt và cả máy chủ.

Session Cookie

Session là một trong những cách xác định phiên người dùng phổ biến nhất của trang web server side render. Cách thức hoạt động khá dễ hiểu, server sinh ra một session ID cho request yêu cầu xác định phiên, trình duyệt lưu lại ID đó và gửi kèm lên trong tất cả các request sau đó.

Phương pháp này yêu cầu máy chủ phải lưu trữ và quản lý phiên của từng máy khách. Session ID thông thường sẽ được client lưu trữ trong Cookie hoặc tham số trong URL. Điều này vẫn khiến nó có thể bị đánh cắp thông qua một cuộc tấn công XSS.

Cookie

Không cần phải nói nhiều nữa, Cookie là thông tin được lưu trữ trên máy tính của bạn bởi một trang web bạn truy cập. Sử dụng Cookie để lưu lại thông tin bạn cần trên một trang web như thông tin người dùng đăng nhập... Có thể nói Cookie là một trong những phương pháp lâu đời nhất được sử dụng nhằm mục đích lưu trữ này. Những thông tin được lưu trữ trong Cookie được gửi lên máy chủ thông qua headers của request nhờ đó máy chủ có thể xác định được phiên của người dùng.

Javascript có quyền truy cập vào Cookie thế nên chúng vẫn có thể bị đánh cắp bởi một cuộc tấn công XSS.

Tuy nhiên, đối với Cookie có một khái niệm là httpOnly. Nếu 1 cookie được chỉ định thêm tùy chọn httpOnly thì nó miễn nhiễm với Javascript. Nghĩa là Javascript không có quyền truy cập vào httpOnly mà cookie đó tự động được gửi lên trong mỗi yêu cầu đến máy chủ.

localStorage và sessionStorage

Đây đều là hai cách lưu trữ thông tin với dung lượng lớn hơn Cookie rất nhiều, chính vì thế chúng thường được dùng để lưu lại thông tin có kích thước lớn hoặc là bộ nhớ cache cho các tính năng của trang web để chúng chạy mượt mà hơn.

Thông tin trên localStorage được lưu trữ bền vững cho đến khi bạn xóa chúng. Ngược lại, sessionStorage thường sẽ bị xóa ngay sau khi bạn kết thúc phiên làm việc với trang web (tắt tab, tắt trình duyệt...).

Javascript có quyền truy cập vào cả hai thế nên chúng vẫn có thể bị đánh cắp bởi một cuộc tấn công XSS.

Từ đó suy ra những cách khác nhau mà bạn có thể lưu trữ:

  • Lưu trữ Access Token và Refresh Token ở localStorage hoặc cookie -> dễ bị CSRF, bị đánh cắp thông qua XSS.
  • Lưu trữ cả hai trong httpOnly cookie -> dễ bị CSRF nhưng có thể tránh được. Tốt hơn so với phương án đầu tiên.
  • Chỉ lưu trữ Refresh Token trong httpOnly cookie -> an toàn khỏi CSRF. Mặc dù Access Token có thể bị đánh cắp tuy nhiên chúng chỉ tồn tại trong thời gian ngắn nên cũng có thể giảm thiểu được mức độ rủi ro.

Có thể thấy cách thứ ba tỏ ra được ưu thế hơn cả hai cách đầu. Cách để thiết lập không quá phức tạp. Chỉ cần lúc đăng nhập hay ủy quyền thì Access Token được trả về trong body response còn Refresh Token chỉ được trả về thông qua httpOnly cookie.

Trên đây là cách lưu trữ để hạn chế tối đa việc Access Token và Refresh Token bị rò rỉ. Trong trường hợp Access Token và Refresh Token bị rò rỉ thì có cách nào để phát hiện hoặc ngăn chặn không? Tôi sẽ tiếp tục có bài viết trong series này, cảm ơn các bạn đã theo dõi.

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

Bình luận (4)

Nội dung bình luận...
Avatar
Nguyen Danh5 tháng trước
PA2 - Lưu trữ cả hai trong httpOnly cookie -> dễ bị CSRF nhưng có thể tránh được. PA3 - Chỉ lưu trữ Refresh Token trong httpOnly cookie -> an toàn khỏi CSRF. Vì sao đều lưu ở httpOnly cookie nhưng PA2 thì dễ bị CSRF còn PA3 thì an toàn ???
Trả lời
Avatar
Trần Ngọc Hải2 năm trước
Thật ra ko thể tránh khỏi việc dữ liệu bị đánh cắp cốt là cách xử lý sau khi bị đánh cắp như thế nào cho giảm thiểu tối đa rủi ro
Trả lời
Avatar
Nguyễn Văn Nhật2 năm trước
M có đọc được lời khuyên là nên lưu vào bộ nhớ ứng dụng ví dụ như reduce hoặc một biến nào đó khởi tạo trong ứng dụng. Ví dụ như là const token = 'xxx'
Trả lời
Avatar
Xuân Hoài Tống2 năm trước
Người dùng vẫn có thể đột ngột tắt trình duyệt hoặc hệ điều hành cũng có quyền kết thúc tiến trình thế nên bạn sẽ không kịp lưu lại được
Avatar
Nguyễn Văn Nhật2 năm trước
Trước khi reload mình sẽ lưu lại vào localStorage, nó giống như kiểu hiện 1 alert khi bấm vào reload vậy
Avatar
Xuân Hoài Tống2 năm trước
Ý bạn là redux? Đúng vậy bạn có thể lưu lại AT hoặc RT vào đó nhưng nếu reload lại page thì sẽ bị mất và người dùng phải đăng nhập lại
Avatar
Long Domi2 năm trước
H mới chịu ra cơ mà e muốn hỏi chổ CSRF kia là ntn ạ hơi khó hiểu
Trả lời
Avatar
Long Domi2 năm trước
@gif [tfUW8mhiFk8NlJhgEh] Trả lời bình luận có cả email luôn ghê thật 
Avatar
Long Domi2 năm trước
Thanks a e sẽ nghiên cứu ạ
Avatar
Vũ Mạnh Đức2 năm trước
CSRF là một cuộc tấn công giả mạo người dùng, bạn có thể đọc thêm trên google hoặc https://developer.mozilla.org/en-US/docs/Glossary/CSRF. Đại loại là kẻ tấn công lợi dụng chính những request từ máy bạn để vượt qua được xác thực (bởi vì các request được gửi từ máy bạn) để thực hiện hành vi yêu cầu quyền xác thực. Những hành vi đó có thể gây nguy hiểm như đổi mật khẩu, đổi email...