Permissions
Your Permissions Fingerprint
Every well-known permission name, queried silently. The set of supported names, and their default states, is browser-specific.
Permissions
Querying browser permission states...
How anti-bot vendors use this
- Set of supported names. Chromium supports clipboard-read/write and background-sync; Firefox and Safari don't. If your UA claims Chrome but clipboard-read reports "unsupported", that's a leak.
- Notifications state cross-check. If
Notification.permission === "denied"whilepermissions.query({name: "notifications"}).state === "prompt", you're headless Chrome. - All-granted or all-denied profiles. Real users pick a mix. Uniform answers imply automation.
Frequently asked questions
→What does navigator.permissions.query do?
It reports the current state of a named permission for this origin, without triggering a prompt. States are granted, denied, or prompt. It's the standard way for a page to decide whether it's worth asking the user for camera/mic/geolocation access.
→Why is this a fingerprinting signal?
Two reasons. First, the set of *supported* names differs by browser: Chromium accepts 'clipboard-read', Firefox doesn't. Second, the state of Notification.permission vs permissions.query({name: 'notifications'}) is a classic headless Chrome leak (Notification says 'denied' while permissions.query says 'prompt', a contradiction).
→Does querying a permission ever prompt the user?
No. query() is silent. Only calling getUserMedia(), Notification.requestPermission(), etc. can prompt. That's what makes this a free signal for anti-bot scripts.
→What do the results look like on a real Chrome?
Most defaults are 'prompt', because the user hasn't decided. Persistent-storage is often 'granted' for large origins. Camera/microphone are 'prompt' until explicitly granted or denied. A profile where everything is 'denied' at once is suspicious.
→How do stealth plugins handle this?
They patch navigator.permissions.query to return spec-compliant, plausible defaults, matching what a real Chrome on a fresh profile would say. The trick is to keep it consistent with Notification.permission and with what getUserMedia() would actually do if called.