navigator.permissions.query() раскрывает состояния granted/prompt/denied для ~15 разрешений — фингерпринт поддержки и состояний без единого действия.
Большинству техник фингерпринтинга нужен canvas, GPU или аудиобуфер. Permissions API не нуждается ни в чём подобном — он создан, чтобы страница могла вежливо спросить «есть ли у меня доступ к геолокации?» перед тем, как запрашивать разрешение, но если опросить его по всем полутора-двум десяткам названий разрешений, которые реально поддерживают браузеры, ответ сам по себе превращается в небольшой структурированный фингерпринт — без запроса, без клика и без единого прочитанного пикселя.
Ключевые выводы
navigator.permissions.query({name})возвращаетPermissionStatus—granted,deniedилиprompt— для конкретного разрешения, ни разу не показывая пользователю диалоговое окно.- Chromium поддерживает примерно 15–17 названий разрешений, доступных для запроса; Firefox и Safari поддерживают гораздо меньше, поэтому уже сам факт того, какие имена вообще резолвятся, а не выбрасывают ошибку, — это сигнал о браузере и движке.
- Состояние
granted/denied/promptдля каждого разрешения отражает сочетание истории конкретного сайта и настроек уровня ОС, так что итоговый вектор — это полу-устойчивый вклад в фингерпринт, привязанный к источнику. - Порядок перечисления названий разрешений и то, какие из них выбрасывают
TypeErrorиз-за отсутствия поддержки, различаются в зависимости от движка и версии — это ещё один слой фингерпринта поддержки и порядка поверх самих состояний. - Проверка отпечатка BrowserInsight показывает этот сигнал рядом с другими раскрываемыми вашим браузером сигналами, чтобы вы видели, как он складывается с canvas, WebGL и шрифтами.
Что на самом деле делает navigator.permissions.query()
Permissions API, стандартизированный в спецификации W3C Permissions, даёт скриптам способ проверить состояние разрешения, не вызывая при этом сам запрос разрешения, который обычно показывает такая функция, как геолокация или уведомления. Вызывается это так:
const status = await navigator.permissions.query({ name: 'geolocation' });
console.log(status.state); // "granted" | "denied" | "prompt"
Никакого диалога не появляется. Браузер просто сообщает то, что уже знает: получал ли этот источник геолокацию раньше, было ли это разрешение явно отклонено, или же браузеру пришлось бы спросить пользователя, если бы функция была реально запрошена. В этом и заключается весь смысл API — позволить сайту показывать «включить геолокацию» только тогда, когда ему действительно нужно будет спросить, а не гадать заранее.
Опрос по всем названиям разрешений
Интересная часть с точки зрения фингерпринтинга в том, что API принимает множество разных названий разрешений, и скрипт может просто перебрать их все в цикле:
// Иллюстративный набросок — не каждое имя ниже поддерживается в каждом браузере
const candidateNames = [
'geolocation', 'notifications', 'push', 'midi', 'camera', 'microphone',
'background-sync', 'persistent-storage', 'ambient-light-sensor',
'accelerometer', 'gyroscope', 'magnetometer', 'screen-wake-lock',
'clipboard-read', 'clipboard-write', 'display-capture', 'nfc',
];
async function fingerprintPermissions() {
const results = {};
for (const name of candidateNames) {
try {
const status = await navigator.permissions.query({ name });
results[name] = status.state; // granted | denied | prompt
} catch {
results[name] = 'unsupported'; // имя вызвало ошибку — браузер его не распознаёт
}
}
return results; // передаётся в хеш вместе с другими сигналами
}
Из этого цикла получаются две независимые оси. Первая — какие имена резолвятся, а какие выбрасывают ошибку — это фингерпринт поддержки, ближе к feature detection, чем к состоянию как таковому, и он довольно прямо соответствует движку и версии браузера. Вторая — фактическое состояние, возвращаемое для каждого поддерживаемого имени — эта часть формируется историей конкретного пользователя на данном источнике и настройками разрешений на уровне ОС, поэтому она различается от человека к человеку даже на идентичных браузерах.
Почему результаты отличаются в разных браузерах
Браузеры на основе Chromium (Chrome, Edge, Opera, Brave) распознают самый широкий набор имён — примерно от 15 до 17 в зависимости от версии, включая несколько разрешений, связанных с сенсорами и оборудованием, вроде accelerometer, magnetometer и ambient-light-sensor, которые другие движки вообще не раскрывают через этот API. Firefox поддерживает гораздо меньший набор — надёжно работают только geolocation, notifications, persistent-storage и push, а запросы имён, специфичных для Chromium, выбрасывают ошибку вместо того, чтобы резолвиться. Реализация WebKit в Safari ещё уже, и исторически отставала как по срокам внедрения, так и по количеству распознаваемых имён.
Этот разрыв — не случайность и не лень разработчиков; каждое из этих сенсорных разрешений соответствует device API, который движок реально реализует, поэтому ошибка при вызове navigator.permissions.query({ name: 'magnetometer' }) в Firefox — это прямое и надёжное следствие того, что Firefox просто не поставляет поверхность Generic Sensor API, которую это разрешение защищает. Практический результат для фингерпринтинга в том, что набор поддерживаемых имён сужает семейство и версию браузера при минимуме кода — похоже на то, как различия в поддержке функций и API в целом раскрывают идентичность движка, только здесь «функцией», которую детектируют, является сама система разрешений.
Стоит отличать это от другой, не связанной модели разрешений: то, что позволено делать установленному расширению (читать каждую страницу, обращаться к куки, запускать скрипты), определяется собственными manifest-разрешениями расширения, а не navigator.permissions.query() — о том, как работает этот отдельный, гораздо более широкий вид доступа, читайте в статье Риски приватности браузерных расширений.
Вектор состояний как источник энтропии
Помимо того, какие имена резолвятся, второй слой сигнала образует само состояние, до которого резолвится каждое имя. Большинство пользователей никогда явно не предоставляли и не отклоняли большинство разрешений, поэтому в типичном случае почти всё оказывается в состоянии prompt — низкоэнтропийное значение по умолчанию. Но любое разрешение, с которым пользователь когда-либо взаимодействовал — предоставил геолокацию какому-то сайту, заблокировал уведомления глобально в настройках браузера, дал постоянное хранилище веб-приложению, — переключает соответствующую запись в granted или denied, и она остаётся такой, пока пользователь снова её не изменит.
Это делает вектор гибридным сигналом: в основном фингерпринт браузера/движка, формируемый паттерном поддержки, с меньшим, но реальным вкладом от фактической истории разрешений конкретного пользователя. В сочетании с другими пассивными сигналами — canvas, WebGL или количеством медиаустройств — он добавляет ещё одну по большей части независимую ось, которую скрипт может прочитать бесплатно, одним асинхронным вызовом, без запроса разрешения на само себя.
Меры смягчения
В основных браузерах нет отдельного переключателя «заблокировать permissions.query», потому что этот API существует именно для того, чтобы легитимные сайты не переспрашивали пользователей слишком часто — убрать его значило бы ухудшить работу каждой функции, защищённой разрешением. Реальные меры смягчения те же, что ограничивают фингерпринты на основе рендеринга в целом:
privacy.resistFingerprintingв Firefox стандартизирует некоторые связанные с разрешениями особенности поведения как часть более широкой стратегии единообразия, снижая различия паттерна поддержки относительно базовой линии Firefox.- Сводите к минимуму то, что вы разрешаете. Каждое разрешение, которое вы реально предоставили или явно отклонили, становится устойчивым, доступным для запроса битом. Отклонение ненужных разрешений и отзыв устаревших грантов в настройках сайтов вашего браузера удерживает бо́льшую часть вашего вектора в низкоэнтропийном состоянии
promptпо умолчанию. - Осознанно выбирайте браузер с более узким набором поддерживаемых имён. Это реальный компромисс, а не бесплатный выигрыш — более узкая поддержка у Safari и Firefox означает меньше данных для фингерпринтинга через этот конкретный API, ценой деградации некоторых веб-функций.
- Проверьте, что вы раскрываете. Запустите проверку отпечатка BrowserInsight, чтобы увидеть состояния разрешений, canvas, WebGL и другие сигналы, которые сейчас раскрывает ваш браузер, все вместе.
Часто задаваемые вопросы
Требует ли запрос разрешений взаимодействия пользователя или показа диалога?
Нет. navigator.permissions.query() сообщает уже существующее состояние без показа диалога. Только фактическое использование функции, защищённой разрешением (например, вызов getCurrentPosition() для геолокации), запускает запрос — и только когда состояние prompt, а не уже granted или denied.
Может ли эта техника сама по себе меня идентифицировать?
Не слишком надёжно сама по себе. У большинства пользователей большинство разрешений остаются в состоянии prompt по умолчанию, поэтому у вектора состояний ограниченная энтропия сама по себе. Трекерам он полезнее всего в сочетании с паттерном поддержки браузера (коррелирующим с движком/версией) и другими пассивными сигналами вроде canvas или шрифтов.
Почему некоторые имена разрешений выбрасывают ошибки вместо возврата состояния?
Потому что браузер вообще не реализует соответствующую функцию. navigator.permissions.query({ name: 'magnetometer' }) выбрасывает ошибку в браузере без поддержки Generic Sensor API, поскольку не существует системы разрешений, состояние которой можно было бы проверить для функции, которой попросту нет в этом движке.
Останавливает ли это resistFingerprinting?
privacy.resistFingerprinting в Firefox снижает часть различий в поведении, связанном с разрешениями, как часть общей стратегии единообразия, но сам API и его поведение при сообщении состояния остаются — он сужает сигнал, а не устраняет его, точно так же, как и для canvas и API вёрстки.
Заключение
Permissions API — небольшое разумное удобство: сначала проверь, потом спрашивай — которое при этом бесплатно раскрывает два независимых сигнала: какие названия разрешений вообще распознаёт браузер и в каком состоянии сейчас находится каждое из них. По отдельности ни один из них не громкий, но в сочетании с десятками других пассивных сигналов, которые страница может прочитать без единого запроса, это ещё одна причина, почему «фингерпринтинг» означает куда больше, чем canvas и WebGL.
Рекомендуем почитать:


