вернуться к обзору Больше статей

Отпечаток по батарее: ваш уровень заряда — это трекер

Проверка утечки данных батареи...

Вы открыли режим инкогнито. Вы очистили куки. А сайт всё равно обошёлся с вами как с тем же посетителем, которого видел десять минут назад. Одна из самых незаметных причин, почему так продолжает происходить, сидит в углу вашего экрана: индикатор батареи.

Почти всё прошлое десятилетие любой сайт мог спросить у вашего браузера, насколько именно заряжен ваш ноутбук — с подозрительно точным числом — и сколько секунд жизни ему осталось. Считайте эти числа на двух разных сайтах с разницей в мгновение — и этого хватит, чтобы решить, что оба посетителя — один и тот же человек. Панель на этой странице задаёт вашему браузеру тот же вопрос прямо сейчас и показывает вам ответ.

Если коротко: отпечаток по батарее держится на двух числах. Battery Status API выдаёт ваш уровень заряда и время до разрядки, и в любой конкретный момент эта пара почти уникальна — так что она работает как недолговечный ключ, который заново связывает вас между сайтами и окнами инкогнито без всякой куки. Firefox из-за этого удалил весь API целиком; Chrome по-прежнему отдаёт урезанную версию.

Четыре числа, которые выдаёт Battery Status API

Вызовите navigator.getBattery() — и получите объект с четырьмя полями: level от 0 до 1, charging (true или false), chargingTime в секундах до полной зарядки и dischargingTime в секундах до полной разрядки. Ни серийного номера, ничего похожего на идентификатор. Именно поэтому в начале 2010-х он вошёл в браузеры почти без сопротивления. Ну что такого может выдать процент заряда батареи?

Как выяснилось — энтропию. Один только dischargingTime попадает куда-то в диапазон шириной в тысячи секунд. Соедините его с level, который когда-то сообщался с множеством знаков после запятой, — и вы получите значение, которое в эту самую секунду держат очень немногие другие люди в сети. Это не имя. Это число, достаточно редкое, чтобы какое-то время работать как имя.

Как два сайта находят друг друга с его помощью

Механизм был подробно описан в 2015 году исследователями Olejnik, Acar, Castelluccia и Díaz в статье под названием The leaking battery. Показание — это метка для повторной идентификации со сроком годности. Сайт A записывает ваши level и dischargingTime; мгновением позже сайт B записывает почти ту же пару; и эти две записи можно объединить без всякого обмена куками. Показание переживает очищенную корзину с куками и переключение в приватный режим, потому что ничего из этого не хранится в браузере. Каждый раз, когда вы спрашиваете, значение считывается заново прямо с оборудования.

Одно показание батареи, два сайта
shop.example
47%
до разрядки: 2:12
=
news.example
47%
до разрядки: 2:12
Та же метка — два визита связаны
Оба сайта считывают один и тот же заряд и время до разрядки, поэтому могут объединить вас в один профиль без всякой куки.
Показание — это ключ для объединения, а не сохранённый идентификатор. Оно связывает два сайта, которые видят его близко друг к другу по времени, и истекает по мере изменения заряда батареи — годится примерно на тридцать секунд — пару минут.

Окно короткое. Числа дрейфуют по мере разрядки батареи, так что совпадение держится всего около тридцати секунд — пары минут. Тридцати секунд с лихвой хватает, чтобы сшить две загрузки страниц в одного посетителя. Через год после статьи принстонский обход миллиона сайтов обнаружил, что реальные трекинговые скрипты уже читали этот API в реальных условиях, — то есть атака уже вышла за стены лаборатории.

Трюк с ёмкостью: как достать mAh из воздуха

Вот часть, которую пропускают другие разборы. Операционная система вычисляет level как charge_now / charge_full — два целых числа в собственных единицах батареи, микроампер-часах на типичном ноутбуке с Linux. Значит, дробь, которую сообщает ваш браузер, не произвольна. Это отношение, знаменатель которого — ёмкость вашей батареи при полном заряде. Firefox на Linux когда-то отдавал сырое отношение с полной точностью, что-то вроде 0.9301926, и из настолько точного показания можно прогнать дробь в обратную сторону и восстановить лежащий под ней знаменатель.

