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
  • countless.dev là một trang web khá thú vị khi mà nó so sánh giá tiền sử dụng các mô hình LLMs của các nhà cung cấp khác nhau.

    Tại đây bạn có thể nhìn thấy tất cả các mô hình ngôn ngữ lớn phổ biến bởi các nhà cung cấp như OpenAI, Azure, Mistral... Bảng giá cho mỗi 1M tokens đầu vào/ra. Hoặc thậm chí có thể so sánh chúng với nhau để tìm ra nhà cung cấp hoặc mô hình rẻ nhất tuỳ theo mục đích sử dụng.

    » Xem thêm
  • 1,2 năm trước, Kubernetes (k8s) tự nhiên được nhắc đến như một hiện tượng, chắc vì nó bá quá nên ai cũng muốn học và sử dụng. Nó là một công cụ "Automating deployment, scaling, and management of containerized applications" - Vâng! nghe hay ho đấy chứ 🤤.

    Hồi đó thì mình đang đam mê với Docker, đặc biệt là Docker Swarm, cũng tương tự như k8s ý nhưng ở quy mô nhỏ hơn. Docker Swarm thì có vẻ ít phức tạp hơn nhiều so với k8s. Mà như thế cũng tốt vì nó đã và đang đáp ứng rất tốt nhu cầu sử dụng của mình lúc đó, lại còn bớt đi phần phức tạp, lằng nhằng.

    Ấy thế mà 1-2 tháng trở lại đây, các bài viết có tiêu đề "bạn có thực sự cần đến Kubernetes" lại đang nổi lên với tần suất dày hơn. Quả thật k8s rất mạnh nhưng cũng quá phức tạp. Tại sao phải cố dùng dao "mổ trâu để giết gà" cơ chứ? Trừ khi bạn lường trước được độ phức tạp khi muốn áp dụng một công nghệ. Một cái nữa k8s tiêu tốn tài nguyên và nguồn lực ghê ghớm, để vận hành được nó không đơn giản là dựng lên được là xong mà còn phải có rất nhiều nhiều kiến thức nữa 😨.

    À, chắc cũng một phần nữa là do các "ông lớn" đang tập trung đẩy mạnh vào Serverless, giảm bớt sự phức tạp trong khâu vận hành đi, thay vào đó là nên tập trung vào phát triển ứng dụng.

    Bên cạnh đó, thì cái tên WASM cũng đang được nhắc đến rất là nhiều 🤔

    Do you really need Kubernetes in your company/startup? | dev.to

    Do You Really Need Kubernetes?

    » Xem thêm
  • Trước mình cứ khen lấy khen để Serverless, rằng tối ưu chi phí xuống 0đ để duy trì blog các thứ. Đúng là như vậy thật! Nhưng bên cạnh đó serverless cũng có các mặt tối đáng để lưu tâm đấy!

    Hôm kia mình phải mất ngày trời để truy tìm và khắc phục sự cố chỉ vì gọi hàm build-in của Cloudflare KV. Cụ thể là hàm list với limit 1000 - tức là một lần gọi nó trả về 1000 keys của KV. Cơ mà đời không như là mơ. Con số 1000 chỉ là trên lý thuyết. Lúc thì trả về vài trăm, lúc thì vài chục, thậm chí lúc thì lẹt đẹt có vài cái. Thế là làm tắc nghẽn cả hệ thống. À mà cũng không phải là nghẽn mà là hệ thống "nhàn rỗi" quá không có việc gì để làm, trong khi thực tế đáng ra nó phải xử lý cả trăm ngàn cái keys cơ 🥲

    » 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

Tôi & khao khát "chơi chữ"

Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên ngay!

Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên 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 Danh4 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ải1 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ống1 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ật1 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...