🔥Được vừa ra mắt khóa học Node.js Super đấy, xem thử đi nào

[P4] Giải ngố authentication: Lưu JWT token ở local storage hay cookie?

🎉 Nếu anh em thấy Front-End cạnh tranh quá thì chúng ta học thêm Back-End thôi.

Mình đang có khóa học🏆 Node.js Super, với các kiến thức như: Express.js, TypeScript, MongoDB, Socket.io, Docker, AWS, Swagger, ...

Vậy nên anh em có thể mua ngay từ bây giờ để tăng khả năng pass phỏng vấn nhé 😉

Đây là phần 4 trong series Giải ngố authentication. Nếu các bạn chưa xem phần 1,2,3 thì có thể xem ở đây:

Có rất nhiều tranh cãi xung quanh việc lưu token ở đâu? Có người lưu ở Local Storage, có người lưu ở Cookie, có người lưu ở Session Storage, có người lưu ở RAM, có người lưu ở IndexedDB, có người lưu ở WebSQL, có người lưu ở đâu đó khác nữa...

Vậy thực sự thì lưu ở đâu mới tốt?

Bài viết này mình chia sẻ dựa trên quan điểm cá nhân nên là có thể nó không hợp với dự án của bạn, nhưng mình sẽ cố gắng đưa ra các lập luận để bạn có thể tự đưa ra quyết định cho dự án của mình.

Oke bắt đầu luôn nhé 🤜


🥇Tóm tắt về Access Token và Refresh Token

Bạn nào đã đọc bài viết [P3] Giải ngố authentication: JWT thì sẽ biết công dụng của 2 loại JWT này

  • Access Token: Là một token có thời gian sống ngắn, được tạo ra bởi server, lưu ở client và client sẽ đính kèm nó vào HTTP request khi gửi request lên server, nhằm giúp server xác thực client.

  • Refresh Token: token có thời gian sống dài hơn, được lưu trong database ở server và lưu ở client, dùng để tạo ra access token mới mỗi khi access token hết hạn.


🥇Lưu Access Token ở đâu?

Với đa số các dự án thì mình sẽ không chọn lưu ở Session Storage và Ram (lưu trong 1 biến của JavaScript) bởi vì

  • Nếu lưu ở Session Storage thì khi bạn đóng trình duyệt đi mở lại thì session storage sẽ bị xóa => Bạn sẽ phải đăng nhập lại.

  • Nếu lưu ở RAM thì bạn sẽ không thể chia sẻ access token giữa các tab trình duyệt được, cũng như đóng tab thì access token sẽ mất => Bạn sẽ phải đăng nhập lại

Rõ ràng UX trong 2 trường hợp này không tốt, trừ khi yêu cầu dự án của các bạn là như vậy.

🥈Lưu ở Local Storage

Ưu điểm

  • Nhanh, tiện lợi, không cần phụ thuộc vào backend để lưu trữ.
  • Bộ nhớ khá lớn, thường là trên 5MB
  • Có thể tự quyết định request nào cần access token để gửi lên server, request nào không cần
  • Không tự động gửi access token lên server, nên nếu bị tấn công CSRF thì attacker không thể lấy được access token của bạn.

Nhược điểm

  • Nếu bị tấn công XSS thì attacker có thể lấy được access token

Một website có thể bị tấn công XSS từ khá là nhiều nguồn, ví dụ như: Do chính code chúng ta viết ra có lỗ hổng, do các thư viện bên thứ 3 như React, Vue, Lodash,...

Đây là cái lý do duy nhất mà một số người anti localstorage một cách cực đoan.

Ưu điểm

  • Không thể truy cập được từ Javascript nếu bạn set thuộc tính httpOnly, nên nếu có bị tấn công XSS thì cũng không lấy được token của bạn.

Nhược điểm

Có một cái nhược điểm đó là có thể bị tấn công CSRF, nhưng bạn có thể giải quyết bằng cách thêm một số thuộc tính cho cookie như sameSite, secure, domain, path để giảm thiểu khả năng bị tấn công CSRF.

Ngoài ra nếu dùng các framework SPA ngày nay nữa thì khả năng bị tấn công CSRF cũng không còn cao nữa. Vậy nên mình không cho đây là nhược điểm.

Vậy theo mình nhược điểm khi dùng Cookie là

  • Bạn không thể lấy được các payload của JWT token, vì JavaScript không truy cập được vào cookie nếu chúng ta set thuộc tính httpOnly cho cookie.
  • Bộ nhớ Cookie trên trình duyệt rất bé, loanh quanh 4KB thôi.
  • Dùng cookie thì phía backend sẽ phải xử lý thêm một số thứ như: parse cookie, set cookie, kiểm tra request đến server. Nếu đến từ browser thì parse cookie, nếu đến từ mobile app thì dùng header Authorization để lấy token...

🥇Suy nghĩ của mình

Bạn thấy đấy, lưu ở đâu cũng có ưu nhược riêng.

🥈Tại sao chúng ta không kết hợp cả 2 nhỉ?

Cookie đem lại ưu thế hơn 1 xíu về độ bảo mật khi so với local storage, nhưng cũng làm mất đi cái hay của JWT là có thể đọc được payload của JWT token ở client.

