Skip to content

O-X-L/browser-fingerprint

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Client-Side Browser-Fingerprinting

This is an example on how to generate a simple browser-fingerprint for your Web-Clients which you can use for traffic analysis.

Test it: fingerprint.oxl.app

Be aware:

  • As we have to set this fingerprint as cookie or request-header - the user could manually modify or replace it.
  • If attackers are aware of this they are able to work around it. (p.e. randomize the hash)
  • Clients with noscript or bots without JS-capabilities will not send any fingerprint.

You have to think of those facts when you are analyzing your request-logs.


You might want to combine it with server-side client-fingerprinting techniques like JA4. See:

More information:


Example

We utilize the OSS thumbmarkjs library.

Result

  • Linux Laptop Chromium 136.x => linux_chrome_1536x864_347e44db7f27cb8c236bec1ae84791ce
  • Linux Laptop Chromium 136.x private tab => linux_chrome_1536x864_347e44db7f27cb8c236bec1ae84791ce
  • Linux Laptop Firefox 128.x => linux_firefox_1536x864_957bc354b66b5f2def573500c8c0f466
  • Linux Laptop Firefox 128.x private tab => linux_firefox_1536x864_957bc354b66b5f2def573500c8c0f466

Minimal Code

<script src="https://cdn.jsdelivr.net/npm/@thumbmarkjs/thumbmarkjs/dist/thumbmark.umd.js"></script>
<script>...</script>
ThumbmarkJS.setOption('logging', false);

// NOTE: some browsers and private-modes will change those values - if you don't care about that or want more 'uniqueness' you can remove the entries
ThumbmarkJS.setOption('exclude', ['webgl', 'canvas', 'permissions', 'audio.sampleHash']);

function screenSize() {
    let [w, h] = [screen.width, screen.height];
    // handle device rotation if mobile
    if (navigator.maxTouchPoints > 0 && window.matchMedia("(orientation: landscape)").matches) {
        w = screen.height;
        h = screen.width;
    }
    return Promise.resolve({width: w, height: h});
}
ThumbmarkJS.includeComponent('size', screenSize);

/**
 * Generates a minimal cleartext key. This information can be useful to categorize requests.
 * @returns {string}
 */
function buildBrowserFingerprintKey(c) {
    let s = c.system.platform.split(' ')[0].toLowerCase();

    if (navigator.userAgent.match(/iPhone|iPad/i)) {
        s = 'ios';
    } else if (navigator.userAgent.includes('Android')) {
        s = 'android';
    }

    const k = [
        s,
        c.system.browser.name.toLowerCase(),
        `${c.size.width}x${c.size.height}`,
    ];
    if (c.screen.is_touchscreen) {
        k.push('touch');
    };
    return k.join('_');
}

/**
 * Generates the full browser fingerprint string.
 * @returns {Promise<string>} A Promise that resolves with the fingerprint string.
 */
async function generateBrowserFingerprint() {
    try {
        const c = await ThumbmarkJS.getFingerprintData();
        const h = await ThumbmarkJS.getFingerprint();
        return `${buildBrowserFingerprintKey(c)}_${h}`;
    } catch (error) {
        console.error('Error generating fingerprint:', error);
        // Re-throw the error so the caller can handle it
        throw error;
    }
}

/* replace with your implementation: */
async function setFingerprintCookie() {
    const fp = await generateBrowserFingerprint();
    console.log(fp);
    document.cookie = `fingerprint=${fp}`;
}

async function performSomeRequest() {
    const fp = await generateBrowserFingerprint();
    const headers = {
        'X-Fingerprint': fp,
        'Content-Type': 'application/json'
    };
    console.log(headers);
}

setFingerprintCookie();
performSomeRequest();