Websites use browser permission prompts (for notifications, camera, microphone, location) as fingerprinting vectors to track users by analyzing how quickly you respond to prompts, whether you grant or deny permissions, and which APIs are available on your browser. These behavioral fingerprints persist across sessions even when cookies are blocked, creating a unique identifier based on your permission handling patterns. You can defeat this by denying all permission requests, using permission management extensions, or enabling “Always ask for permissions” to randomize your patterns.
What Is Permission Prompt Fingerprinting
When a website requests access to browser features like notifications, camera, microphone, or location, the browser displays a permission prompt. This interaction creates several tracking opportunities:
- Permission state detection: Websites can query whether you’ve already granted or denied specific permissions
- Prompt behavior: How quickly you respond to prompts, whether you allow or block, and your interaction patterns
- API availability: The presence or absence of certain APIs can reveal browser configuration
- Browser-specific responses: Different browsers handle permission requests differently, creating distinguishing signals
Unlike traditional fingerprinting techniques that rely on hardware or software characteristics, permission prompt fingerprinting focuses on user behavior and browser state—making it particularly difficult to detect and block.
How the Notification API Enables Tracking
The Notification API is one of the most commonly exploited permission APIs for fingerprinting. Here’s how it works:
Checking Permission Status
// Check if notifications are already permitted
function getNotificationStatus() {
if ('Notification' in window) {
return Notification.permission;
}
return 'not-supported';
}
// Possible return values:
// 'default' - user hasn't been prompted yet
// 'granted' - user has allowed notifications
// 'denied' - user has blocked notifications
This simple check reveals whether a user has previously interacted with the notification prompt. Since users rarely clear permission states, this becomes a persistent tracking identifier.
Detecting Browser-Specific Behavior
Different browsers expose permission APIs differently:
// Detect browser vendor through permission API behavior
function detectBrowserViaPermissions() {
const results = {
hasNotification: 'Notification' in window,
hasPermissions: 'permissions' in navigator,
hasPush: 'PushManager' in window,
notificationPermission: Notification.permission
};
// Different browsers return different combinations
return results;
}
// Compare behavior across browsers
const perms = detectBrowserViaPermissions();
// Chrome: { hasNotification: true, hasPermissions: true, hasPush: true }
// Firefox: { hasNotification: true, hasPermissions: true, hasPush: true }
// Safari: { hasNotification: true, hasPermissions: false, hasPush: false }
Timing-Based Fingerprinting
The timing of your response to permission prompts creates a unique signature:
function fingerprintPermissionTiming() {
const results = [];
// Request notification permission and measure response time
const startTime = performance.now();
Notification.requestPermission().then(permission => {
const responseTime = performance.now() - startTime;
// Different users have different response times
// Some immediately deny, others take time to read the prompt
results.push({
permission: permission,
responseTimeMs: responseTime,
timestamp: Date.now()
});
// Send fingerprint data to server
sendFingerprintData(results);
});
}
This timing data—when combined with other signals—creates a unique behavioral profile.
Other Permission APIs Used for Fingerprinting
Beyond notifications, several other permission APIs can be exploited:
Geolocation Permission
// Query geolocation permission status
function getGeoPermission() {
if ('geolocation' in navigator) {
return navigator.permissions.query({ name: 'geolocation' })
.then(result => {
return result.state; // 'granted', 'denied', or 'prompt'
});
}
return Promise.resolve('not-supported');
}
Camera and Microphone
// Check camera/mic permission status
function getMediaPermissions() {
const permissions = {};
if ('mediaDevices' in navigator) {
navigator.permissions.query({ name: 'camera' })
.then(result => permissions.camera = result.state);
navigator.permissions.query({ name: 'microphone' })
.then(result => permissions.microphone = result.state);
}
return permissions;
}
Clipboard Access
// Clipboard permission reveals user behavior
function checkClipboardPermission() {
if ('clipboard' in navigator) {
return navigator.permissions.query({ name: 'clipboard-read' })
.then(result => result.state);
}
return 'not-supported';
}
Combining Permission Signals for Unique Identification
The real power of permission fingerprinting comes from combining multiple signals:
function getCompletePermissionFingerprint() {
const fingerprint = {
notification: Notification.permission,
geolocation: null,
camera: null,
microphone: null,
clipboard: null,
midi: null,
persistentStorage: null,
backgroundSync: null,
notificationsEnabled: Notification.permission === 'granted'
};
const queries = [
navigator.permissions.query({ name: 'geolocation' }),
navigator.permissions.query({ name: 'camera' }),
navigator.permissions.query({ name: 'microphone' }),
navigator.permissions.query({ name: 'clipboard-read' }),
navigator.permissions.query({ name: 'midi' }),
navigator.permissions.query({ name: 'persistent-storage' }),
navigator.permissions.query({ name: 'background-sync' })
];
const names = ['geolocation', 'camera', 'microphone', 'clipboard',
'midi', 'persistentStorage', 'backgroundSync'];
return Promise.all(queries).then(results => {
results.forEach((result, index) => {
fingerprint[names[index]] = result.state;
});
// Generate unique hash from combined permissions
return hashPermissionFingerprint(fingerprint);
});
}
function hashPermissionFingerprint(fp) {
const combined = Object.values(fp).join('|');
// Create hash for tracking
let hash = 0;
for (let i = 0; i < combined.length; i++) {
const char = combined.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash.toString(16);
}
This combined fingerprint can uniquely identify users because:
- Different users grant different permissions to different sites
- Browser defaults vary between browsers
- Users develop unique patterns of allowing/denying permissions
- The combination of all permission states creates a high-entropy identifier
Real-World Tracking Techniques
Cross-Site Permission Harvesting
Malicious sites can build permission profiles by:
- Checking existing permission states for known services
- Requesting new permissions and tracking responses
- Correlating permission patterns across multiple domains
- Building user profiles based on permission similarities
// Example: Check if user has allowed common notification services
function checkNotificationServices() {
const services = [
{ name: 'OneSignal', url: 'https://onesignal.com' },
{ name: 'Pushwoosh', url: 'https://pushwoosh.com' },
{ name: 'Airship', url: 'https://urbanairship.com' }
];
// Attempt to detect which push services user has subscribed to
services.forEach(service => {
// Check localStorage for service-specific tokens
const hasToken = localStorage.getItem(service.name.toLowerCase() + '_token');
// Cross-reference with permission state
if (Notification.permission === 'granted' && hasToken) {
trackUserPreference(service.name);
}
});
}
Browser Extension Fingerprinting
Extensions can also be detected through permission prompts:
// Detect installed extensions by checking their permission requirements
function detectExtensionsViaPermissions() {
const knownExtensions = [
{ id: 'cjpalhdlnbpafiamejdnhcphjbkeiagm', name: 'uBlock Origin' },
{ id: 'gighmmpiobklfepjocnamgkkbiglidom', name: 'AdBlock' },
{ id: 'nmioekflnmbiannmkhjbbplncbdcarge', name: 'Privacy Badger' }
];
// Check if certain permissions are blocked by extensions
const blocked = [];
// Try to access APIs that extensions might block
try {
// Extensions often block certain fingerprinting APIs
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// If certain APIs fail or return unexpected results,
// it may indicate extension interference
} catch (e) {
blocked.push('canvas');
}
return blocked;
}
How Browsers Are Responding
Browser vendors are aware of these tracking techniques and are implementing protections:
Chromium Changes
- Permission Prompts: Chrome now limits how often sites can request permissions
- Quiet Prompts: Google introduced “quieter” notification prompts to reduce manipulation
- Permission Revocation: Users can now easily revoke previously granted permissions
Firefox Protections
- Permission Manager: Firefox provides centralized permission management
- Auto-Revoke: Firefox can automatically revoke permissions for unused sites
- Enhanced Tracking Protection: Includes permission-based tracking detection
Safari Changes
- Intelligent Tracking Prevention: Safari blocks cross-site permission-based tracking
- Permission Limits: Safari limits the lifetime of granted permissions
- User Prompt Changes: Safari requires more user interaction for permission requests
How to Protect Yourself
Browser Settings
- Review Permission Settings Regularly
- Chrome: Settings → Privacy and Security → Site Settings → Permissions
- Firefox: Settings → Privacy & Security → Permissions
- Safari: Preferences → Websites → Permissions
- Use Permission Managers
- Install browser extensions that manage permissions
- Use “ask” default for all permissions
- Clear Permission States
- Periodically reset permissions for all sites
- Use browser tools to view and manage granted permissions
Extension Protection
Several privacy extensions help block permission fingerprinting:
- Privacy Badger: Automatically blocks detected permission-based trackers
- uBlock Origin: Can block permission query APIs
- NoScript: Provides fine-grained control over permission APIs
Browser Choice
Some browsers are more resistant to permission fingerprinting:
- Brave: Blocks most permission-based tracking by default
- Tor Browser: Uses uniform permission states for all users
- Firefox (Strict Mode): Includes enhanced permission protections
Detecting Permission-Based Tracking
To check if a site is using permission fingerprinting:
- Check Network Requests
- Look for requests to analytics services with permission-related data
- Monitor for unusual permission API queries
- Use Developer Tools
- Check console for permission-related errors or warnings
- Monitor JavaScript execution for permission queries
- Test with Fingerprinting Test Sites
- Use sites likecovery.org or AmIUnique to see what permissions they can detect
Related Reading
- Browser Permission Prompt Fingerprinting How Notification Re
- 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