Фингерпринтинг CPU: защита hardwareConcurrency
Как navigator.hardwareConcurrency раскрывает количество ядер CPU для фингерпринтинга и методы контроля отчётности о ядрах во всех контекстах.
Нужна поддерживаемая продуктовая документация?
У этой статьи есть соответствующая страница в центре документации. Используйте docs для каноничного сценария настройки, актуальных флагов и долгосрочной справки.
Введение
Свойство navigator.hardwareConcurrency возвращает количество логических ядер CPU, доступных браузеру. Оно было создано, чтобы помочь веб-приложениям оптимизировать параллельные нагрузки, позволяя JavaScript-коду решать, сколько Web Workers запустить или как разделить задачи обработки данных. Однако это же свойство раскрывает информацию об оборудовании, которая вносит вклад в фингерпринтинг браузера. Устройство с 6 логическими ядрами встречается гораздо реже, чем с 8, а сервер с 64 ядрами сразу выделяется. Поскольку число ядер CPU стабильно, легко запрашиваемо и различается в зависимости от конфигурации оборудования, оно стало стандартным компонентом систем отслеживания по отпечатку. Эта статья объясняет, как количество ядер CPU влияет на фингерпринтинг, почему простые подмены создают проблемы и как BotBrowser обеспечивает согласованную отчётность о числе ядер через систему профилей.
Влияние на конфиденциальность
Количество ядер CPU может показаться маловажным сигналом для отпечатка, но его вклад значителен в контексте. Ценность любого атрибута отпечатка зависит от его распределения в популяции. По данным проекта AmIUnique, navigator.hardwareConcurrency имеет около 4,5 бит энтропии, что позволяет различить примерно 23 группы. Хотя 23 группы не кажется большим числом, каждый бит энтропии примерно удваивает идентифицирующую способность при комбинировании с другими сигналами.
Распределение сильно смещено. Значения 4 и 8 составляют большинство настольных браузеров. Значения 2 (старые машины, бюджетные Chromebook), 6 (определённые конфигурации Intel), 12 (процессоры Ryzen 5), 16 (высокопроизводительные настольные ПК) и всё, что выше 16, встречаются всё реже. Машина, сообщающая 24, 32 или 64 ядра, почти наверняка является сервером или рабочей станцией, что крайне нетипично для обычной браузерной аудитории.
Мобильные устройства добавляют дополнительную дифференциацию. Большинство телефонов сообщают о 4 или 8 ядрах, но конкретные семейства SoC (Snapdragon, Exynos, MediaTek, Apple Silicon) сочетают определённое число ядер с другими идентифицируемыми свойствами. Телефон с hardwareConcurrency равным 8 в сочетании с определённым паттерном user agent сужает устройство до нескольких моделей.
Проблема конфиденциальности усиливается тем, что hardwareConcurrency доступен во всех контекстах выполнения: основной поток, выделенные workers, общие workers и service workers. Он не требует разрешений и не может быть заблокирован без нарушения работы легитимных веб-приложений, использующих его для оптимизации производительности.
Техническая справка
Как работает hardwareConcurrency
Свойство navigator.hardwareConcurrency возвращает беззнаковое целое число, представляющее количество доступных логических процессоров. «Логические процессоры» означают физические ядра, умноженные на потоки на ядро (например, 4-ядерный CPU с гиперпоточностью сообщает 8).
console.log(navigator.hardwareConcurrency); // e.g., 8
Значение также доступно в контекстах workers:
// Inside a Web Worker
console.log(self.navigator.hardwareConcurrency); // same value
Почему значение различается
Несколько факторов определяют, что возвращает hardwareConcurrency:
- Физическое оборудование. Количество ядер и потоков CPU определяется моделью процессора. Процессоры Intel i5 обычно имеют 4-6 ядер с гиперпоточностью. Процессоры AMD Ryzen 7 имеют 8 ядер с SMT. Чипы Apple M-серии имеют разное число ядер по кластерам эффективности и производительности.
- Виртуализация. Виртуальные машины сообщают количество выделенных vCPU, которое может отличаться от хост-машины. ВМ с 2 vCPU сообщает
hardwareConcurrencyравное 2, независимо от реального числа ядер хоста. - Планировщик ОС. Некоторые операционные системы позволяют ограничивать видимое число процессоров. Контейнерные среды (Docker, LXC) могут ограничивать видимость CPU.
- Реализация в браузере. Большинство браузеров сообщают необработанное значение от ОС. Некоторые экспериментировали с ограничением или группировкой значения, но Chromium в настоящее время сообщает реальное число.
Корреляция с другими сигналами
Количество ядер CPU не существует изолированно. Системы отслеживания сопоставляют его с:
navigator.deviceMemory- 2-ядерная машина с 8 ГБ ОЗУ правдоподобна; 2-ядерная машина с 0,25 ГБ ОЗУ предполагает другой класс устройства.navigator.platform- Платформа «Linux x86_64» с 4 ядрами сужает круг до конкретного оборудования. «Linux armv81» с 8 ядрами предполагает конкретный ARM-сервер или телефон.- User agent. Версия браузера и версия ОС ограничивают, какие числа ядер реалистичны.
- Тайминг производительности. Реальная производительность параллельного выполнения (измеренная через побочные каналы тайминга) может быть сопоставлена с заявленным числом ядер.
Несоответствие между заявленным числом ядер и реальной производительностью параллельного выполнения является сильным сигналом подмены значения.
Распространённые подходы защиты и их ограничения
Ручная подмена hardwareConcurrency через браузерное расширение или JavaScript-инъекцию - наиболее распространённый подход. Он изменяет возвращаемое значение, но создаёт несколько проблем:
- Несогласованность производительности. Если вы сообщаете 4 ядра, а на вашей машине 16, параллельные нагрузки (SharedArrayBuffer, Web Workers) выполняются быстрее, чем это было бы возможно на 4-ядерной машине. Это расхождение по таймингу измеримо.
- Обнаружение через прототипы. JavaScript-подмена
navigator.hardwareConcurrencyможет быть обнаружена путём проверки дескриптора свойства, цепочки прототипов или измерения времени выполнения геттера. - Пробелы в контексте workers. Некоторые расширения модифицируют только объект
navigatorосновного потока, не затрагивая контексты workers. Скрипт отслеживания, выполняемый в Web Worker, видит реальное значение.
Рандомизация значения создаёт нереалистичные конфигурации. Случайное значение 7 или 13 не соответствует никакой реальной архитектуре CPU. Значения должны быть степенями двойки или конкретными известными конфигурациями, чтобы быть правдоподобными.
Использование фиксированного распространённого значения (например, всегда сообщать 8) лучше рандомизации, но всё же рискованно, если оно противоречит другим сигналам. Сообщение о 8 ядрах при user agent, заявляющем Android-телефон с 4-ядерным SoC, несогласованно.
Фундаментальное требование - заявленное число ядер должно быть внутренне согласовано со всеми другими свойствами устройства и не должно противоречить наблюдаемым характеристикам производительности.
Подход BotBrowser на уровне движка
BotBrowser контролирует navigator.hardwareConcurrency на уровне движка Chromium через систему профилей отпечатков. Этот подход имеет несколько преимуществ перед модификациями на уровне JavaScript.
Значения, определяемые профилем
Каждый профиль отпечатка BotBrowser указывает значение hardwareConcurrency, соответствующее реальной конфигурации CPU профилируемого устройства. Профиль настольного ПК с Windows 10 и Intel i7 сообщает 8 или 16 ядер. Профиль телефона Android со Snapdragon 888 сообщает 8 ядер. Значения берутся из реальных конфигураций устройств, а не произвольные числа.
Согласованность во всех контекстах
Поскольку контроль происходит на уровне движка, каждый контекст сообщает одно и то же значение:
navigator.hardwareConcurrencyв основном потокеself.navigator.hardwareConcurrencyв Dedicated Workerself.navigator.hardwareConcurrencyв Shared Workerself.navigator.hardwareConcurrencyв Service Worker
Нет пробела, при котором один контекст возвращает реальное значение, а другой - значение профиля.
Согласованность между сигналами
Значение hardwareConcurrency в профиле является частью полной идентичности устройства. Оно согласовано с:
- Строкой user agent и платформой
- Значением deviceMemory
- Разрешением экрана и другими аппаратными сигналами
- Маркой и версией браузера
Это гарантирует, что ни одна перекрёстная проверка не выявит несогласованность.
Отсутствие JavaScript-артефактов
Поскольку значение задаётся на уровне движка, нет модифицированных дескрипторов свойств, нет ненативных геттеров и нет разницы в тайминге по сравнению с нативным доступом к свойству. Свойство ведёт себя точно так же, как на профилируемом оборудовании.
Настройка и использование
Базовая загрузка профиля
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
Профиль автоматически определяет сообщаемое число ядер.
Проверка значения
// In browser console or automation script
console.log(navigator.hardwareConcurrency);
// Returns the profile's core count, not your actual hardware
Интеграция с Playwright
const { chromium } = require('playwright');
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
const cores = await page.evaluate(() => navigator.hardwareConcurrency);
console.log(`Reported cores: ${cores}`);
// Verify worker context matches
const workerCores = await page.evaluate(() => {
return new Promise(resolve => {
const blob = new Blob([
'postMessage(self.navigator.hardwareConcurrency)'
], { type: 'application/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = e => resolve(e.data);
});
});
console.log(`Worker cores: ${workerCores}`);
// Both values should be identical
Интеграция с Puppeteer
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
defaultViewport: null,
args: [
'--bot-profile=/path/to/profile.enc'
]
});
const page = await browser.newPage();
await page.goto('https://example.com');
const cores = await page.evaluate(() => navigator.hardwareConcurrency);
console.log(`Reported cores: ${cores}`);
Проверка
Проверка значения. Запросите navigator.hardwareConcurrency и убедитесь, что оно соответствует ожидаемому значению для загруженного профиля. Оно не должно отражать ваше реальное оборудование.
Согласованность workers. Создайте Web Worker и запросите self.navigator.hardwareConcurrency внутри него. Значение должно точно совпадать с основным потоком.
Согласованность между свойствами. Убедитесь, что число ядер правдоподобно для платформы, user agent и памяти устройства профиля. Конфигурация с 8 ядрами, 8 ГБ памяти и user agent Windows 10 реалистична. Конфигурация с 64 ядрами и user agent Android - нет.
Стабильность между сессиями. Запросите значение в нескольких сессиях с одним профилем. Оно должно быть идентичным каждый раз.
Лучшие практики
- Используйте полный профиль. Не пытайтесь переопределять только
hardwareConcurrencyизолированно. Значение должно быть согласовано со всеми другими свойствами устройства. Профили BotBrowser обеспечивают это автоматически. - Выбирайте профили с реалистичными конфигурациями. Распространённые значения (4, 8) сливаются с общей массой лучше, чем необычные значения (6, 12, 24). Выбирайте профили, подходящие для вашего целевого сценария.
- Тестируйте в контекстах workers. Всегда проверяйте, что запросы из workers возвращают то же значение, что и основной поток.
- Не переопределяйте число ядер вручную. Профили BotBrowser разработаны с внутренне согласованными конфигурациями. Ручное изменение числа ядер без корректировки других свойств создаёт обнаружимые несогласованности.
Часто задаваемые вопросы
В: Какие наиболее распространённые значения hardwareConcurrency? О: На настольных ПК 4 и 8 являются безусловно самыми распространёнными, вместе составляя более 70% браузеров. На мобильных устройствах 4 и 8 также доминируют. Значения 2, 6, 10, 12, 16 и выше встречаются всё реже.
В: Может ли сайт измерить реальное число ядер независимо от того, что сообщает hardwareConcurrency? О: Теоретически сайт может попытаться измерить производительность параллельного выполнения с помощью SharedArrayBuffer и таймеров высокого разрешения. На практике меры безопасности браузеров (сниженная точность таймеров, требования COOP/COEP) делают это затруднительным. Контроль BotBrowser на уровне движка также помогает, так как заявленное значение соответствует ожидаемому поведению профиля.
В: Меняется ли hardwareConcurrency при обновлениях браузера? О: Нет. Значение отражает конфигурацию оборудования и ОС, а не версию браузера. Оно остаётся стабильным при обновлениях браузера на одной и той же машине.
В: Доступен ли navigator.hardwareConcurrency во всех браузерах? О: Да. Он поддерживается во всех основных браузерах (Chrome, Firefox, Safari, Edge) и стандартизирован как часть спецификации HTML.
В: Почему некоторые ВМ сообщают число ядер, отличающееся от хоста?
О: Виртуальные машины сообщают количество vCPU, выделенных гипервизором. Хост с 16 ядрами может выделить только 2 или 4 vCPU для ВМ, и именно это значение hardwareConcurrency возвращает изнутри ВМ.
В: Влияет ли число ядер на производительность Web Worker в BotBrowser? О: BotBrowser контролирует сообщаемое значение, а не реальное планирование CPU. Ваши реальные аппаратные ядра по-прежнему используются для выполнения. Значение профиля влияет только на то, что JavaScript видит при запросе свойства.
Итоги
navigator.hardwareConcurrency - простое свойство, возвращающее одно число, но его значение вносит существенный вклад в фингерпринтинг браузера, особенно в сочетании с другими аппаратными сигналами. BotBrowser контролирует это значение на уровне движка Chromium через систему профилей, обеспечивая согласованную отчётность во всех контекстах выполнения JavaScript. Поскольку значение является частью полного профиля устройства, оно согласовано с user agent, платформой, памятью и всеми другими сигналами идентичности.
По связанным темам смотрите Что такое фингерпринтинг браузера, Защита свойств Navigator, Защита экрана и окна и Детерминированное поведение браузера.
Похожие статьи
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.