Заголовок ETag существует для экономии трафика при ревалидации кеша — но уникальный ETag превращает кеш браузера в идентификатор для слежки.
Очистка cookie ощущается как нажатие кнопки сброса, но на деле опустошает только одну конкретную банку. Заголовок ETag придуман для рутинной задачи — сказать браузеру «у тебя уже есть актуальная версия файла, повторно скачивать не нужно», — и именно этот механизм можно превратить в способ выдать каждому посетителю уникальный, незаметно подтверждаемый идентификатор, который живёт в кеше браузера, а не в хранилище cookie. Кеш-суперкука, построенная так, переживает очистку cookie именно потому, что никогда ею не была.
Кратко
- ETag задуман как идентификатор версии кешированного файла, но ничто не мешает серверу сделать его уникальным для каждого посетителя, а не для каждой версии контента — превращая обычную ревалидацию кеша в трекинговый обмен запросами.
- Техника не теоретическая — она реально применялась: проект evercookie Сэми Камкара реализовал рабочий механизм хранения на базе ETag ещё в 2010 году, а в 2011-м KISSmetrics уличили в использовании ETag (вместе с Flash-cookie) для «возрождения» ID, которые пользователи специально удаляли — что привело к коллективному иску.
- ETag — не единственный вектор трекинга через кеш: флаги HSTS, кеширование favicon и другое состояние браузера, которое сайт может и установить, и позже считать обратно, использовались тем же образом; ETag — просто наиболее подробно задокументированный случай.
- Современные браузеры закрыли межсайтовую версию этой дыры. Начиная с Chrome 86 Chrome партиционирует HTTP-кеш по сайту верхнего уровня: запись кеша (и её ETag), установленная на одном сайте, больше не читается с другого. Safari и Firefox выпустили собственные версии такого же партиционирования.
- Трекинг через ETag внутри одного сайта по-прежнему работает, потому что партиционирование блокирует только межсайтовое использование общего кеша — отдельный сайт всё ещё может выдать вашему браузеру уникальный ETag и считывать его при каждом возвращении на этот же сайт, вообще без cookie.
Для чего на самом деле нужен ETag
ETag — это заголовок HTTP-ответа, который сервер прикрепляет к ресурсу (изображению, скрипту, ответу API) в качестве идентификатора версии. Собственное описание MDN недвусмысленно: он «позволяет кешам работать эффективнее и экономить трафик, поскольку веб-серверу не нужно повторно отправлять полный ответ, если контент не изменился». Способ генерации значения не стандартизирован; на практике это обычно хеш содержимого, хеш метки времени последнего изменения или просто номер ревизии.
Цикл ревалидации прост:
- Браузер запрашивает ресурс. Сервер отвечает содержимым и заголовком
ETag: "abc123". - Браузер кеширует и содержимое, и это значение ETag.
- При следующем запросе того же ресурса браузер не скачивает его заново вслепую, а отправляет
If-None-Match: "abc123". - Если текущий ETag сервера всё ещё совпадает, он отвечает пустым
304 Not Modified— минимум трафика. Если содержимое изменилось, приходит новая версия с новым ETag.
Этот обмен — браузер доказывает, что у него уже что-то есть, сервер подтверждает, актуально ли это — по форме в точности совпадает с проверкой cookie. Разница только в том, в каком заголовке едет идентификатор.
Как ETag превращается в идентификатор для слежки
Спецификация HTTP нигде не требует, чтобы ETag представлял именно версию контента. Сервер вполне может при первой загрузке трекингового пикселя или скрипта сгенерировать уникальный, привязанный к посетителю ETag, сохранить это значение у себя вместе с сессией и вернуть его как ETag: "visitor-8f2c91a4". Браузер не может отличить легитимный токен свежести кеша от трекингового токена, поэтому послушно кеширует его и на каждом следующем запросе этого ресурса возвращает обратно через If-None-Match — незаметно повторно опознавая вас при каждой загрузке страницы, без единого cookie, без записи в localStorage и вообще без JavaScript.
Именно это продемонстрировал в 2010 году проект evercookie Сэми Камкара: наряду с HTML5-хранилищем, Flash Local Shared Objects и полудюжиной других механизмов он включал компонент на базе ETag с серверной поддержкой — сохранял идентификатор в HTTP ETag и считывал его обратно на следующем запросе специально для того, чтобы ID можно было восстановить даже после очистки cookie. Заявленная цель проекта — показать, сколько независимых от cookie векторов хранения может собрать воедино настойчивый трекер, и кеш ETag оказался одним из самых живучих, поскольку удаление cookie никогда не затрагивает HTTP-кеш браузера.
Техника быстро перешла от концепт-демонстрации к продакшену: в 2011 году исследователи и журналисты задокументировали, что аналитический сервис KISSmetrics — использовавшийся сайтами вроде Hulu и Spotify — применял ETag вместе с Flash-cookie для respawning («возрождения») идентификаторов, которые пользователи явно удаляли. Возникший скандал привёл к урегулированию коллективного иска и остаётся самым наглядным реальным примером трекинга через ETag, применённого в масштабе, а не просто продемонстрированного как концепция.
Слежка через кеш — это не только ETag
ETag привлекает больше всего внимания как самый наглядный пример, но это лишь один представитель более широкого семейства кеш-суперкук — идентификаторов, спрятанных в любом состоянии браузера, которое сайт может и установить, и позже считать обратно, минуя банку cookie:
| Вектор | Что устанавливается | Что считывается обратно |
|---|---|---|
| Трекинг через ETag | Уникальный ETag на кешированном ресурсе | If-None-Match при следующем запросе |
| HSTS-суперкука | Какие поддомены из набора «закреплены» через HSTS (каждый бит кодирует часть ID) | Обновляет ли браузер каждый поддомен до HTTPS без сетевого запроса |
| Кеширование favicon | Favicon с уникальным URL, кешируемый отдельно от прочих хранилищ | Запрашивает ли браузер его заново или отдаёт кешированную копию |
| Тайминг Cache-Control | Присутствует ли ресурс в кеше | Разница во времени загрузки между попаданием в кеш и новым запросом |
Все четыре вектора используют тот же структурный приём, что и bounce-трекинг и CNAME-маскировка: они эксплуатируют механизм, изначально не предназначенный для слежки, поэтому специфичные для cookie средства защиты — блокировка сторонних cookie, очистка банки cookie — на них попросту не действуют.
Почему это переживает очистку cookie и приватный режим
Очистка cookie опустошает только хранилище cookie. HTTP-кеш — отдельная подсистема со своим хранилищем и, в большинстве браузеров, отдельным флажком «очистить» в настройках приватности — который пользователи часто пропускают, поскольку диалог «очистить данные браузера» перечисляет кеш и cookie как независимые опции. ETag, записанный в кеш, остаётся там нетронутым, пока не будет очищен сам кеш или пока запись не устареет естественным образом.
Приватные/инкогнито-окна тоже принципиально не помогают: в течение работы приватной сессии по-прежнему используется HTTP-кеш, поэтому ETag, установленный ранее в этой же приватной сессии, ведёт себя точно так же, как в обычном окне, — он отбрасывается только при закрытии окна, вместе со всем остальным. Наша статья о том, как сайты определяют режим инкогнито, рассматривает смежную, но другую проблему — сайты замечают, что вы находитесь в приватном окне, по особенностям квоты хранилища, — это отдельный вопрос от того, сохраняются ли идентификаторы на основе кеша внутри одной сессии.
Меры браузеров: партиционирование кеша закрыло межсайтовую дыру
Главное структурное исправление было нацелено не конкретно на ETag — оно затронуло сам HTTP-кеш. Начиная с Chrome 86 Chrome партиционирует HTTP-кеш по сайту верхнего уровня: кешированный ресурс теперь ключуется не только по URL, а по URL плюс сайту верхнего уровня, инициировавшему запрос (в более поздних доработках — ещё и по сайту фрейма). Собственное описание команды Chrome прямо называет мотивацией именно такие злоупотребления — наличие ресурса в кеше использовалось и для определения истории просмотров, и для внедрения межсайтовых трекинговых идентификаторов, а партиционирование убирает общий кеш, который делал возможными оба приёма. Safari и Firefox с тех пор выпустили собственные сопоставимые схемы партиционирования.
Практический эффект: трекинговый скрипт, встроенный на site-a.com, больше не может установить ETag, который будет считан обратно при загрузке того же скрипта на site-b.com, потому что теперь у каждого сайта своя изолированная часть кеша. Межсайтовый трекинг через ETag — версия, позволявшая одному рекламному вендору сопоставлять вас на всех сайтах, куда он был встроен, — в современных браузерах в основном закрыт.
Что партиционирование не исправляет — это трекинг внутри одного сайта: сайт, который вы посещаете регулярно, по-прежнему может назначить вашему браузеру уникальный ETag при первой загрузке и считывать его при каждом возвращении, целиком в рамках собственного раздела кеша, без единого cookie. Партиционирование кеша создавалось, чтобы остановить межсайтовую корреляцию, а не чтобы помешать сайту узнавать своих постоянных посетителей нецookie-способом.
Как обнаружить и снизить риск трекинга через ETag
Не существует единственного переключателя, который нейтрализует это так же, как «блокировать сторонние cookie» нейтрализует классический трекинг, — именно поэтому собственный issue Privacy Badger об обнаружении ETag-суперкук (EFForg/privacybadger#2136) до сих пор открыт: легитимные серверы постоянно выставляют ETag для обычного кеширования, поэтому отличить снаружи версионный идентификатор от трекингового — по-настоящему сложная эвристическая задача, а не простое сопоставление с шаблоном.
Кое-что реально помогает:
- Очищайте HTTP-кеш, а не только cookie. В большинстве браузеров диалог «очистить данные браузера» перечисляет «кешированные изображения и файлы» отдельным пунктом от «cookie и другие данные сайтов» — отметьте оба.
- Используйте приватный/инкогнито-режим для сессий, которые не хотите связывать друг с другом, поскольку его кеш (как и cookie) отбрасывается при закрытии окна — но это не остановит трекинг внутри самой этой сессии.
- Полностью отключайте кеширование для чувствительных контекстов через опцию «Disable cache» в DevTools браузера или расширение для приватности, которое очищает или рандомизирует обработку
ETag/If-None-Match— ценой потери экономии трафика, ради которой ETag и существует. - Полагайтесь на то, что партиционирование кеша уже делает свою работу для межсайтового случая — оно включено по умолчанию во всех основных браузерах, так что самое ценное исправление уже произошло без каких-либо действий с вашей стороны.
Ничто из этого не требует чтения JavaScript на странице, потому что суть техники именно в том, что она вообще не затрагивает ни один API хранилища, который скрипт мог бы проверить, — она целиком живёт в заголовках HTTP, которыми обмениваются до загрузки контента страницы.
Сам обмен ETag происходит на уровне, недоступном любому скрипту страницы, но кеш-идентификатор редко применяют в одиночку — трекеры связывают его со всем остальным, что раскрывает ваш браузер. Проверка отпечатка от BrowserInsight показывает canvas, WebGL, хранилища и другие клиентские сигналы, которые трекер объединил бы с идентификатором на основе кеша, — всё измеряется прямо в вашем браузере и никуда не отправляется для анализа.
Часто задаваемые вопросы
Останавливает ли приватное/инкогнито-окно трекинг через ETag?
Только между сессиями, но не внутри одной. Кеш приватного окна отбрасывается при его закрытии, поэтому ETag, установленный в этой сессии, нельзя считать обратно после повторного открытия браузера — но пока это приватное окно открыто, повторное опознавание через ETag работает точно так же, как в обычном окне.
Останавливает ли блокировка сторонних cookie ETag-суперкуки?
Нет, и именно поэтому техника когда-то привлекла внимание. Блокировка сторонних cookie проверяет конкретно заголовок Cookie; она никак не видит ETag или If-None-Match, поэтому идентификатор на основе кеша беспрепятственно проходит мимо блокировщика cookie. По-настоящему останавливает именно межсайтовую версию партиционирование HTTP-кеша — отдельный механизм браузера.
Может ли VPN или простая очистка cookie остановить это?
Нет, ни то ни другое. VPN меняет ваш IP-адрес, а не содержимое кеша браузера. Очистка cookie опустошает хранилище cookie, но совершенно не трогает HTTP-кеш — и любой ETag внутри него, — а это именно та щель, которую техника и предназначена эксплуатировать.
Используется ли трекинг через ETag сегодня?
Межсайтовый трекинг через ETag стал заметно менее эффективным с тех пор, как Chrome, Safari и Firefox выпустили партиционирование HTTP-кеша, которое не даёт записи кеша (и её ETag), установленной на одном сайте, быть считанной с другого. Трекинг внутри одного сайта — когда сайт повторно опознаёт собственных постоянных посетителей — технически по-прежнему возможен и вообще не затрагивается партиционированием кеша.
Заключение
Трекинг через ETag напоминает, что совет «очистите cookie» всегда касался одного конкретного механизма хранения, а не был гарантией того, что вас перестанут узнавать. HTTP-кеш существует, чтобы веб работал быстрее, и любой механизм, который сервер может и записать, и позже считать обратно — ETag, флаг HSTS, URL favicon, — можно превратить в идентификатор в тот момент, когда ему присваивается уникальность вместо честной версии контента. Партиционирование кеша закрыло самую опасную, межсайтовую часть проблемы; внутрисайтовый случай меньше и тише, но всё ещё стоит держать в уме, когда в следующий раз очистка cookie, кажется, ничего не сбросила.
Рекомендуем почитать:


