Смерть third-party cookies. Что ждет нас в 2024?
О чем речь?
Похоже, что в череде всевозможных инициатив по борьбе со «сторонними» куками начал проглядываться конец. Давайте разберемся, что происходит и зачем все это нужно.
Куки — браузерный механизм, который позволяет хранить данные по пользователю с привязкой к домену и затем передавать эти данные при запросах. Куку можно установить как со стороны сервера через специальный заголовок Set‑Cookie, так и в браузере прямо на странице через javascript. После этого кука будет передаваться при каждом запросе из браузера к домену.
Куки позволяют воплощать в жизнь множество как полезных, так и сомнительных механик. Из неприятных — возможность отслеживания пользователя сторонними сайтами или увеличение риска CSRF.
Как это работает сейчас?
First‑party куки — это куки того сайта, который открыт в вашем браузере (его домен вы видите в адресной строке). Thirt‑party — это куки запросов, которые ходят за данными на другие домены (загрузка картинок и скриптов с CDN, запросы за рекламой). Легко догадаться, что если какой‑то сервис установил свои скрипты на большинстве сайтов, то отслеживание пользователей для него остается лишь делом техники.
Помимо разделения на first и third‑party для нас также важно разделение на 3 режима работы кук с точки зрения разграничения по сайтам (чуть позже для нас это станет важно). Эти режимы работы регулируется параметром SameSite:
SameSite=None
означает, что кука не привязана к сайту. После того как она выставлена, она будет передаваться как в first‑party, так и в third‑party режиме.SameSite=Strict
означает, что кука передается только в first‑party режиме (пока находишься на сайте)SameSite=Lax
работает примерно какStrict
за тем исключением, что кука отправляется еще и при навигации в браузере (при запросе за исходным HTML)
В большинстве браузеров уже есть те или иные механизмы блокировки third‑party кук. В одних браузерах это ограничивается тем, что third‑party куки разрешено отправлять только на посещенные пользователем сайты. В других браузерах внедряют различные «enhanced» и «intellegent» tracking protection, либо совсем блокируют third‑paty куки. Для тех, кто захочет разобраться детально в перипетиях отдельных браузеров, есть прекрасные статьи.
В 2019 году волей сообщества дефолтное поведение кук в браузерах изменилось с SameSite=None
на SameSite=Lax
. Что, впрочем, привело лишь к тому, что большинство сервисов перевело свои трекеры в режим SameSite=None
.
Консенсус в сообществе свелся к тому, что:
— Нужно запретить отслеживать пользователя между сайтами
— First‑party — это вполне ОК. Сайт внутри себя может позволить себе любые манипуляции с пользовательскими данными
— Third‑party — в целом тоже ОК, но только если ограничить third‑party куки одним сайтом (следите за руками ниже)
Так какие же есть решения для того, чтобы отказаться от third‑party кук и ничего не сломать?
Какие есть решения?
На данный момент механизма разграничения third‑party кук не существует, поэтому для решения проблемы была изобретена еще одна «фича» — Cookies Having Independent Partitioned State (CHIPS). «Чипсы» (CHIPS) приходят на замену «печенек» (Cookies), кек. В рамках этой фичи у кук появляется еще один атрибут — Partitioned
. Кука с таким атрибутом будет отправляться только с того сайта, на котором она была выставлена.
Например, пользователь заходит на сайт https://first-site.com
. Сайт загружает картинку с https://avatars.com
. В ответ https://avatars.com
отправляет заголовок Set-Cookie
с атрибутом Partitioned
. Эта кука будет отправляться из браузера при каждом запросе к https://avatars.com
с сайта https://first-site.com
. Однако когда пользователь зайдет на другой сайт https://second-site.com
и попытается загрузить ту же картинку с сайта https://avatars.com
, имеющаяся кука уже не будет отправлена. Вместо этого сервер может выставить для пользователя новую куку с атрибутом Partitioned
, которая будет отправляться только с сайта https://second-site.com
.
При этом внедрение Partitioned кук подразумевает постепенный отказ от всех остальных third‑party кук. Все куки, работающие в режиме SameSite=None
без выставленного атрибута Partitioned
просто перестанут отправляться из third‑party окружения. Именно перестанут отправляться, а не просто будут сконвертированы в Partitioned. Гугл планирует раскатить эту фичу в 2 этапа — на 1% пользователей хрома с января 2024 и на 100% пользователей осенью 2024.
Это сломает множество сценариев использования сайтов. Очень часто сайтам нужно шарить куку между доменами (например между google.com и google.ru). Для того чтобы дать сайтам такую возможность, была изобретена еще одна «фича» — Storage Access API (SAA). Она позволит получить доступ к SameSite=None
куке без атрибута Partitioned
для запросов из third‑party окружения, но только с явного разрешения браузера или пользователя.
Как пример — я захожу на сайт first-site.com
, на котором встроена авторизация через сторонний сервис authorization.com
. Сайт встраивает iframe стороннего сервиса. Внутри iframe вызывается скрипт, который проверяет, может ли он получить доступ к своей SameSite=None
куке. Проверка происходит через вызов await navigator.permissions.query({name: 'storage-access'})
. Если пользователь ни разу не был на сайте authorization.com
, или окружение не соответствует другим критериям — получаем отказ. Пример того, как это должно работать, лежит тут. Если промис резолвится с объектом { state: 'prompt' }
, значит для получения доступа к SameSite=None
куке необходимо спросить разрешение у пользователя.
После этого можно запросить разрешение через вызов await document.requestStorageAccess()
. При необходимости апрува пользователя, prompt будет выглядеть примерно так:
authorization.com wants to use info they’ve saved about you
authorization.com will know that you visited first‑site.com.
Learn more about embedded content
Промис резолвится — значит у нас появился доступ к SameSite=None
кукам authorization.com
(как через document.cookie
, так и к HttpOnly кукам при запросах из айфрейма). Реджект — извините.
При каждом обновлении страницы доступ к кукам необходимо перезапрашивать через requestStorageAccess
. Prompt показывается только один раз (последующие разы разрешение выдается автоматически).
Все, что написано ниже, мне удалось найти в документации, но не посчастливилось проверить своими руками. Если вам удастся, напишите в комментариях:)
Так, ну с iframe проблема, хоть и неудобно, но решается. А как быть с ресурсами типа картинок, которые не могут подгрузить для себя скрипт и запросить разрешение? Для таких случаев разработчики Chrome придумали еще одну «фичу» — requestStorageAccessFor. Это API позволяет сделать «обратный финт» — запросить доступ к кукам не с самого домена, а «для» домена. Например, я захожу на сайт first-site.com
и собираюсь загрузить аватарку с домена avatars.com
. Прежде, чем начать загрузку, я запрашиваю разрешение через await document.requestStorageAccessFor('https://avatars.com')
. Если все успешно — при запросе за картинкой отправятся SameSite=None
куки с домена avatars.com
.
Chrome также внедряет еще одну полезную «фичу», про которую нужно знать. Related Website Sets (RWS) позволяет создавать группы сайтов с общими куками. Для того чтобы это заработало — нужно буквально закоммитить список своих сайтов в специальный файлик на гитхабе. В этом файле указывается primary
сайт, associatedSites
— которые являются неотъемлемой частью сервиса и требуют обмена пользовательскими данными, а также их ccTLDs
для разных регионов. Чтобы подтвердить свои благородные намерения, необходимо добавить на свой сайт файлик /.well-known/related-website-set.json
с теми же данными. Если вы сделали все правильно, вам не нужно будет запрашивать разрешение у пользователя для обмена куками.