Партиционирование привязывает состояние к встроенному сайту и странице верхнего уровня: один виджет на двух сайтах получает два разных хранилища.
Почти всю историю веба браузер отвечал на вопрос «какое хранилище достаётся этому коду?», глядя на одну-единственную вещь: источник (origin), из которого код был загружен. Поэтому трекинговый виджет, встроенный на сотню сайтов, читал и записывал одно и то же хранилище cookie на всех ста, — и именно это общее хранилище делало межсайтовую слежку такой дешёвой. Партиционирование хранилища меняет сам вопрос. Теперь хранилище привязывается к двум ключам — к источнику, которому оно принадлежит, и к сайту верхнего уровня, который вы на самом деле посещаете, — поэтому один и тот же встроенный виджет на двух разных сайтах получает два не связанных между собой хранилища, которые не видят друг друга.
Ключевые выводы
- Вся суть — в двойном ключе. Браузер индексирует состояние не только по источнику ресурса, а по паре (источник ресурса, сайт верхнего уровня). Руководство MDN по State Partitioning описывает Firefox как браузер, который использует двойной ключ для «всего состояния на стороне клиента — по источнику загружаемого ресурса и по сайту верхнего уровня».
- Один и тот же встраиваемый элемент на двух сайтах — это два хранилища, а не одно. Трекер, встроенный в
A.exampleиB.example, больше не может сохранить ID в одном месте и считать его в другом: данные, которые он видит на A, отличаются от данных на B. - «Cookie» — лишь часть поверхности. Партиционирование охватывает также
localStorage,sessionStorage, IndexedDB, Cache API, service workers и сетевое состояние вроде HTTP-кеша — причём разные движки партиционируют не всё одинаково. - Storage Access API — намеренно оставленный запасной выход. Встроенный фрейм может вызвать
requestStorageAccess()после действия пользователя и вернуть себе обычные first-party cookie, но разрешение действует для одной пары (сайт верхнего уровня, встроенный сайт), а не включается глобально. - Партиционирование ломает межсайтовый трекинг на основе состояния, но не слежку как таковую. Отпечатку браузера хранилище не нужно, а значит, партиционировать нечего — поэтому это изменение сделало фингерпринтинг для трекеров ценнее, а не менее ценным.
Что на самом деле значит «двойной ключ»
Традиционно браузеры привязывают состояние на стороне клиента к источнику (а иногда по регистрируемому домену) того места, откуда загружен ресурс. MDN приводит канонический пример: cookie, объекты localStorage и кеши, доступные iframe, загруженному с https://example.com/hello.html, привязаны к ключу example.com. Это верно независимо от того, загружает ли браузер example.com как страницу, на которой вы находитесь (first-party), или как встроенный элемент чужой страницы (third-party).
Трекеры пользовались именно этим. Поместите iframe или скрипт с example.com на A.example и B.example, сохраните идентификатор пользователя в его cookie или localStorage — и он считает тот же идентификатор на обоих сайтах. Сговор между сайтами не нужен: связывает их общее хранилище.
Партиционирование добавляет второй ключ. Первый по-прежнему — источник загружаемого ресурса. Второй — сайт верхнего уровня: в большинстве случаев это схема плюс регистрируемый домен страницы в вашей адресной строке. Вот что получается, когда example.com встроен в два сайта:
| Где выполняется код | Какое хранилище он получает |
|---|---|
example.com открыт напрямую | (example.com, example.com) |
example.com встроен в A.example | (example.com, A.example) |
example.com встроен в B.example | (example.com, B.example) |
Это три отдельных хранилища вместо одного. У трекера хранилище по-прежнему есть — он просто не может перенести идентификатор из первого в остальные два. Сохранив ID при прямом посещении, он больше не получит его обратно, будучи встроенным на другом сайте.
Обратите внимание на то, что не изменилось: на самом example.com, в обычной вкладке, всё работает как раньше. Партиционирование срабатывает только тогда, когда код выполняется в стороннем (third-party) контексте.
Что партиционируется (это не только cookie)
Самая частая ошибка — считать это «блокировкой сторонних cookie под новым названием». Это другой подход. Прежние политики блокируют доступ к определённым API хранилища в стороннем контексте; партиционирование выдаёт встраиваемому элементу отдельное хранилище для каждого сайта верхнего уровня, поэтому для изоляции ничего блокировать не нужно.
Документация MDN по Firefox делит поверхность на три группы:
| Группа | Примеры | Поведение в Firefox |
|---|---|---|
| Доступное хранилище | localStorage, sessionStorage, DOM Cache, IndexedDB, Broadcast Channel, Shared Workers, Service Workers | Партиционируется по сайту верхнего уровня |
| Cookie | Сторонние cookie | Партиционируются по умолчанию, есть способ вернуть непартиционированный доступ (см. ниже) |
| Сетевое состояние | HTTP-кеш, кеш изображений, кеш favicon, пул соединений, DNS, HSTS, идентификаторы TLS-сессий, OCSP, шрифты | Партиционируется постоянно; сайты не могут это ослабить |
Строка про сетевое состояние важна для приватности: эти механизмы никогда не предназначались для хранения данных, но ими можно злоупотребить как хранилищем. Именно в этом суть трюка с ETag и кеш-суперкуки: кешированный ответ, который один сайт может установить, а другой — считать обратно, становится каналом слежки. Партиционирование HTTP-кеша по сайту верхнего уровня закрывает его межсайтовую версию.
Как обстоят дела в разных браузерных движках
Честный ответ таков: единого «состояния веб-платформы» не существует, потому что движки пришли к партиционированию по отдельности и в разное время. Дальше — только то, что говорят первичные источники; любые номера версий стоит перепроверять по актуальной документации.
- Safari (WebKit) начал первым. Его Intelligent Tracking Prevention, появившаяся в 2017 году, определяет домены, способные отслеживать пользователя между сайтами, и либо партиционирует их cookie, либо удаляет данные их сайтов. В собственном анонсе Storage Access API от 2018 года WebKit описывает, как ITP оставляет встроенному контенту «только партиционированные cookie», как только он классифицирован как межсайтовый трекер. Там же отмечено, что первоначальная реализация API в WebKit охватывала только cookie и не меняла того, как партиционировались IndexedDB или
localStorage. - Firefox пришёл к этому через то, что называет State Partitioning (в маркетинге — Total Cookie Protection). MDN фиксирует ход внедрения: сетевое партиционирование включено по умолчанию для всех пользователей начиная с Firefox 85, а динамическое партиционирование — то есть та часть, что касается cookie, — включено по умолчанию начиная с Firefox 103, после более ранних этапов с ручным включением для Strict-режима (Firefox 86) и приватного просмотра (Firefox 90).
- Chromium шёл поэтапно. Вместо одного переключателя он партиционировал компоненты по отдельности — сначала HTTP-кеш, позже сторонние API хранилища — и дополнил это добровольным (opt-in) механизмом партиционированных cookie — CHIPS, о котором речь ниже. Точное поведение и пороги версий отличаются от Firefox, поэтому для нужного вам API смотрите данные о совместимости браузеров, а не предполагайте паритет.
Практическое следствие для разработчика: код, который работает в одном движке за счёт общего стороннего состояния, может незаметно сломаться в другом. Практическое следствие для пользователя: один и тот же трекер сталкивается с разными препятствиями в зависимости от вашего браузера.
Партиционированные cookie: CHIPS и атрибут Partitioned
Полная блокировка сторонних cookie ломает законные встраиваемые элементы: виджет чата или карта, запоминающие настройку для каждого сайта отдельно, слежкой не занимаются, но тотальная блокировка их ломает. CHIPS (Cookies Having Independent Partitioned State) — это компромисс: сайт включает партиционирование для cookie атрибутом Partitioned, и браузер хранит её под двойным ключом.
Set-Cookie: __Host-widget=abc123; Secure; Path=/; SameSite=None; Partitioned
Cookie, установленная таким образом, отправляется обратно только тогда, когда встраиваемый элемент загружен под тем же сайтом верхнего уровня, который был при её установке. Страница MDN о Storage Access API прямо называет компромисс: поскольку в cookie, которая не может следовать за вами между сайтами, нет риска для приватности, браузеры отправляют партиционированные cookie в запросах и делают их доступными встроенным ресурсам, — но раз cookie не разделяются между сайтами, они и не синхронизируются между сайтами автоматически.
Эта последняя оговорка — цена такой модели. Встраиваемому элементу по схеме «войти один раз и быть узнанным везде» — например, фрейму единого входа (SSO) — нужно что-то более мощное.
Запасной выход: Storage Access API
Storage Access API позволяет межсайтовому iframe запросить обратно свои обычные first-party cookie. Он появился в WebKit в 2018 году — в анонсе объясняется предыстория: отзывы разработчиков об ITP сводились к тому, что встроенному контенту нужен способ аутентифицировать пользователей, уже вошедших в свои first-party сервисы.
Схема состоит из двух методов:
document.hasStorageAccess()— промис, который разрешается значением, есть ли у фрейма уже непартиционированный доступ.document.requestStorageAccess()— запрашивает доступ; должен вызываться во время действия пользователя (MDN называет это transient activation), например при клике на кнопку «Войти» внутри фрейма.
Что означает разрешение и чего оно не означает:
- Оно выдаётся для пары, а не глобально. MDN описывает разрешение как хранящееся со структурой
<сайт верхнего уровня, встроенный сайт>. Доступ, выданныйexample.com, встроенному вembedder.com, не переносится наexample.com, встроенный где-либо ещё. - Оно возвращает first-party cookie, а не данные встраивающей страницы. В публикации WebKit прямо сказано, что доступ к хранилищу «никак не ослабляет политику одного источника (same-origin policy)» — это не означает, что третья сторона лезет в хранилище хост-страницы или наоборот.
- Запрос подтверждения зависит от браузера. По данным MDN, Safari и Chrome показывают запрос для встраиваемых элементов, которым доступ ранее не выдавался, а Firefox — только когда источник запросил доступ более чем на пороговом числе сайтов. В Chrome встраиваемые элементы и встраивающие страницы из одного набора связанных сайтов (related website set) могут обойтись без запроса.
- Firefox также выдаёт доступ по эвристикам. Чтобы не ломать распространённые интеграции, он может выдать встраиваемому элементу доступ на 30 дней после того, как пользователь взаимодействует с открытым им всплывающим окном, либо после быстрой последовательности «перешёл — взаимодействовал — вернулся». MDN называет эти механизмы переходными и предупреждает разработчиков не полагаться на них.
Замысел в том, чтобы возврат доступа был видимым решением, принимаемым для каждого сайта отдельно в контексте реального взаимодействия, — а не переключателем, который трекер может незаметно щёлкнуть сразу для всех сайтов.
Что партиционирование не останавливает
Важно точно очертить границу, потому что слово «партиционированный» легко прочитать как «приватный».
- Трекинг первой стороны не затронут. Сайт по-прежнему может узнавать собственных вернувшихся посетителей по своим cookie. Партиционирование нацелено только на межсайтовое связывание.
- Оно не влияет на другие межсайтовые приёмы. Техники, связывающие визиты иными способами, — например, bounce-трекинг, который ненадолго проводит вас через собственный сайт трекера, чтобы тот выполнился как first-party, — находятся вне двойного ключа. Как и определение того, в какие сайты вы вошли.
- Это не изоляция по выбору пользователя. Контейнеры Firefox отделяют ваши собственные идентичности друг от друга; партиционирование отделяет друг от друга встраиваемые элементы разных сайтов. Firefox применяет оба механизма одновременно, и ни один не заменяет другой — см. контейнерные вкладки и слежку.
- Это не режим инкогнито. Приватные окна сбрасывают состояние при закрытии; партиционирование действует и в обычных окнах. О том, как сайты всё же пытаются распознать приватный режим, читайте в статье про определение инкогнито.
- Это не защита от постоянного ID. Для идентификаторов, рассчитанных на то, чтобы пережить очистку, — им посвящена статья про постоянные Visitor ID, — партиционирование убирает один путь хранения, но не саму цель.
Почему это подтолкнуло трекеры к фингерпринтингу
Вот компромисс, сформулированный прямо. Партиционирование ломает межсайтовый трекинг на основе состояния: идентификатор, который трекер записал в хранилище на одном сайте, больше нельзя считать с другого. Все техники этого семейства опирались на то, что браузер держит одно общее хранилище на источник, — и это допущение исчезло.
Отпечаток этот путь не использует. Он вычисляется из того, что браузер и железо и так раскрывают, — вывод canvas и WebGL, шрифты, параметры экрана и оборудования, поведение аудио, — и на вашем устройстве ничего не записывается. Хранилище не задействовано, поэтому браузеру нечего партиционировать, очищать или блокировать. ID не сохраняется, а узнаётся заново при следующем визите по тем же входным данным.
Вот почему закрытие пути через хранилище повысило ценность пути через отпечаток для всех, кто всё ещё хочет связывать активность между сайтами. Чтобы увидеть, какие сигналы раскрывает ваш браузер, обратитесь к нашему руководству по фингерпринтингу браузера, где разобрана механика, а проверка отпечатка покажет всё в реальном времени.
Часто задаваемые вопросы
Партиционирование хранилища — это то же самое, что блокировка сторонних cookie?
Нет. Блокировка закрывает встраиваемому элементу доступ к хранилищу в стороннем контексте. Партиционирование даёт ему хранилище — отдельное для каждого сайта верхнего уровня, — поэтому встраиваемые элементы продолжают работать, но ничто из сохранённого на одном сайте не видно на другом.
Ломает ли партиционирование встроенные логины и виджеты?
Может. Встраиваемый элемент, который полагался на одну общую cookie на многих сайтах, чтобы узнавать вошедшего пользователя, обнаруживает на каждом сайте новое пустое хранилище. Поддерживаемые решения — requestStorageAccess() из Storage Access API (нужно действие пользователя) или, для состояния на уровне сайта, cookie с атрибутом Partitioned.
Мешает ли партиционирование сайту следить за мной на его собственных страницах?
Нет. First-party cookie привязана к сайту, на котором вы находитесь, поэтому партиционирование её не трогает. Оно лишает возможности связывать вашу активность между несвязанными сайтами через общее встроенное хранилище.
Одинаково ли партиционируют хранилище все браузеры?
Нет. Safari, Firefox и Chromium внедрили партиционирование в разное время и с разным охватом, а такие детали, как перечень охваченных API хранилища и момент появления запроса подтверждения, различаются. Прежде чем полагаться на то или иное поведение, проверьте актуальные данные о совместимости для конкретного API.
Заключение
Партиционирование хранилища — небольшое изменение с большим эффектом: добавление сайта верхнего уровня как второго ключа означает, что хранилище встроенного трекера на одном сайте и на другом — это просто два разных хранилища. Это убирает лазейку общего состояния, на которой держалась дешёвая межсайтовая слежка, но оставляет контролируемый путь назад — Storage Access API или cookie с атрибутом Partitioned, включаемую по выбору сайта, — для законных случаев, где это нужно. Слежка при этом не исчезает: борьба переносится туда, где хранилище вообще не требуется, и потому, когда ваш браузер партиционирует состояние, понимать отпечатки становится не менее, а более важно.
Рекомендуем прочитать:


