Information Security 11 min read

How to Generate Reliable Browser Fingerprints with Navigator, Canvas, and WebGL

This article explains what browser fingerprinting is, outlines common techniques such as Navigator, Canvas, and WebGL fingerprints, and provides complete JavaScript examples that collect device information and generate stable hashes, helping developers understand and implement device‑unique identifiers for identity verification.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
How to Generate Reliable Browser Fingerprints with Navigator, Canvas, and WebGL

What is Browser Fingerprinting?

Browser fingerprinting is a set of feature values that uniquely identify a browser. It collects information such as browser, operating system, screen resolution, fonts, plugins, etc., and combines them into a unique ID. Unlike cookies, it does not store anything on the device.

Use Case

In a recent project I needed a unique credential from the user's device to verify identity. Directly accessing hardware identifiers like IMEI or serial numbers is impossible from JavaScript, so I turned to browser fingerprinting.

Feasible Solutions

Navigator fingerprint : browser type, version, platform, and other information.

Canvas fingerprint : render a hidden image and read pixel differences.

WebGL fingerprint : use graphics driver differences.

Font, plugin, timezone, screen resolution, etc.

Combining multiple signals improves uniqueness.

Navigator Fingerprint

Navigator is the primary interface for obtaining browser and device environment information.

Common properties and methods (with good cross‑browser support):

navigator.userAgent

– Returns the user‑agent string, useful for detecting browser and OS.

navigator.platform

– Returns the operating system platform (e.g., Win32, Linux x86_64, MacIntel).

navigator.appVersion

– Returns browser version and some platform info.

navigator.appName

– Returns the browser name (most modern browsers return “Netscape”).

navigator.language

– Returns the preferred language (e.g., “en-US”).

navigator.languages

– Returns an array of preferred languages.

navigator.hardwareConcurrency

– Returns the number of logical processors.

navigator.plugins

– Returns the list of installed plugins (desktop browsers only).

navigator.onLine

– Indicates whether the browser is online.

navigator.cookieEnabled

– Indicates whether cookies are enabled.

navigator.geolocation

– Provides geolocation services (requires user permission).

navigator.maxTouchPoints

– Maximum number of touch points supported.

navigator.mediaDevices

– Access to audio/video device APIs.

navigator.clipboard

– Read/write system clipboard (requires HTTPS and permission).

navigator.connection

– Network connection information (bandwidth, type, etc.).

navigator.userAgentData

– Newer user‑agent information object with better privacy.

Below is a concise example that gathers these Navigator properties, hashes them with SHA‑256, and displays the result.

<code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
<head>
    &lt;title&gt;Navigator Fingerprint Example&lt;/title&gt;
&lt;/head&gt;
<body>
    &lt;h2&gt;Navigator Fingerprint Example&lt;/h2&gt;
    &lt;pre id="output"&gt;&lt;/pre&gt;
    &lt;script&gt;
        async function getNavigatorFingerprint() {
            const data = {
                userAgent: navigator.userAgent,
                platform: navigator.platform,
                language: navigator.language,
                languages: navigator.languages,
                cookieEnabled: navigator.cookieEnabled,
                hardwareConcurrency: navigator.hardwareConcurrency || 'N/A',
                deviceMemory: navigator.deviceMemory || 'N/A',
                webdriver: navigator.webdriver || false,
            };
            const dataString = JSON.stringify(data);
            const hashBuffer = await crypto.subtle.digest(
                "SHA-256",
                new TextEncoder().encode(dataString)
            );
            const hashArray = Array.from(new Uint8Array(hashBuffer));
            const hashHex = hashArray.map(b =&gt; b.toString(16).padStart(2, '0')).join('');
            return { data, fingerprint: hashHex };
        }
        getNavigatorFingerprint().then(result =&gt; {
            const output = document.getElementById('output');
            output.textContent =
                "Collected Navigator data:\n" + JSON.stringify(result.data, null, 2) +
                "\n\nGenerated fingerprint (SHA-256):\n" + result.fingerprint;
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code>

Canvas Fingerprint

Different devices render the same Canvas content with subtle variations due to OS, GPU, drivers, and font rendering, producing distinct image data that can be hashed into a fingerprint. This method is generally more stable than Navigator alone.

<code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
<head>
    &lt;title&gt;Simple Canvas Fingerprint&lt;/title&gt;
&lt;/head&gt;
<body>
    &lt;h2&gt;Simple Canvas Fingerprint&lt;/h2&gt;
    &lt;p&gt;Open the console (F12) to see the result&lt;/p&gt;
    &lt;script&gt;
        function generateCanvasFingerprint() {
            const canvas = document.createElement('canvas');
            canvas.width = 200;
            canvas.height = 100;
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = 'red';
            ctx.fillRect(20, 20, 50, 50);
            ctx.fillStyle = 'blue';
            ctx.beginPath();
            ctx.arc(120, 45, 25, 0, Math.PI * 2);
            ctx.fill();
            ctx.fillStyle = 'black';
            ctx.font = '16px Arial';
            ctx.fillText('Canvas fingerprint', 60, 80);
            const dataURL = canvas.toDataURL();
            function simpleHash(str) {
                let hash = 0;
                for (let i = 0; i < str.length; i++) {
                    const char = str.charCodeAt(i);
                    hash = ((hash << 5) - hash) + char;
                    hash = hash & hash;
                }
                return hash.toString(16);
            }
            const fingerprint = simpleHash(dataURL);
            return { fingerprint, dataURL };
        }
        const result = generateCanvasFingerprint();
        console.log('Canvas fingerprint:', result.fingerprint);
        console.log('Canvas dataURL first 100 chars:', result.dataURL.substring(0, 100) + '...');
        if (window.crypto && window.crypto.subtle) {
            const encoder = new TextEncoder();
            const data = encoder.encode(result.dataURL);
            window.crypto.subtle.digest('SHA-256', data)
                .then(hashBuffer => {
                    const hashArray = Array.from(new Uint8Array(hashBuffer));
                    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
                    console.log('Canvas fingerprint (SHA-256):', hashHex);
                });
        }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code>

Other fingerprinting methods follow similar principles and are omitted for brevity.

JavaScriptWeb Securitybrowser fingerprintingnavigator APIcanvas fingerprint
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.