Извлекаем ёмкость из десятичных знаков
Ваш браузер сообщает
battery.level = 0.9301926
Наименьшая дробь, которая совпадает
4650963 / 5000000
Ёмкость восстановлена: ~5000 mAh
Этот знаменатель — ёмкость при полном заряде в микроампер-часах. Она падает по мере старения батареи, поэтому это сигнал, уникальный для вашей физической ячейки.
Уровень заряда — это charge-now, делённое на charge-full, поэтому дробь прячет ёмкость в своём знаменателе. Полная точность её выдаёт; округление до процента её отнимает. Панель вверху этой страницы выполняет это на вашей реальной батарее.

Этот знаменатель — ваша ёмкость в mAh, и это идентификатор получше, чем когда-либо был уровень заряда, потому что ёмкость батареи при полном заряде медленно снижается по мере старения. Она уникальна для вашей физической ячейки в её текущей точке износа. Современный Chromium по большей части прикрыл это, округляя level до целого процента, так что единственный знаменатель, который удаётся вытащить обратно, — это 100, и настоящая ёмкость исчезает. Панель на этой странице выполняет такую реконструкцию на вашем текущем показании и почти наверняка сообщит, что значение квантовано. Этот провал — это и есть защита, делающая свою работу.

Почему Firefox просто удалил всё это целиком

Mozilla не стала латать battery API. Она его удалила — в Firefox 52 в 2017 году, вся поверхность navigator.getBattery исчезла целиком. Apple же изначально так и не выпустила его в Safari. Для веб-функции это грубый ответ, и он говорит о том, как мир стандартов в итоге увидел этот случай: вектор слежки без какой-либо пользы для того, за кем следят. Chrome и Edge всё ещё несут его в себе — теперь отгороженный так, что доступен только защищённым страницам верхнего уровня, и с округлением вниз, — а сама спецификация теперь открывается предупреждением о приватности, которого у неё не было в 2012 году. Если вы читаете это в Firefox или Safari, тест сверху возвращается чистым, потому что там просто нечего отвечать. То же разделение действует и на телефонах: Chrome на Android тоже выдаёт показание, тогда как Safari на iOS и Firefox для Android не выдают ничего.

Дело никогда не было только в батарее

Батарея — лишь одна строка в списке подлиннее, который ваш браузер зачитает любому сайту по запросу, и нигде никакого запроса разрешения. navigator.hardwareConcurrency выдаёт число логических ядер вашего процессора. navigator.deviceMemory сообщает объём вашей оперативной памяти, огрублённый до степени двойки — долго ограниченный сверху восемью на десктопе, хотя нынешний Chrome теперь отдаёт также 16 и 32. maxTouchPoints говорит, есть ли у вас сенсорный экран. Адаптер WebGPU передаёт производителя и архитектуру вашего GPU обычными строками. Ни один из этих сигналов сам по себе вас не выдаёт. Сложенные вместе, они сужают толпу, в которой вы прячетесь, до горстки — точно так же, как это делают рендеринг canvas и обработка звука в наших разборах фингерпринтинга canvas и аудиоотпечатка. Панель «Оборудование» в полном сканировании браузера показывает точные значения, которые ваш браузер выдаёт прямо сейчас.

Что с этим делать на самом деле

  1. Посмотрите, что вы выдаёте, прежде чем решить, что вы защищены. Запустите сканирование браузера и прочитайте панели батареи и оборудования. Гадать, что вы выдаёте, — не то же самое, что посмотреть.
  2. Инкогнито здесь не помогает. Повторная привязка идёт через оборудование, а не через куки, так что приватное окно с той же батареей и тем же GPU — это тот же посетитель. Об этом стоит помнить везде, где вы на него рассчитывали.
  3. Firefox и Safari уже закрывают вектор батареи. Если вы на Chrome, то именно поддержание актуальной версии удерживает уровень заряда квантованным, а не сырым.
  4. Если вы жонглируете несколькими личностями, сделайте всю историю с оборудованием согласованной. Подмена одного значения, когда остальные с ним не сходятся, — это ровно то противоречие, за которым охотятся системы фингерпринтинга. Incogniton строит каждый профиль так, чтобы батарея, GPU, ядра и память, которые он предъявляет, складывались в одну правдоподобную машину, а не в кучу противоречий.

Индикатор батареи — слишком мелкая вещь, чтобы выдать так много. Направьте собственный браузер на сканирование и посмотрите, что всё ещё говорит ваш.

Логотип Incogniton

Повысьте уровень своей приватности

Стрелка