Có những trường hợp chúng ta cần đọc payload để biết thời gian hết hạn của token chẳng hạn, nhưng không lấy được access token ở trong cookie cũng khá là khó chịu.

Giải quyết vấn đề này thì chúng ta có thể chia access token làm 2 phần:

  • Header.Payload thì lưu ở local storage
  • Signature thì lưu ở cookie

Khi gửi lên server thì server sẽ ghép 2 phần này lại thành 1 và kiểm tra tính hợp lệ của token.

Như vậy thì client có thể đọc được payload JWT và cũng giữ lại ưu điểm của việc lưu ở Cookie.

🥈Vậy thì không nên lưu token ở Local Storage à?

Đâu đó bạn sẽ gặp những bài như Please Stop Using Local Storage hoặc LocalStorage vs Cookies: All You Need To Know About Storing JWT Tokens Securely in The Front-End làm bạn hoang mang và có cái nhìn không tốt về Local Storage.

Nếu các bạn lướt xuống đọc comment các bài viết trên thì vẫn có rất nhiều ý kiến không đồng tình với tác giả.

Chúng ta cần làm rõ thế này, lưu trữ access token ở Cookie không giúp chúng ta tránh được tấn công XSS mà khi bị tấn công XSS thì hacker khó lấy được access token của bạn hơn thôi.

Nhiều người không hình dung ra được mức độ thiệt hại khi bị tấn công XSS nó lớn như thế nào.

Một website mà bị tấn công XSS nghĩa là web đấy toang, hacker có thể làm được nhiều việc nghiêm trọng hơn là lấy được access token của bạn. Ví dụ:

  • Điều khiển website của bạn để lừa người dùng gửi tiền vào tài khoản của hacker
  • Hiển thị popup yêu cầu người dùng nhập username/password để lấy thông tin người dùng

Vậy nên lưu token ở Local Storage mình thấy rất là bình thường, nó đem lại sự tiện lợi cho cả phía Front-End lẫn Back-End, không có vấn đề gì phải anti nó cả.

Muốn cân bằng giữa Cookie và Local Storage thì có thể kết hợp cả 2 như mình đã nói ở trên, rồi mã hóa thêm bằng một thuật toán nữa ở phía client cho tăng độ khó,...

Nói chung muốn bảo mật hơn thì có nhiều cách lắm, nhưng hãy nghĩ xem nó có thực sự cần thiết hay không, liệu nó có đáng để bỏ thời gian ra làm hay không.

À xíu nữa quên, nếu API chỉ nhận access token thông quan HTTP Header Authorization thì lại thêm 1 lý do nữa để chúng ta lưu token ở Local Storage rồi 😃


🥇Tóm lại

  • XSS là game over, bất kể bạn lưu token ở đâu

  • Lưu token ở Local Storage hay Cookie đều ổn, không có gì phải anti cả.

  • Muốn cân bằng giữa ưu điểm của cả 2 thì có thể kết hợp cả 2.

  • Mã hóa thêm 1 vài bước ở client nếu muốn tăng độ bảo mật.


Khóa học ReactJs giúp bạn chinh phục mức lương 25 - 30 triệu/tháng

Phew! Cuối cùng bạn cũng đã đọc xong. Bài viết này có hơi dài một tí vì mình muốn nó đầy đủ nhất có thể 😅

Chúng ta đều hiểu rằng Javascript và React không hề dễ, chúng có quá nhiều concept cần phải học. Mình cũng cảm thấy nó khó! Nay lại có thể Typescript nữa 🥲, thật sự khó nuốt.

Nhưng đừng lo: Bạn có thể nắm vững các kiến thức trên chỉ trong một khóa học ReactJs Super - Shopee Clone Typescript

Mình đã bắt đầu code React vào năm 2019, và nó đã trở thành thư viện ưa thích của mình để xây dựng UI và web app. Mình cũng đã làm việc với nhiều framework khác như Angular, Vue nhưng thực sự chỉ có React là đem lại cho mình cảm xúc và sự hiệu quả. 💓

Nếu bạn đang gặp khó khăn với React, mình ở đây để giúp bạn!

Mình đã dành hơn 6 tháng để phát triển khóa học ReactJs Super - Shopee Clone Typescript. Trong khóa này bạn sẽ được học mọi thứ về thư viện ReactJs, các kiến thức từ cơ bản cho đến nâng cao nhất, mục đích của mình là giúp bạn chinh phục mức lương 25 - 30 triêu/tháng

Nếu bạn cảm thấy bài viết này của mình hữu ích, mình nghĩ bạn sẽ thích hợp với phong cách dạy của mình. Không như bài viết này, khóa học là sự kết hợp giữa các bài viết, video, quizz, bài tập nhỏ và dự án lớn có thể xin việc được ngay. Học xong mình đảm bảo bạn sẽ lên tay ngay. 💪🏻

Avatar Dư Thanh Được
Dư Thanh Được
Một developer thích nghiên cứu và chia sẻ kiến thức về lập trình, blockchain, marketing. Chuyên code và dạy lập trình website