Phân vùng bộ nhớ khóa trạng thái theo cả trang được nhúng lẫn trang cấp cao nhất, nên một widget trên hai trang sẽ nhận hai kho lưu trữ riêng biệt.
Trong phần lớn lịch sử của web, để trả lời câu hỏi "đoạn mã này được dùng kho lưu trữ nào?", trình duyệt chỉ nhìn vào một thứ: origin nơi đoạn mã được tải về. Vì vậy, một widget theo dõi nhúng trên cả trăm trang sẽ đọc và ghi vào cùng một kho cookie ở cả trăm trang đó, và chính kho dùng chung ấy khiến việc theo dõi liên trang trở nên quá rẻ. Phân vùng bộ nhớ (storage partitioning) thay đổi câu hỏi này. Giờ đây, bộ nhớ được khóa theo hai thứ — origin sở hữu nó và trang cấp cao nhất mà bạn thực sự đang truy cập — nên cùng một widget được nhúng trên hai trang khác nhau sẽ nhận hai kho không liên quan gì đến nhau và không thể nhìn thấy nhau.
Điểm chính
- Toàn bộ ý tưởng nằm ở khóa kép. Thay vì chỉ khóa trạng thái theo origin của tài nguyên, trình duyệt khóa nó theo (origin của tài nguyên, trang cấp cao nhất). Hướng dẫn State Partitioning của MDN mô tả Firefox là đánh khóa kép "toàn bộ trạng thái phía client theo origin của tài nguyên được tải và theo trang cấp cao nhất".
- Cùng một phần nhúng trên hai trang nghĩa là hai kho, không phải một. Một bên theo dõi được nhúng trong
A.examplevàB.examplekhông còn có thể lưu ID ở một nơi rồi đọc lại ở nơi kia; dữ liệu nó thấy trên A khác với dữ liệu nó thấy trên B. - "Cookie" chỉ là một phần của bề mặt này. Phân vùng còn bao trùm
localStorage,sessionStorage, IndexedDB, Cache API, service worker, và cả trạng thái mạng như HTTP cache — và các engine không phải lúc nào cũng phân vùng cùng những thứ giống nhau theo cùng một cách. - Storage Access API là lối thoát có chủ đích. Một frame được nhúng có thể gọi
requestStorageAccess()sau một thao tác của người dùng để lấy lại cookie bên thứ nhất bình thường, nhưng quyền này chỉ áp dụng cho một cặp (trang cấp cao nhất, trang được nhúng), chứ không được bật toàn cục. - Phân vùng phá vỡ việc theo dõi liên trang dựa trên trạng thái, chứ không phá vỡ việc theo dõi nói chung. Dấu vân tay không cần lưu trữ, nên chẳng có gì để phân vùng — đó là lý do thay đổi này khiến dấu vân tay có giá trị hơn với bên theo dõi, chứ không giảm đi.
"Đánh khóa kép" thực chất nghĩa là gì
Theo truyền thống, trình duyệt khóa trạng thái phía client theo origin, hoặc đôi khi theo tên miền đăng ký (registrable domain), của địa chỉ nơi tài nguyên được tải về. MDN đưa ra ví dụ kinh điển: cookie, các đối tượng localStorage và cache mà một iframe được tải từ https://example.com/hello.html có thể dùng đều được khóa theo example.com. Điều đó đúng cho dù trình duyệt đang tải example.com làm trang bạn đang xem (bên thứ nhất) hay làm một phần nhúng bên trong trang của người khác (bên thứ ba).
Bên theo dõi đã khai thác đúng điểm này. Đặt một iframe hoặc script của example.com lên A.example và B.example, lưu một định danh người dùng vào cookie hoặc localStorage của nó, và nó sẽ đọc lại đúng định danh đó trên cả hai trang. Hai trang không cần thông đồng với nhau; kho dùng chung tự làm nhiệm vụ liên kết.
Phân vùng thêm một khóa thứ hai. Khóa thứ nhất vẫn là origin của tài nguyên được tải. Khóa thứ hai là trang cấp cao nhất — trong hầu hết trường hợp là scheme cộng với tên miền đăng ký của trang trên thanh địa chỉ. Dưới đây là kết quả khi example.com được nhúng vào hai trang:
| Nơi đoạn mã chạy | Kho lưu trữ nó nhận được |
|---|---|
example.com được truy cập trực tiếp | (example.com, example.com) |
example.com được nhúng trong A.example | (example.com, A.example) |
example.com được nhúng trong B.example | (example.com, B.example) |
Vậy là có ba kho riêng biệt thay vì một. Bên theo dõi vẫn có bộ nhớ — chỉ là nó không thể mang một định danh từ kho đầu tiên sang hai kho còn lại. Lưu một ID khi được truy cập trực tiếp không còn giúp nó lấy lại ID đó khi bị nhúng ở nơi khác.
Hãy lưu ý điều không thay đổi: trên chính example.com, trong một tab bình thường, mọi thứ vẫn hoạt động như cũ. Phân vùng chỉ phát huy tác dụng khi đoạn mã chạy trong ngữ cảnh bên thứ ba.
Những gì được phân vùng (không chỉ có cookie)
Sai lầm phổ biến nhất là coi đây là "chặn cookie bên thứ ba, đổi tên gọi". Thực ra đó là một cách tiếp cận khác. Các chính sách cũ chặn quyền truy cập vào một số API lưu trữ trong ngữ cảnh bên thứ ba; còn phân vùng cấp cho phần nhúng một kho riêng cho mỗi trang cấp cao nhất, nên không cần chặn gì mà sự cách ly vẫn được giữ vững.
Tài liệu Firefox của MDN chia bề mặt này thành ba nhóm:
| Nhóm | Ví dụ | Hành vi trong Firefox |
|---|---|---|
| Bộ nhớ truy cập được | localStorage, sessionStorage, DOM Cache, IndexedDB, Broadcast Channel, Shared Workers, Service Workers | Được phân vùng theo trang cấp cao nhất |
| Cookie | Cookie bên thứ ba | Được phân vùng theo mặc định, có cách để lấy lại quyền truy cập không phân vùng (xem bên dưới) |
| Trạng thái mạng | HTTP cache, cache ảnh, cache favicon, connection pooling, DNS, HSTS, định danh phiên TLS, OCSP, phông chữ | Được phân vùng vĩnh viễn; các trang web không thể nới lỏng |
Hàng trạng thái mạng rất quan trọng với quyền riêng tư, vì những cơ chế này vốn không được thiết kế để lưu dữ liệu, nhưng vẫn có thể bị lạm dụng làm nơi lưu trữ. Đó chính là mánh khóe đằng sau ETag và siêu cookie cache: một phản hồi được cache mà trang này đặt được và trang khác đọc lại được chính là một kênh theo dõi. Việc phân vùng HTTP cache theo trang cấp cao nhất đã bịt phiên bản liên trang của nó.
Vị thế của từng engine trình duyệt
Câu trả lời trung thực là không có một trạng thái "nền tảng web" duy nhất, vì các engine đến với phân vùng riêng rẽ và theo những lộ trình khác nhau. Phần dưới đây chỉ giới hạn ở những gì các nguồn chính thống nêu ra; hãy coi bất kỳ số phiên bản nào là thứ cần kiểm tra lại với tài liệu hiện hành.
- Safari (WebKit) đi trước. Intelligent Tracking Prevention (ITP), ra mắt năm 2017, phát hiện các tên miền có khả năng theo dõi người dùng xuyên trang và hoặc phân vùng cookie của chúng, hoặc xóa sạch dữ liệu website của chúng. Thông báo năm 2018 của chính WebKit về Storage Access API mô tả việc ITP chỉ cấp cho nội dung được nhúng "cookie đã phân vùng" một khi nó bị xếp vào loại bên theo dõi liên trang. Thông báo đó cũng lưu ý rằng bản triển khai ban đầu của WebKit cho API này chỉ bao gồm cookie và không thay đổi cách IndexedDB hay
localStorageđược phân vùng. - Firefox đạt được điều này với cái mà họ gọi là State Partitioning (được quảng bá là Total Cookie Protection). MDN ghi lại quá trình triển khai: phân vùng mạng được bật mặc định cho mọi người dùng từ Firefox 85, và phân vùng động — phần cookie — được bật mặc định từ Firefox 103, sau các giai đoạn bật thử nghiệm trước đó cho chế độ Strict (Firefox 86) và duyệt web riêng tư (Firefox 90).
- Chromium đi theo lộ trình từng bước. Thay vì một công tắc duy nhất, nó phân vùng từng phần riêng lẻ — trước là HTTP cache, sau đó là các API lưu trữ bên thứ ba — và đi kèm một cơ chế cookie phân vùng tự chọn tham gia (opt-in), CHIPS, được mô tả bên dưới. Hành vi cụ thể và các mốc phiên bản khác với Firefox, vì vậy hãy kiểm tra dữ liệu tương thích trình duyệt cho API bạn phụ thuộc thay vì mặc định là tương đương.
Hệ quả thực tế với lập trình viên: đoạn mã chạy tốt ở engine này nhờ dựa vào trạng thái bên thứ ba dùng chung có thể âm thầm hỏng ở engine khác. Hệ quả thực tế với người dùng: cùng một bên theo dõi sẽ gặp những trở ngại khác nhau tùy trình duyệt bạn dùng.
Cookie phân vùng: CHIPS và thuộc tính Partitioned
Chặn hẳn cookie bên thứ ba sẽ làm hỏng các phần nhúng hợp pháp — một widget chat hay bản đồ ghi nhớ tùy chọn theo từng trang không có động cơ theo dõi nào, nhưng lệnh chặn đại trà lại làm nó hỏng. CHIPS (Cookies Having Independent Partitioned State) là giải pháp dung hòa: một trang web chọn cho cookie tham gia phân vùng bằng thuộc tính Partitioned, và trình duyệt lưu nó dưới khóa kép.
Set-Cookie: __Host-widget=abc123; Secure; Path=/; SameSite=None; Partitioned
Một cookie được đặt theo cách này chỉ được gửi lại khi phần nhúng được tải dưới cùng một trang cấp cao nhất đã có mặt lúc nó được đặt. Trang Storage Access API của MDN nêu thẳng sự đánh đổi này: vì không có rủi ro quyền riêng tư nào ở một cookie không thể đi theo bạn qua các trang, trình duyệt gửi cookie phân vùng trong các yêu cầu và cho phép các tài nguyên được nhúng dùng chúng — nhưng vì các cookie này không được chia sẻ giữa các trang, chúng cũng không tự động được đồng bộ hóa giữa các trang.
Vế cuối chính là cái giá của mô hình này. Một phần nhúng kiểu "đăng nhập một lần, được nhận ra ở mọi nơi", chẳng hạn frame đăng nhập một lần (single sign-on), cần một thứ mạnh hơn.
Lối thoát: Storage Access API
Storage Access API cho phép một iframe khác trang xin lại cookie bên thứ nhất bình thường của nó. API này được WebKit đưa vào năm 2018 — thông báo của họ nêu rõ nguồn gốc: phản hồi của lập trình viên về ITP là nội dung được nhúng cần một cách để xác thực những người dùng đã đăng nhập vào các dịch vụ bên thứ nhất của họ.
Luồng hoạt động gồm hai phương thức:
document.hasStorageAccess()— một promise trả về việc frame đã có quyền truy cập không phân vùng hay chưa.document.requestStorageAccess()— xin quyền truy cập; phải được gọi trong lúc có thao tác của người dùng (MDN gọi là transient activation), chẳng hạn một cú nhấp vào nút "Đăng nhập" bên trong frame.
Quyền được cấp có nghĩa và không có nghĩa là gì:
- Quyền áp dụng theo từng cặp, không toàn cục. MDN mô tả quyền này được lưu với cấu trúc
<top-level site, embedded site>. Quyền truy cập được cấp choexample.comnhúng trongembedder.comkhông áp dụng choexample.comnhúng ở bất kỳ nơi nào khác. - Nó khôi phục cookie bên thứ nhất, không phải dữ liệu của trang chứa. Bài viết của WebKit nói rõ rằng quyền truy cập bộ nhớ "không nới lỏng chính sách same-origin theo bất kỳ cách nào" — đó không phải là việc bên thứ ba thò tay vào bộ nhớ của trang chứa, hay ngược lại.
- Cách hỏi ý kiến khác nhau tùy trình duyệt. Theo MDN, Safari và Chrome hiện hộp thoại hỏi với các phần nhúng chưa từng được cấp quyền, còn Firefox chỉ hỏi khi một origin đã xin quyền truy cập trên nhiều hơn một số lượng trang nhất định. Trong Chrome, phần nhúng và trang chứa thuộc cùng một related website set có thể bỏ qua bước hỏi này.
- Firefox còn cấp quyền theo heuristic. Để tránh làm hỏng các tích hợp phổ biến, nó có thể cấp cho phần nhúng quyền truy cập 30 ngày sau khi người dùng tương tác với một popup mà nó mở, hoặc sau chuỗi thao tác nhanh: điều hướng, tương tác, rồi quay lại. MDN gọi các cơ chế này là tạm thời và cảnh báo lập trình viên không nên dựa vào chúng.
Mục tiêu thiết kế là việc lấy lại quyền truy cập phải là một quyết định hiển thị, theo từng trang, được đưa ra trong bối cảnh một tương tác thực sự — chứ không phải công tắc mà bên theo dõi có thể âm thầm bật cho mọi trang cùng lúc.
Những gì phân vùng không ngăn được
Cần nói chính xác về ranh giới, vì "được phân vùng" rất dễ bị hiểu quá thành "riêng tư".
- Theo dõi bên thứ nhất không bị đụng tới. Một trang vẫn có thể nhận ra khách quay lại của chính mình bằng cookie của nó. Phân vùng chỉ nhắm vào việc liên kết liên trang.
- Nó không ảnh hưởng đến các thủ thuật liên trang khác. Những kỹ thuật liên kết các lượt truy cập bằng phương tiện khác — chẳng hạn bounce tracking, vốn chuyển hướng bạn thoáng qua trang riêng của bên theo dõi để nó chạy như một bên thứ nhất — nằm ngoài khóa kép. Việc dò xem bạn đang đăng nhập vào những trang nào cũng vậy.
- Nó không phải là sự cách ly do người dùng tự chọn. Container của Firefox tách các danh tính của chính bạn khỏi nhau; phân vùng tách các phần nhúng của từng trang khỏi nhau. Firefox áp dụng cả hai cùng lúc, và cái này không thay thế được cái kia — xem container tabs và theo dõi.
- Nó không phải chế độ ẩn danh. Cửa sổ riêng tư xóa bỏ trạng thái khi đóng; phân vùng áp dụng cả ở các cửa sổ thông thường. Về cách các trang web vẫn cố nhận ra chế độ riêng tư, xem phát hiện chế độ ẩn danh.
- Nó không phải lá chắn chống lại một ID bền vững. Với những định danh được thiết kế để sống sót qua thao tác xóa dữ liệu — chủ đề của visitor ID bền vững — phân vùng chỉ loại bỏ một đường lưu trữ, chứ không loại bỏ mục tiêu.
Vì sao điều này đẩy bên theo dõi sang lấy dấu vân tay
Đây là sự đánh đổi, nói thẳng ra. Phân vùng phá vỡ việc theo dõi liên trang dựa trên trạng thái: một định danh mà bên theo dõi ghi vào bộ nhớ trên một trang không còn đọc được từ trang khác. Mọi kỹ thuật trong họ này đều dựa vào việc trình duyệt giữ một kho dùng chung cho mỗi origin, và giả định đó không còn nữa.
Dấu vân tay không đi theo đường ấy. Nó được tính từ những gì trình duyệt và phần cứng vốn đã để lộ — đầu ra canvas và WebGL, phông chữ, thông số màn hình và phần cứng, hành vi âm thanh — mà không ghi gì lên thiết bị của bạn. Không có lưu trữ nào tham gia, nên trình duyệt không có gì để phân vùng, xóa hay chặn. ID không được lưu; nó được nhận ra lại ở lần truy cập sau từ cùng những đầu vào đó.
Đó là lý do việc bịt đường lưu trữ đã nâng giá trị của đường dấu vân tay với bất kỳ ai vẫn muốn liên kết liên trang. Nếu muốn xem trình duyệt của mình đang để lộ những tín hiệu nào, hướng dẫn về lấy dấu vân tay trình duyệt của chúng tôi giải thích cơ chế, còn công cụ kiểm tra dấu vân tay cho bạn xem kết quả trực tiếp.
Câu hỏi thường gặp
Phân vùng bộ nhớ có giống việc chặn cookie bên thứ ba không?
Không. Chặn là từ chối phần nhúng truy cập bộ nhớ trong ngữ cảnh bên thứ ba. Phân vùng thì cấp cho nó bộ nhớ — một kho riêng cho mỗi trang cấp cao nhất — nên các phần nhúng vẫn hoạt động, nhưng thứ chúng lưu trên trang này không hiển thị trên trang khác.
Phân vùng có làm hỏng đăng nhập và widget được nhúng không?
Có thể. Một phần nhúng dựa vào một cookie dùng chung trên nhiều trang để nhận ra người dùng đã đăng nhập sẽ thấy một kho mới, trống trơn trên mỗi trang. Các cách khắc phục được hỗ trợ là requestStorageAccess() của Storage Access API (cần thao tác của người dùng) hoặc, với trạng thái theo từng trang, một cookie có thuộc tính Partitioned.
Phân vùng có ngăn một trang theo dõi tôi trên chính các trang của nó không?
Không. Cookie bên thứ nhất được khóa theo trang bạn đang truy cập, nên phân vùng không đụng đến nó. Phân vùng chỉ loại bỏ khả năng liên kết hoạt động của bạn giữa các trang không liên quan thông qua bộ nhớ nhúng dùng chung.
Mọi trình duyệt có phân vùng bộ nhớ giống nhau không?
Không. Safari, Firefox và Chromium đưa phân vùng vào ở những thời điểm khác nhau và với phạm vi khác nhau, và các chi tiết như API lưu trữ nào được bao phủ hay khi nào hộp thoại hỏi xuất hiện đều khác nhau. Hãy kiểm tra dữ liệu tương thích hiện hành cho đúng API bạn cần trước khi dựa vào bất kỳ hành vi nào.
Kết luận
Phân vùng bộ nhớ là một thay đổi nhỏ với tác động lớn: thêm trang cấp cao nhất làm khóa thứ hai nghĩa là bộ nhớ của một bên theo dõi được nhúng trên trang này và trên trang kia đơn giản là hai kho khác nhau. Điều đó loại bỏ lối tắt trạng thái dùng chung mà việc theo dõi liên trang giá rẻ từng dựa vào, đồng thời để lại một đường quay lại có kiểm soát — Storage Access API, hoặc cookie Partitioned tự chọn tham gia — cho những trường hợp chính đáng cần đến. Nó không xóa bỏ việc theo dõi; nó chuyển cuộc đấu sang những nơi không cần bộ nhớ nào cả, và đó là lý do việc hiểu về dấu vân tay càng quan trọng hơn, chứ không kém đi, khi trình duyệt của bạn phân vùng trạng thái.
Đọc thêm:


