Фингерпринтинг частоты кадров: отслеживание
Как тайминг requestAnimationFrame и частота обновления дисплея создают сигналы отпечатков, и техники контроля частоты кадров на уровне движка.
Нужна поддерживаемая продуктовая документация?
У этой статьи есть соответствующая страница в центре документации. Используйте docs для каноничного сценария настройки, актуальных флагов и долгосрочной справки.
Введение
API requestAnimationFrame был создан для помощи разработчикам в создании плавных анимаций путём синхронизации операций отрисовки с циклом обновления дисплея. Вместо произвольных таймеров вроде setTimeout, requestAnimationFrame вызывает функцию перед следующей перерисовкой экрана, обычно с нативной частотой обновления монитора. Это обеспечивает более плавные анимации, лучшую энергоэффективность и более отзывчивый пользовательский опыт.
Однако частота обратных вызовов requestAnimationFrame напрямую раскрывает частоту обновления дисплея. Стандартный монитор 60 Гц вызывает обратные вызовы приблизительно каждые 16,67 миллисекунд, тогда как игровой монитор 144 Гц - каждые 6,94 миллисекунды. Дисплей 120 Гц, 240 Гц и дисплей с переменной частотой обновления (VRR) - каждый производит измеримо различные интервалы обратных вызовов. Эта информация доступна любому веб-сайту без запроса разрешений, что делает её надёжным и постоянным сигналом фингерпринтинга.
Влияние на приватность
Частота обновления дисплея - это аппаратная характеристика, о которой пользователи редко задумываются в контексте приватности. В отличие от cookies или IP-адресов, частота обновления привязана к физическому оборудованию монитора и не меняется между сессиями браузера, режимами приватного просмотра или после очистки данных сайтов.
Риск фингерпринтинга значительно возрос по мере диверсификации дисплейных технологий. Десять лет назад почти все потребительские мониторы работали на 60 Гц, предоставляя мало отличительной информации. Сегодня рынок включает панели 60 Гц, 75 Гц, 90 Гц, 120 Гц, 144 Гц, 165 Гц, 240 Гц и 360 Гц. Конфигурации с несколькими мониторами могут сообщать разные частоты в зависимости от того, на каком дисплее находится окно браузера. Ноутбуки с функцией динамической частоты обновления (такой как Apple ProMotion на 120 Гц) добавляют дополнительную вариативность.
Это разнообразие означает, что информация о частоте кадров теперь несёт значимую энтропию. Посетитель с монитором 165 Гц встречается значительно реже, чем с дисплеем 60 Гц, что делает частоту обновления ценным сигналом для сужения идентичности. В сочетании с разрешением экрана, глубиной цвета, информацией о GPU и другими сигналами, связанными с дисплеем, частота кадров вносит вклад в детальный аппаратный профиль.
Проблема усугубляется тем, что несколько API раскрывают эту информацию. Помимо тайминга requestAnimationFrame, CSS-анимации, Screen API и даже тайминг обратных вызовов setTimeout (которые браузер выравнивает по циклу дисплея) могут раскрыть частоту обновления.
Техническая основа
Как requestAnimationFrame раскрывает частоту обновления
Когда веб-сайт вызывает requestAnimationFrame(callback), браузер вызывает функцию обратного вызова перед каждой перерисовкой экрана. Обратный вызов получает параметр DOMHighResTimeStamp, указывающий, когда начался кадр. Измеряя интервал между последовательными метками времени, веб-сайт может определить частоту обновления дисплея с высокой точностью.
Измерение просто: собрать от 30 до 60 временных меток кадров, вычислить средний интервал и определить частоту. Уже при 10 кадрах частоту обновления можно определить надёжно. Измерение занимает менее 200 миллисекунд на дисплее 60 Гц и невидимо для пользователя.
CSS-анимации и переходы
CSS-анимации по умолчанию работают с частотой обновления дисплея. Анимация @keyframes, длящаяся ровно одну секунду, произведёт 60 промежуточных кадров на дисплее 60 Гц и 144 кадра на 144 Гц. Хотя спецификация CSS не гарантирует покадровую точность, тайминг событий анимации (animationstart, animationend, тайминг переходов) всё же раскрывает информацию о частоте обновления.
Сложность переменной частоты обновления
Современные дисплеи с технологией переменной частоты обновления (G-Sync, FreeSync, ProMotion) могут динамически менять частоту обновления в зависимости от контента. Это создаёт дополнительное измерение фингерпринтинга: не только базовая частота обновления, но и паттерн адаптивного поведения становится сигналом. Дисплей, переключающийся между 48 Гц и 120 Гц в зависимости от активности контента, производит характерный временной паттерн, отличный от дисплея с фиксированной частотой.
Конфигурации с несколькими мониторами
Когда окно браузера занимает несколько мониторов или перемещается между дисплеями с разной частотой обновления, тайминг requestAnimationFrame соответственно сдвигается. Этот паттерн перехода может раскрыть конфигурации с несколькими мониторами, которые сами по себе являются сигналом фингерпринтинга.
Распространённые подходы защиты и их ограничения
VPN и прокси-серверы
VPN не влияют на частоту обновления дисплея. Частота кадров - это локальное аппаратное свойство, измеряемое полностью в конвейере рендеринга браузера. Средства приватности сетевого уровня не могут его изменить.
Инкогнито и приватный просмотр
Режимы приватного просмотра не изменяют частоту обновления дисплея или поведение requestAnimationFrame. Отпечаток частоты кадров в инкогнито идентичен отпечатку в обычном окне.
Расширения браузера
Расширения, пытающиеся модифицировать поведение requestAnimationFrame, сталкиваются со значительными трудностями:
- Оборачивание обратного вызова: расширение может перехватить
requestAnimationFrameи ограничить обратные вызовы до целевой частоты (например, 60 FPS). Однако это создаёт видимые подёргивания в обычных анимациях и обнаруживаемо при сравнении таймингаrequestAnimationFrameс таймингом CSS-анимаций, которые расширение может не контролировать. - Модификация временных меток: изменение
DOMHighResTimeStamp, передаваемого в обратные вызовы, может симулировать другую частоту обновления, но несоответствия сperformance.now(),Date.now()и таймингом CSS-переходов раскрывают модификацию. - Блокировка API: полное отключение
requestAnimationFrameнарушает работу большинства современных веб-приложений, которые используют его для рендеринга.
Фундаментальная проблема в том, что расширения работают на уровне JavaScript API и не могут контролировать фактический конвейер рендеринга. Реальная частота обновления дисплея влияет на несколько подсистем браузера одновременно, и модификация одного API без контроля всех остальных создаёт обнаруживаемые несоответствия.
Ограничение частоты кадров на уровне браузера
Некоторые браузеры предлагают ограничение частоты кадров в настройках разработчика. Этот подход ограничивает частоту рендеринга, но не обеспечивает детальный контроль, не может быть задан для каждой сессии и часто вносит визуальные артефакты, указывающие на ограничение.
Подход BotBrowser на уровне движка
BotBrowser контролирует частоту кадров на уровне конвейера рендеринга движка браузера через флаг --bot-fps. Это не JavaScript-обёртка и не перехватчик API. Сам цикл рендеринга работает с заданной частотой, обеспечивая внутреннюю согласованность всех зависящих от частоты кадров сигналов.
Частота кадров на основе профиля
При использовании режима profile BotBrowser считывает целевую частоту кадров из загруженного профиля отпечатка:
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=profile \
--user-data-dir="$(mktemp -d)"
Профиль определяет ожидаемую частоту обновления дисплея на основе целевого устройства. Профиль стандартного офисного ноутбука сообщает 60 FPS. Профиль игрового десктопа может сообщать 144 FPS. Конвейер рендеринга работает с этой частотой, поэтому обратные вызовы requestAnimationFrame, CSS-анимации и все остальные зависящие от кадров тайминги соответствуют характеристикам дисплея профиля.
Произвольная частота кадров
Для конкретных сценариев вы можете задать точную частоту кадров:
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=60
Это принуждает конвейер рендеринга работать на 60 FPS независимо от реальной частоты обновления хостового дисплея. Все зависящие от тайминга измерения будут сообщать приблизительно 16,67 мс на кадр.
Реальная частота дисплея
Когда вы хотите, чтобы браузер использовал реальную частоту обновления дисплея:
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=real
Согласованность на уровне движка
Поскольку контроль применяется на уровне конвейера рендеринга, все зависящие от частоты кадров сигналы согласованы:
- Обратные вызовы
requestAnimationFrameвызываются с контролируемой частотой - Временные метки кадров соответствуют ожидаемому интервалу
- CSS-анимации продвигаются с правильной скоростью
- Тайминг
performance.now()во время кадров анимации соответствует заявленной частоте - Нет расхождений между измеренной через JavaScript частотой кадров и фактическим поведением рендеринга
Конфигурация и использование
Базовое использование CLI
# Использование частоты кадров из профиля
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=profile \
--user-data-dir="$(mktemp -d)"
# Фиксированные 60 FPS
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=60
# Реальная частота дисплея
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=real
Интеграция с Playwright
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-fps=60',
],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('https://example.com');
// requestAnimationFrame будет вызываться с частотой 60 FPS
await browser.close();
})();
Интеграция с Puppeteer
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-fps=profile',
],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
Комбинирование с защитой тайминга
Для полного контроля аппаратных сигналов комбинируйте контроль частоты кадров с масштабированием тайминга:
chrome --bot-profile="/path/to/profile.enc" \
--bot-fps=60 \
--bot-time-scale=1.0 \
--bot-noise-seed=42 \
--user-data-dir="$(mktemp -d)"
Верификация
После запуска BotBrowser с настроенной частотой кадров проверьте результат:
const measuredFps = await page.evaluate(() => {
return new Promise(resolve => {
const timestamps = [];
function frame(ts) {
timestamps.push(ts);
if (timestamps.length < 61) {
requestAnimationFrame(frame);
} else {
const intervals = [];
for (let i = 1; i < timestamps.length; i++) {
intervals.push(timestamps[i] - timestamps[i - 1]);
}
const avgInterval = intervals.reduce((a, b) => a + b) / intervals.length;
resolve({
fps: Math.round(1000 / avgInterval),
avgInterval: avgInterval.toFixed(2),
minInterval: Math.min(...intervals).toFixed(2),
maxInterval: Math.max(...intervals).toFixed(2),
});
}
}
requestAnimationFrame(frame);
});
});
console.log('Measured FPS:', measuredFps.fps);
console.log('Average interval:', measuredFps.avgInterval, 'ms');
Что проверять:
- Измеренная частота FPS соответствует настроенному значению (например, 60 для
--bot-fps=60) - Интервалы между кадрами согласованы с целевой частотой
- Несколько методов измерения (requestAnimationFrame, тайминг CSS-анимаций) дают одинаковый результат
- Инструменты тестирования отпечатков сообщают ожидаемую частоту обновления
Лучшие практики
-
Сопоставляйте FPS с оборудованием профиля. Профиль стандартного офисного ноутбука должен использовать 60 FPS. Профиль игрового десктопа может использовать 120 или 144. Несоответствующие значения ослабляют когерентность отпечатка.
-
Используйте
--bot-fps=profileдля автоматизированных рабочих процессов. Это автоматически выбирает правильную частоту кадров из профиля, уменьшая ошибки конфигурации. -
Комбинируйте с
--bot-time-scale. Частота кадров и тайминг выполнения должны быть согласованы. Профиль медленного устройства с частотой 60 FPS должен иметь временные характеристики, соответствующие оборудованию начального уровня. -
Избегайте произвольных значений FPS. Выбирайте значения, соответствующие реальному дисплейному оборудованию: 24, 30, 48, 60, 75, 90, 120, 144, 165, 240. Значения вроде 73 или 137 не соответствуют ни одному реальному монитору и будут выделяться.
-
Тестируйте в headless-режиме. Headless-браузеры не имеют физического дисплея, поэтому частота кадров полностью определяется конфигурацией BotBrowser. Убедитесь, что настроенная частота FPS корректно сообщается в headless-режиме.
Часто задаваемые вопросы
Работает ли фингерпринтинг частоты кадров на мобильных устройствах?
Да. Мобильные устройства имеют различные частоты обновления (60 Гц, 90 Гц, 120 Гц), и многие современные телефоны поддерживают динамическое переключение частоты обновления. Тайминг requestAnimationFrame раскрывает текущую частоту обновления в мобильных браузерах так же, как и на десктопных.
Могут ли веб-сайты обнаружить, что FPS контролируется?
Если контроль применяется на уровне JavaScript (через оборачивание requestAnimationFrame), да, потому что CSS-анимации и другие источники тайминга могут сообщать другую частоту. BotBrowser контролирует FPS на уровне конвейера рендеринга, поэтому все источники тайминга согласованы.
Влияет ли --bot-fps на воспроизведение видео?
Воспроизведение видео использует отдельный конвейер декодирования и не затрагивается флагом --bot-fps. Видео воспроизводятся с их закодированной частотой кадров. Контролируется только цикл рендеринга и тайминг анимаций.
Что происходит с контентом переменной частоты обновления?
BotBrowser применяет фиксированную частоту кадров на основе конфигурации. Это соответствует тому, как большинство реальных браузеров ведут себя при использовании монитора с фиксированной частотой, что по-прежнему является большинством дисплеев.
Можно ли использовать разные значения FPS для разных вкладок?
Флаг --bot-fps применяется ко всему экземпляру браузера. Для разных частот кадров запускайте отдельные экземпляры браузера с разными конфигурациями.
Как частота кадров взаимодействует с API visibility?
Браузеры обычно ограничивают requestAnimationFrame для фоновых вкладок. BotBrowser следует этому поведению, если не используется --bot-always-active, который поддерживает работу всех вкладок с настроенной частотой кадров независимо от состояния видимости.
Соответствует ли свойство Screen.refreshRate?
В браузерах, которые предоставляют screen.refreshRate или аналогичные свойства, BotBrowser обеспечивает согласованность этих значений с частотой кадров, настроенной через --bot-fps и загруженный профиль.
Итоги
Частота обновления дисплея, измеряемая через тайминг requestAnimationFrame, CSS-анимации и связанные API, является постоянным аппаратным сигналом фингерпринтинга, с которым стандартные средства приватности не могут справиться. BotBrowser контролирует частоту кадров на уровне конвейера рендеринга движка браузера через флаг --bot-fps, обеспечивая внутреннюю согласованность всех зависящих от кадров сигналов. В сочетании с защитой тайминга производительности, контролем Canvas и комплексным управлением профилями BotBrowser обеспечивает полную защиту аппаратных сигналов для рабочих процессов, ориентированных на приватность.
Похожие статьи
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.