LocalStorage và Cookies - chọn cái nào để lưu JWT Tokens hiệu quả và an toàn?
JWT Tokens là một cách thức lưu trữ thông tin xác thực hiệu quả, nhưng làm cách nào để chúng ta có thể giúp chúng an toàn hơn? Có 2 cách thường dùng để lưu trữ JWT Tokens là LocalStorage và Cookies. Bây giờ chúng ta sẽ bắt đầu "mổ xẻ" các ưu - nhược điểm của mỗi loại nhé.
Tóm tắt đơn giản về Access Token và Refresh Token
Access Token thường là các JWT Tokens tồn tại trong một khoảng thời gian ngắn, được tạo ra từ phía server, và được server yêu cầu đính kèm trong mỗi Http Request để phía server xác thực người dùng.
Refresh Token thường là các chuỗi string đặc thù nào đó được lưu trong database của server, được sử dụng để tạo ra các Access Token mới mỗi khi chúng hết hạn.
Mỗi khi nhận được token rồi thì lưu chúng ở đâu?
Có 2 cách lưu trữ Access Token phổ biến, đó là localStorage
và Cookies
.
Khá nhiều cuộc tranh luận đã nổ ra nhằm xác định cái nào tốt hơn, tuy nhiên hầu hết mọi người đều cho rằng Cookies tốt hơn vì chúng an toàn hơn.
Local Storage
Ưu điểm: tiện lợi.
Đây là một tính năng hầu hết các trình duyệt đều hỗ trợ mà không phải cài gì thêm. Nếu bạn không có phần backend và phải dựa vào API của bên thứ 3 thì bạn không thể yêu cầu người ta đặt Cookies cụ thể cho website của mình.
Nó làm việc với các API bằng cách đặt Access Token vào Request Header như ví dụ sau đây:
Authorization: `Bearer ${accessToken}`
Nhược điểm: dễ bị tấn công XSS.
Cross-Site Scripting (XSS) là một trong những kĩ thuật tấn công phổ biến nhất hiện nay. Kỹ thuật XSS được thực hiện dựa trên việc chèn các đoạn script nguy hiểm vào trong source code ứng dụng web, nhằm thực thi các đoạn mã độc Javascript để chiếm phiên đăng nhập của người dùng.
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.
Ở thời điểm người người xài js, nhà nhà xài js như hiện tại, hầu như không thể không đưa bất kỳ thư viện bên thứ ba nào vào trang web của bạn.
Hacker có thể chạy 1 đoạn js kiểu: localStorage.get('token')
trên site của mình để lấy đc token, sau đó gửi ajax tới server của hacker.
Kiểu như này:
<script src="https://cdn.awesomejs-site.com/jquery.min.js"></script>
Có trời mới biết mấy cái thư viện bên thứ 3 như vầy có cái gì trong đó. Đến đây chắc bạn cũng biết mất token thì nguy hiểm như thế nào rồi chứ?
Vậy bạn có bao giờ tự hỏi, tại sao các trang dạy coding online lại thường sử dụng localStorage mặc dù biết nó không bảo mật?
Đơn giản là việc sử dụng localStorage sẽ tiện dụng và dễ dàng hơn cho họ khi demo.
Hầu hết khóa học ứng dụng các Javascript framework đều chỉ tập trung vào concept và cách triển khai chúng mà thôi, và khi ứng dụng thực tế sẽ có đôi điểm khác biệt.
Và nếu như cái gì cũng chỉ hết cho bạn rồi thì làm sao họ bán được các khóa học nâng cao hơn, phải không nào? =)))
Cookies
Ưu điểm: Không thể truy cập httpOnly
và secure
cookie qua JavaScript, do đó nó không dễ bị tấn công XSS như localStorage.
Nếu bạn đang sử dụng httpOnly
và secure
cookie, hacker không thể truy cập cookie của bạn bằng JavaScript. Điều này có nghĩa là, ngay cả khi hacker có thể chạy javascript trên trang web của bạn, chúng cũng không thể đọc các Access Token của bạn từ cookie.
Cookies tự động được gửi trong mọi yêu cầu HTTP đến máy chủ của bạn.
Nhược điểm: Tùy thuộc vào các use case cụ thể mà bạn có thể không lưu trữ được Access Token của mình vào trong cookie.
Cookie có giới hạn kích thước là 4KB. Do đó, nếu bạn đang sử dụng một JWT Access Token lớn, thì việc lưu trữ trong cookie không phải là một lựa chọn đúng đắn.
Có những trường hợp bạn không thể chia sẻ cookie với máy chủ API của mình hoặc API yêu cầu bạn đặt Access Token vào trong Authorization Header. Trong trường hợp này, bạn sẽ không thể sử dụng cookie để lưu trữ Access Token của mình.
LocalStorage và tấn công XSS
localStorage
dễ bị tấn công vì nó có thể dễ dàng truy cập bằng JavaScript và hacker có thể lấy Access Token của bạn và sử dụng nó sau này.
Tuy nhiên, mặc dù không thể truy cập cookie gắn cờ httpOnly
bằng JavaScript, điều này không có nghĩa là bằng cách sử dụng cookie, bạn sẽ an toàn trước các cuộc tấn công XSS liên quan đến Access Token của mình.
Nếu kẻ tấn công có thể chạy JavaScript trong ứng dụng của bạn, thì chúng chỉ có thể gửi một HTTP Request đến server của bạn và điều đó sẽ tự động gửi kèm cookie của bạn. Nó chỉ kém thuận tiện hơn cho kẻ tấn công vì họ không thể đọc nội dung Access Token mặc dù họ hiếm khi phải làm vậy.
Việc hacker phải tấn công bằng cách sử dụng trình duyệt của nạn nhân (bằng cách gửi HTTP Request) đương nhiên sẽ tốt hơn là hacker có thể sử dụng máy của chính hacker để tấn công bạn bằng các phương thức khác.
Cookies và tấn công CSRF
CSRF (Cross-site Request Forgery) là một kiểu tấn công lợi dụng sự tin tưởng của người dùng. Chúng lừa nạn nhân gửi đi các HTTP Request không mong muốn đến website nào đó.
Khi gửi một HTTP Request (hợp pháp hoặc không hợp pháp), trình duyệt của nạn nhân sẽ gửi kèm theo các Cookies Header. Các cookies thường được dùng để lưu trữ một session nhằm định danh người dùng giúp họ không phải xác thực lại mỗi khi thực hiện Request.
Nếu phiên làm việc đã xác thực của nạn nhân được lưu trữ trong một Cookie vẫn còn hiệu lực, và website nạn nhân bảo mật kém và dễ bị tấn công CSRF, kẻ tấn công có thể sử dụng CSRF để chạy bất cứ Request nào với website nạn nhân mà website nạn nhân không biết được các Request này có hợp pháp hay không.
Một lỗ hổng CSRF sẽ ảnh hưởng đến các quyền của người dùng ví dụ như quản trị viên, kết quả là chúng truy cập được đầy đủ quyền hạn trên website nạn nhân.
Một ví dụ về kịch bản tấn công CSRF
- Người dùng Trung Nghĩa truy cập 1 diễn đàn yêu thích của mình như thường lệ. Một người dùng khác, Duy PT - đăng tải một bài viết lên diễn đàn. Giả sử rằng Duy PT có ý đồ không tốt và hắn ta muốn xóa đi một dự án quan trọng nào đó mà Trung Nghĩa đang làm.
- Duy PT sẽ tạo một bài viết trên diễn đàn, trong đó có chèn thêm một đoạn code như sau:
<img width="0" height="0" src="www.nghiattran-sample.com/project/1/destroy"/>
Thêm vào đó thẻ img sử dụng trong trường hợp này có kích thước 0x0 pixel và người dùng sẽ không thể thấy được.
Giả sử Trung Nghĩa đang truy cập vào tài khoản của mình ở www.nghiattran-sample.com và chưa thực hiện logout để kết thúc session. Bằng việc xem bài post bên diễn đàn mà Duy PT đăng lên, trình duyệt của Trung Nghĩa sẽ đọc thẻ img và cố gắng load ảnh từ www.nghiatran-sample.com, do đó sẽ gửi câu lệnh xóa đến địa chỉ này.
Ứng dụng web ở www.nghiatran-sample.com sẽ chứng thực Trung Nghĩa dựa trên cookie và sẽ xóa project với ID là 1. Nó sẽ trả về trang kết quả mà không phải là ảnh, do đó trình duyệt sẽ không hiển thị ảnh.
Kết luận
Mặc dù Cookies vẫn tồn tại nhiều khuyết điểm, nhưng bạn nên ưu tiên sử dụng Cookies bất cứ khi nào có thể.
- Cả
localStorage
vàcookie
đều dễ bị tấn công XSS nhưng kẻ tấn công sẽ khó thực hiện cuộc tấn công hơn khi bạn đang sử dụng Cookie với cờhttpOnly
. - Cookie dễ bị tấn công CSRF nhưng nó có thể được giảm thiểu bằng cách sử dụng cờ
sameSite
,secure
và Anti CSRF Token. Nếu bạn sử dụng một số framework như Laravel thì nó đã có sẵn cơ chế xác thực CSRF.
Bạn có thể tham khảo các khuyến cáo về bảo mật của tổ chức OWASP.ORG tại HTML5 Security Cheat Sheet.
Ở đây có một vài khuyến cáo về việc sử dụng Cookies thay cho Local Storage để lưu trữ các thông tin quan trọng.
Pay extra attention to "localStorage.getItem" and "setItem" calls implemented in HTML5 page. It helps in detecting when developers build solutions that put sensitive information in local storage, which is a bad practice.
Do not store session identifiers in local storage as the data is always accessible by JavaScript. Cookies can mitigate this risk using the httpOnly flag.
Bài viết đến đây là kết thúc rồi. Cám ơn các bạn đã theo dõi :D