Отпечаток WebGL, разобранный по механике
Страница создаёт контекст WebGL, задаёт ему серию вопросов о графическом железе внизу и рисует небольшую сцену, чтобы измерить, как это железо её отрисовывает. Разрешения ни на что не требуется, потому что холст и 3D-контекст - обычные веб-API. Ответы различаются по модели видеокарты, драйверу и операционной системе точнее почти всего остального, что браузер отдаёт бесплатно.
Две строки, называющие ваше железо
Расширение WEBGL_debug_renderer_info существует специально для того, чтобы снять маску с двух параметров, которые иначе остаются общими:
const gl = document.createElement('canvas').getContext('webgl')
const info = gl.getExtension('WEBGL_debug_renderer_info')
gl.getParameter(info.UNMASKED_VENDOR_WEBGL)
gl.getParameter(info.UNMASKED_RENDERER_WEBGL)
Без расширения gl.getParameter(gl.VENDOR) и gl.getParameter(gl.RENDERER) обычно возвращают что-то общее вроде WebKit и WebKit WebGL. С ним настоящая машина на Windows вернёт что-то в форме ANGLE (NVIDIA, NVIDIA GeForce RTX <модель> Direct3D11 vs_x_x ps_x_x, D3D11) или эквивалент от Intel либо AMD. Одна эта строка раскрывает вендора видеокарты, конкретную модель, графический бэкенд, транслирующий вызовы WebGL в нативные, и модель шейдеров, о которой сообщает драйвер. На macOS имя бэкенда меняется на Metal или OpenGL вместо Direct3D, а на Linux это обычно OpenGL или Mesa, называющие реальную карту.
Список параметров, различающийся по железу
Помимо двух заглавных строк, getParameter отвечает на десятки зависящих от железа вопросов, каждый из которых добавляет немного энтропии: MAX_TEXTURE_SIZE, MAX_VERTEX_ATTRIBS, MAX_VIEWPORT_DIMS, MAX_RENDERBUFFER_SIZE, MAX_COMBINED_TEXTURE_IMAGE_UNITS и ALIASED_LINE_WIDTH_RANGE среди прочих. Ничего из этого не задаёт вендор браузера. Всё читается прямо из заявленных драйвером возможностей, поэтому мобильная видеокарта среднего уровня и десктопная карта сообщают разные числа даже на одинаковой сборке браузера.
Точность шейдеров добавляет ещё слой. gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT) и его собратья для FRAGMENT_SHADER и точностей MEDIUM/LOW возвращают тройку rangeMin, rangeMax и precision для каждого сочетания, и они различаются между семействами видеокарт, потому что работа с плавающей точкой в шейдерах сама по себе зависит от железа.
Наконец, gl.getSupportedExtensions() возвращает полный список расширений WebGL, которые отдаёт драйвер, часто сорок и более записей, и точный набор, порядок и соглашения об именовании тесно коррелируют с сочетанием видеокарты и драйвера.
Хеш отрисовки
Та же логика, что делает отрисовку текста на canvas машинно-специфичной, работает и здесь, слоем глубже. Страница рисует сцену с конкретными шейдерами, освещением или геометрией в контекст WebGL, вызывает gl.readPixels(), чтобы вытащить отрисованные байты обратно, и хеширует их. Цепочка от исходника шейдера до финального пикселя проходит через растеризатор и блок плавающей точки самой видеокарты, поэтому хеш стабилен при повторных отрисовках на одной машине и расходится между моделями видеокарт. Эквивалентный механизм на 2D-контексте разобран в статье про отпечаток canvas; эти два часто сверяют друг с другом, ведь скрипт, заявляющий конкретную видеокарту, должен давать и хеш canvas, согласующийся с известным поведением отрисовки этой видеокарты.
Почему программный рендерер - самый громкий признак
SwiftShader и llvmpipe - программные реализации, отрисовывающие WebGL целиком на процессоре, когда настоящего пути аппаратного ускорения нет, и их строки вендора и рендерера прямо об этом говорят. Пользовательский ноутбук или десктоп, сообщающий программный рендерер, - прямое противоречие: настоящее железо, даже встроенная графика, отдаёт аппаратно ускоренный путь. Детектору здесь не нужно гадать. Он читает строку рендерера, сверяет её с коротким и хорошо известным списком программных запасных вариантов и считает совпадение почти достоверным доказательством того, что браузер работает в окружении без реального видеоадаптера, а это ровно профиль автоматизированного headless-воркера, а не ноутбука.
Почему строка рендерера обязана совпадать с заявленной ОС
Графический бэкенд, названный внутри строки рендерера, - не украшение. ANGLE ... Direct3D11 бывает только на Windows, потому что ANGLE транслирует вызовы WebGL в Direct3D именно на этой платформе. User agent, заявляющий macOS в паре с бэкендом Direct3D, или Linux в паре с Metal - не редкое совпадение: это две подсистемы, каждая из которых честно говорит о другой, несовместимой машине, и проверить такой сигнал дешевле, чем почти любую отдельную деталь железа.
Когда правильный ответ - инструмент попроще
Если ваша цель - приватно ходить по сети, а не вести раздельные личности, отпечаток WebGL уже во многом решён, и бесплатно. Режим resist-fingerprinting в Firefox возвращает всем сайтам фиксированную общую строку рендерера вместо вашего настоящего железа, а Tor Browser по умолчанию блокирует WebGL целиком. Оба эффективны, потому что заставляют множество несвязанных пользователей сообщать одно и то же, а это принципиально другая стратегия, нежели производить по одной согласованной, но отличной машине на профиль. Ни то, ни другое не поможет, если вам нужно несколько отдельных аккаунтов, каждый из которых обязан выглядеть своим настоящим устройством, но это другая работа, не та, под которую они сделаны.
Прочитайте свои значения
Выполните это в консоли любого браузера:
function webglReport() {
const gl = document.createElement('canvas').getContext('webgl')
const info = gl.getExtension('WEBGL_debug_renderer_info')
return {
vendor: gl.getParameter(gl.VENDOR),
renderer: gl.getParameter(gl.RENDERER),
unmaskedVendor: info ? gl.getParameter(info.UNMASKED_VENDOR_WEBGL) : null,
unmaskedRenderer: info ? gl.getParameter(info.UNMASKED_RENDERER_WEBGL) : null,
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
maxVertexAttribs: gl.getParameter(gl.MAX_VERTEX_ATTRIBS),
aliasedLineWidthRange: gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE),
extensionCount: gl.getSupportedExtensions().length,
}
}
console.log(webglReport())
CreepJS оценивает ровно это сочетание строк и лимитов по известным профилям исправного железа и выносит несовпадение заявленной ОС с сообщённым графическим бэкендом в отдельную категорию. Полный набор свойств, которые обязаны сходиться друг с другом, - в чек-листе согласованности отпечатка, а общая идея по всем сигналам этой категории - в обзоре отпечатков браузера.
Попробуйте на бесплатном тарифе.
Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.