Browser permission prompts represent an often-overlooked vector for user fingerprinting. While most users understand that websites can request access to their camera, microphone, or location, fewer realize that simply presenting these permission dialogs creates a unique behavioral fingerprint. This article examines how notification request APIs and other browser permissions enable tracking, with practical code examples and mitigation strategies.
Understanding Permission-Based Fingerprinting
When a website requests a browser permission, the browser displays a prompt asking the user to grant or deny access. The response to these prompts—and even the mere presence of certain permission requests—generates identifiable patterns that websites can exploit for tracking purposes.
Unlike traditional cookie-based tracking, permission fingerprinting operates without storing persistent data on the user’s device. Instead, it relies on the unique way browsers handle permission requests across different contexts, operating systems, and user behaviors.
The Notification Permission API as a Tracking Vector
The Notification API provides one of the most significant fingerprinting opportunities. When a site calls the request permission method, several factors create identifiable signals:
// Check current permission status
const currentStatus = Notification.permission;
// Request notification permission
Notification.requestPermission().then(permission => {
console.log('Permission status:', permission);
});
The Notification.permission property returns one of three values: granted, denied, or default. This single value provides a coarse but useful tracking signal. However, the more insidious tracking occurs through behavioral analysis of how users respond to these prompts.
How Permission Requests Create Trackable Fingerprints
Prompt Response Patterns
Users who consistently deny permission requests present a different fingerprint than those who grant them. Websites can request multiple permissions and observe the pattern of grants and denials:
async function fingerprintPermissions() {
const results = {};
// Request various permissions and record responses
const permissions = [
'notifications',
'geolocation',
'camera',
'microphone',
'clipboard-read'
];
for (const perm of permissions) {
try {
const result = await navigator.permissions.query({ name: perm });
results[perm] = result.state;
} catch (e) {
results[perm] = 'unsupported';
}
}
return results;
}
// Example output
// { notifications: 'default', geolocation: 'granted', camera: 'denied', ... }
This technique, sometimes called “permission fingerprinting,” creates a permission state vector that remains stable across sessions for many users. A user who denies camera access but allows location requests produces a different signature than one who reverses these choices.
Timing and Behavioral Signals
Beyond the permission states themselves, the timing and frequency of permission requests provide additional tracking data. Sites can measure:
- Time elapsed since the page loaded before the user responds
- Whether the user has previously seen similar prompts
- Browser behavior differences between grant and deny actions
// Measure response time to permission prompts
const startTime = Date.now();
Notification.requestPermission().then(permission => {
const responseTime = Date.now() - startTime;
// Combine response time with permission state for fingerprinting
const fingerprint = {
permission: permission,
responseTime: responseTime,
timestamp: new Date().toISOString()
};
// Send fingerprint data to analytics
trackFingerprint(fingerprint);
});
Cross-Origin Permission State Leaks
Modern browsers attempt to isolate permission states between origins, but implementation differences create fingerprinting opportunities. The Permission API sometimes reveals states from related domains or subdomains, allowing trackers to build more complete profiles:
// Check if permissions granted on other domains leak through
async function checkCrossOriginPermissions() {
// This query behavior varies across browsers
const status = await navigator.permissions.query({
name: 'notifications',
// Some implementations may leak origin information
});
return status.state;
}
Real-World Tracking Implications
Persistence Without Storage
Unlike cookies that users can delete or ad blockers can remove, permission fingerprints persist until users manually change their browser settings. A user who granted notification permissions on one site carries that state to every subsequent site that requests the same permission.
This persistence makes permission fingerprinting particularly valuable for long-term tracking. Advertisers can combine permission states with other signals to create more persistent user profiles.
Device and Browser Fingerprinting
Permission capabilities vary significantly across devices and browsers. A mobile device running Safari presents different permission capabilities than a desktop Chrome installation. Trackers combine permission availability with other fingerprinting techniques:
// Combine permission fingerprinting with device capabilities
function comprehensiveFingerprint() {
const fp = {
// Permission states
notifications: Notification.permission,
geolocation: 'geolocation' in navigator,
// Hardware capabilities
hardwareConcurrency: navigator.hardwareConcurrency,
deviceMemory: navigator.deviceMemory,
// Browser details
userAgent: navigator.userAgent,
platform: navigator.platform
};
return fp;
}
Protecting Against Permission Fingerprinting
For Users
Several strategies help mitigate permission-based tracking:
-
Standardize responses: Grant or deny permission requests consistently across sites to reduce the uniqueness of your permission pattern.
-
Use browser privacy features: Firefox’s Enhanced Tracking Protection and Brave’s Shields include protections against permission fingerprinting.
-
Review permissions regularly: Clear unwanted permissions through browser settings.
-
Use containerization: Firefox Multi-Account Containers isolate permission states between contexts.
For Developers
Web developers can implement responsible permission handling:
// Responsible permission request pattern
async function requestNotificationPermission() {
// Only request if not already determined
if (Notification.permission === 'default') {
// Use a clear, non-coercive UI pattern
const shouldPrompt = await showUserFriendlyRequest();
if (shouldPrompt) {
return Notification.requestPermission();
}
}
return Notification.permission;
}
// Respect user's existing decision
function initializeNotifications() {
if (Notification.permission === 'granted') {
enableNotificationFeatures();
} else if (Notification.permission === 'denied') {
// Don't repeatedly prompt - respect the denial
showFallbackUI();
}
}
Browser Vendor Responses
Browser vendors continue implementing protections against permission fingerprinting:
- Chrome limits cross-origin permission state queries
- Firefox randomizes some permission prompt behaviors
- Safari requires user activation before permission requests
These protections evolve as vendors discover new fingerprinting techniques.
Related Articles
- Browser Permission Prompt Fingerprinting
- Browser Connection Pooling Fingerprinting How Http2 Connecti
- Browser Fingerprinting Protection Techniques
- Browser Fingerprinting How It Works and How to Prevent It
- Browser Fingerprinting: What It Is and How to Block It
Built by theluckystrike — More at zovo.one