navigator.plugins больше не хранит реальные данные — спецификация зашила его жёстко. Что выдают пустой массив, лишние записи или расхождение с pdfViewerEnabled?
navigator.plugins когда-то был настоящей инвентаризацией: какие плагины Flash, Java или Silverlight установлены у пользователя — всё это выдавалось любой странице по запросу. Этой настоящей инвентаризации больше нет. Последние версии спецификации жёстко зашивают возвращаемый список — любой браузер, соответствующий спецификации, теперь возвращает один и тот же фиксированный набор записей либо не возвращает ничего. Казалось бы, это конец истории для когда-то популярной поверхности фингерпринтинга. Но не совсем: значение, которое должно быть фиксированным, становится информативным ровно в тот момент, когда оно перестаёт быть фиксированным — или расходится с другим API, описывающим ту же возможность.
Ключевые выводы
- Список плагинов больше не реальные данные. Последние версии спецификации жёстко зашивают
navigator.plugins: если поддерживается встроенный просмотр PDF, он содержит ровно пять фиксированных записей; если нет — возвращается пустойPluginArray. navigator.pdfViewerEnabled— официально рекомендованный способ проверки — MDN прямо указывает не выводить это изnavigator.plugins.- Список всё ещё несёт сигнал для обнаружения, но не для идентификации. Пустой массив у браузера, заявляющего себя настольным Chrome, неожиданные лишние записи или расхождение между
navigator.pluginsиnavigator.pdfViewerEnabled— всё это признаки внутренней несогласованности, а не нормальная вариативность браузеров. - Сам по себе
navigator.pluginsсегодня несёт почти нулевую энтропию — почти любой настоящий браузер попадает в одно из двух фиксированных состояний, так что он больше не способен различать посетителей так, как это делают фингерпринты canvas или шрифтов. Его оставшаяся ценность — проверка согласованности поверх других сигналов, а не самостоятельный идентификатор. - Это совсем другая поверхность, чем расширения браузера. Расширения — это устанавливаемые пользователем надстройки, обнаруживаемые совсем другими способами;
navigator.pluginsвсегда описывал только встроенный слой плагинов/обработки PDF самого браузера.
Что на самом деле возвращает navigator.plugins сейчас
Вызов navigator.plugins по-прежнему возвращает PluginArray — не настоящий JavaScript-массив, а объект, похожий на массив, с length, item(index) и namedItem(name). Но то, что внутри, больше не определяется операционной системой. По текущей спецификации содержимое — это ровно один из двух фиксированных исходов:
- Если браузер поддерживает встроенный просмотр PDF, массив содержит пять конкретных записей:
"PDF Viewer","Chrome PDF Viewer","Chromium PDF Viewer","Microsoft Edge PDF Viewer","WebKit built-in PDF". - Если не поддерживает, массив пуст.
if ("PDF Viewer" in navigator.plugins) {
// Браузер поддерживает встроенный просмотр PDF-файлов.
}
Вот и всё. Третьего состояния не существует, частичного списка быть не может, и настоящий браузер, соответствующий спецификации, не способен сообщить три плагина или плагин с другим именем. MDN помечает PluginArray как устаревший — кандидат на удаление в будущем, — а его собственные свойства больше не перечисляемы в текущих версиях браузеров, что закрывает старые трюки с перебором массива через for...in.
navigator.mimeTypes прошёл через точно такое же изменение. Он возвращает MimeTypeArray, который по спецификации содержит записи для application/pdf и text/pdf, если поддерживается встроенный просмотр PDF, и пустой список в противном случае — зашито тем же способом, привязано к тому же единственному биту базовой возможности.
navigator.pdfViewerEnabled — замена, а не navigator.plugins
Именно потому, что список плагинов свёлся к единственному сигналу да/нет о поддержке PDF, платформа добавила свойство, которое отвечает на этот вопрос напрямую: navigator.pdfViewerEnabled, простое булево значение. MDN прямо указывает на этот переход и на странице plugins, и на странице mimeTypes: чтобы определить, поддерживается ли встроенный просмотр PDF-файлов, следует использовать navigator.pdfViewerEnabled и не выводить ответ из этих двух устаревших свойств.
Именно эта инструкция и делает navigator.plugins всё ещё достойным разбора. У легитимного кода, которому нужно узнать «может ли этот браузер показать PDF встроенно», теперь есть прямой, санкционированный ответ. А код, который до сих пор ветвится по navigator.plugins.length или ищет "PDF Viewer" по имени, либо устарел, либо занимается чем-то другим, не связанным с поддержкой PDF — и это как раз то поведение, которое хочет охарактеризовать система обнаружения.
Почему фиксированный список всё ещё остаётся сигналом для обнаружения
Значение с всего двумя допустимыми состояниями не может идентифицировать посетителя — но оно может поймать окружение браузера, которое не ведёт себя так, как заявленное. В этом и заключается вся оставшаяся польза, и она сводится к внутренней согласованности, а не уникальности:
Пустой массив там, где ожидается полный. Строка User-Agent, заявляющая современный настольный Chrome, по спецификации должна возвращать тот самый список из пяти записей PDF-просмотрщика — свежий Chrome по умолчанию поддерживает встроенный просмотр PDF. Пустой navigator.plugins при такой заявленной конфигурации — расхождение, заслуживающее внимания, и именно такой пробел исторически выдавали headless- и автоматизированные окружения браузера, иногда потому, что фреймворк автоматизации полностью отключает компонент PDF-просмотрщика.
Записи, не совпадающие с фиксированным набором. Поскольку спецификация жёстко фиксирует ровно пять имён плагинов, любая запись за пределами этого набора — незнакомое имя, другое количество, запись, где вместо строки отображается [object Object] — указывает, что свойство было пропатчено скриптом, а не сгенерировано браузером нативно. По иронии, инструменты для обхода детекции, пытающиеся подделать «реалистичный» список плагинов, как раз с наибольшей вероятностью выдают список, не совпадающий с тем, что реально выдаёт настоящий современный браузер. Обычно такая заплатка оставляет и второй след: если свойство подменить обычным объектом или JavaScript-массивом, оно перестаёт проходить проверки, которые нетронутый navigator.plugins проходит, — его прототип по-прежнему PluginArray, String(navigator.plugins) по-прежнему даёт "[object PluginArray]", а его методы по-прежнему приводятся к строке как нативный код.
Расхождение между navigator.plugins и navigator.pdfViewerEnabled. Эти два свойства описывают одну и ту же базовую возможность, но принадлежат разным эпохам спецификации. В настоящем, немодифицированном браузере они всегда согласованы: непустой список плагинов подразумевает pdfViewerEnabled === true, и наоборот. Страница, которая запрашивает оба свойства и находит противоречие между ними — pdfViewerEnabled истинно, а navigator.plugins пуст, или наоборот, — поймала скрипт, пропатчивший только то свойство, о котором он знал: код подмены часто правит привычное старое свойство и забывает, что существует более новое.
Ни одна из этих проверок не говорит, кто этот посетитель. Они говорят, что окружение внутренне несогласовано — а это именно тот след, который автоматизированные и подделанные профили браузера оставляют гораздо чаще, чем настоящие.
Это не та же поверхность, что расширения браузера
Легко спутать «плагины» и «расширения», потому что исторически и то и другое — надстройки браузера, но обнаруживаются они и значат совершенно по-разному. navigator.plugins описывает встроенную архитектуру плагинов и обработки PDF самого браузера, сегодня сведённую к тому единственному зашитому сигналу о PDF. Расширения браузера (uBlock Origin, менеджеры паролей, блокировщики рекламы) — это программы, устанавливаемые пользователем, и страница вообще не может перечислить их через этот API — обнаружение установленных расширений опирается на отдельные техники, например проверку, отвечают ли собственные веб-доступные ресурсы расширения. Если вас интересует, что страница может узнать об установленных пользователем расширениях, а не об этом встроенном флаге плагинов браузера, об этом рассказывает наш материал о рисках приватности расширений браузера.
Проверка реальности: сколько энтропии осталось
Стоит честно признать, насколько мало navigator.plugins вносит сам по себе сегодня. При всего двух допустимых состояниях во всех соответствующих спецификации браузерах он практически не добавляет различающей информации — ничего похожего на вклад рендеринга canvas, установленных шрифтов или параметров WebGL. Старые материалы о фингерпринтинге браузера всё ещё описывают navigator.plugins как богатый сигнал для каждого пользователя — это было верно до изменения, зашившего список; считать так сегодня — переоценивать его роль. Его полезная роль теперь стала и уже, и попросту другой: не «кто это», а «согласуются ли сигналы этого окружения о поддержке PDF друг с другом и с заявленной идентичностью браузера». Это ставит его в один ряд с фингерпринтингом состояний Permissions API — ещё одним чтением без запроса и без клика, которое полезнее как проверка согласованности, чем как источник сырой энтропии. Ему далеко до сигналов, которые реально ещё различают устройства — например, до перечня камер и микрофонов, который выдаёт enumerateDevices(). Полную картину того, какие сигналы всё ещё несут реальную различающую ценность, смотрите в нашем руководстве по фингерпринтингу браузера.
Проверьте собственный браузер
С помощью инструмента проверки плагинов BrowserInsight вы можете увидеть, что именно сообщает ваш браузер по этому свойству: сами записи, их количество и включён ли просмотрщик PDF. Проверку на согласованность инструмент тоже делает — совпадает ли список плагинов с navigator.mimeTypes и выглядит ли объект PluginArray по-прежнему нативным, а не пропатченным, — вместе с проверками расширений, которые продолжают работу там, где заканчивается navigator.plugins. Чтобы увидеть это свойство в контексте всех остальных сигналов — canvas, WebGL, шрифтов и прочего, — используйте проверку фингерпринта.
Часто задаваемые вопросы
Это нормально, что navigator.plugins пуст в современном браузере?
Да. Пустой массив просто означает, что браузер по умолчанию не сообщает о поддержке встроенного просмотра PDF — это допустимое, соответствующее спецификации состояние, само по себе не признак подделки. Оно становится заметным только в сочетании с другими сигналами — например, когда User-Agent заявляет версию браузера, которая по идее должна поддерживать встроенный PDF.
Можно ли всё ещё использовать navigator.plugins для проверки поддержки PDF?
Можно, но не стоит — MDN прямо рекомендует для этого navigator.pdfViewerEnabled. И navigator.plugins, и navigator.mimeTypes числятся кандидатами на будущее удаление.
Почему список navigator.plugins у бота может выглядеть неправильно?
Потому что настоящий, соответствующий спецификации список имеет всего две возможные формы, и любой скрипт, пытающийся подсунуть «реалистично выглядящий» самодельный список плагинов, чтобы сойти за человека, по сути борется с уже зафиксированной целью: система обнаружения заранее знает, как выглядит настоящий список, поэтому любое отклонение бросается в глаза сразу. Собранный вручную список плагинов скорее выдаст автоматизацию, чем скроет её, — особенно если он не совпадает с navigator.pdfViewerEnabled или если подменённый объект уже не похож на нативный PluginArray.
Имеет ли navigator.plugins ещё значение для фингерпринтинга?
Едва ли, и совсем не так, как раньше. Его собственный вклад в энтропию близок к нулю. Его оставшаяся ценность — проверка согласованности между navigator.pdfViewerEnabled и заявленной идентичностью браузера, а не способ отличить одного посетителя от другого.


