Back to blog

Browser fingerprinting: implementing fraud detection techniques in the era of AI

Auth & identity

Jul 16, 2025

Author: Stytch Team

Browser fingerprinting: implementing fraud detection techniques in the era of AI

Browser fingerprinting is one method for identifying visitors to your websites. It uses information that can be obtained from your web browser to identify key characteristics about you. However, like cookies, these attributes can be faked, and sophisticated mechanisms are required to identify users for fraud detection and security — especially with novel AI powered attacks becoming more prevalent.

The rising sophistication of AI-powered fraud

AI presents an ever-increasing challenge to the security of web applications. It has always been possible to simulate keyboard inputs and mouse clicks using testing tools like Selenium, Puppeteer, and Playwright. But now, generative AI and agents make it easy for anyone to write a simple prompt to drive entire web browsers and pass themselves off as legitimate web users with inputs, mouse movements, and other signals that make a session appear genuine.

This presents a scalability challenge: You may find yourself defending against thousands of fake autogenerated AI users, each with their own unique browser session and activity, increasing the probability that some bots get through or that the AI behind the attack can learn from its failures and evolve its strategies to evade your defenses.

Browser fingerprinting techniques as part of an anti-fraud strategy

A number of attributes are available for calculating a browser fingerprint which, when combined, can uniquely identify a user.

Browser fingerprinting creates fingerprints: identifiers based on browser attributes including user agent, IP address, hardware, cookies settings, screen resolution, operating system, HTTP headers, browser settings, language and keyboard configuration, and information from JavaScript/HTML5.

Unlike cookies, these fingerprints are deterministically generated and then stored server-side. Once they’re calculated, they are difficult to impersonate and manipulate.

A browser fingerprint is calculated by hashing information from the user's web browser and device.

Passive browser fingerprinting techniques only use information provided by the browser in the course of making a HTTP request. This can be extended for more accuracy using active browser fingerprinting like JavaScript and HTML5 technologies. For example, canvas fingerprinting and audio capabilities can glean extra information. Still, the commonly deployed active browser fingerprinting methods are not enough for fraud prevention and security for modern applications.

Device fingerprinting is a tamper-resistant browser fingerprinting solution that adds additional signals calculated on the device itself to create highly unique fingerprints. Device fingerprinting offers a secure and robust way to uniquely identify visitors and determine whether they are legitimate or not.

Browser and device fingerprinting technologies must be compliant with privacy regulations, as well as being vetted and exploit-free. This requires that these tools be continuously improved to meet emerging requirements and new exploits. These challenges, combined with the imminent threat of fraud (which often places security ahead of privacy concerns) requires balance and makes implementing and maintaining fingerprinting code an increasingly complex burden for development teams.

What does a browser fingerprinting implementation look like?

Here is a code snippet that demonstrates (some of) what you need to do to implement browser fingerprinting on the client side:

// Note this code is for example purposes only and not for production use. You should never copy and paste security-related code into your project without fully understanding and vetting it for your use case.

async function getBrowserFingerprint() {
    // Collect data from the web browser to build the browser fingerprint
    const canvasFingerprint = getCanvasFingerprint();
    const fingerprint = {
        cookiesEnabled: navigator.cookieEnabled,
        localStorageEnabled: !!window.localStorage,
        sessionStorageEnabled: !!window.sessionStorage,
        screenSize: `${screen.width}x${screen.height}`,
        language: navigator.language,
        browserPlugins: Array.from(navigator.plugins).map(p => p.name),
        cpuCount: navigator.hardwareConcurrency,
        memoryGB: navigator.deviceMemory || 'unavailable',
        canvasFingerprint: canvasFingerprint,
    };

    // Submit the fingerprint data to the back-end server, which will add additional server-side data and generate a fingerprint hash
    try {
        const response = await fetch('http://localhost:3000/fingerprint', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(fingerprint)
        });

        const result = await response.json();

        // Print the returned fingerprint data to the console. 
        console.log('Fingerprint Hash:', result.hash);
        console.log('Match:', result.match);
        console.log(Threat Score:', result.threat);
    } catch (err) {
        console.error('Fingerprinting Error:', err);
    }
}

// Function to generate a canvas fingerprint
function getCanvasFingerprint() {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    ctx.textBaseline = 'top';
    ctx.font = "16px 'Arial'";
    ctx.fillStyle = '#f60';
    ctx.fillRect(125, 1, 62, 20);
    ctx.fillStyle = '#069';
    ctx.fillText(Canvas Fingerprint', 2, 15);
    ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
    ctx.fillText(Canvas Fingerprint', 4, 17);
    return canvas.toDataURL();
}

// Run browser fingerprinting on page load
getBrowserFingerprint();

The example code above performs basic browser fingerprinting using attributes including screen resolution, language, the availability of certain browser features, and canvas fingerprinting.

Canvas fingerprinting works by using an invisible HTML5 <canvas> element to render text and shapes. The overlaps and offsets cause the canvas's rendering to vary across devices with different browsers, GPUs, graphics drivers, or even with different fonts installed. The canvas is then converted to a base64 string, which can be hashed into a fingerprint. Audio fingerprinting works similarly, using inconsistencies between devices audio handling to differentiate them.

An overview of the process for creating a canvas fingerprint for browser fingerprinting.

Here's an example of what the server-side implementation may look like in an Express.js route:

// Note this code is for example purposes only and not for production use. You should never copy and paste security-related code into your project without fully understanding and vetting it for your use case.


// Import the crypto library at the top of the file
const crypto = require('crypto');

// Example browser fingerprinting Express.js route 
app.post('/fingerprint', (req, res) => {
    // Read the client data from the HTTP POST request
    const clientData = req.body;

    // Validate request data, more comprehensive validation would be required in a real world scenario 
    if (!clientData || typeof clientData !== 'object') {
        return res.status(400).json({ error: 'Invalid data' });
    }

    // Read the data that cannot be generated on the client from the HTTP request
    const serverData = {
        ipAddress: req.headers['x-forwarded-for'] || req.socket.remoteAddress,
        userAgent: req.headers['user-agent'],
        acceptLanguage: req.headers['accept-language'],
        httpHeaders: req.headers
    };

    // Combine the client and server data
    const fullBrowserFingerprint = {
        ...clientData,
        ...serverData
    };

    // Hash the data to create a unique browser fingerprint string that can be stored and compared 
    const fingerprintJson = JSON.stringify(fullBrowserFingerprint);
    const hash = crypto.createHash('sha256').update(fingerprintJson).digest('hex');

    // To match fingerprints to previous sessions, they would need to be stored in a database and compared to find matches, allowing for small variations in fingerprints for legitimate users and identifying malicious visitors who are trying to evade detection 
    const match = false; 

    // A threat score (that can be used to decide how to treat the user) would also need to be calculated based on fingerprint attributes, traffic source and user/cohort behaviors that require intelligent processing
    const threat = 0; 

    // Return the response
    res.json({
        hash: hash,
        match: match,
        threat: threat 
    });
});

Like the client-side code, this is only a rudimentary example that uses signals readily provided by the browser. Each attribute used here is incredibly easy to block or spoof. Given the vast and ever-increasing number of internet users, and that many will connect from shared IP addresses, block cookies, and use identically or very similarly configured devices, these attributes are no longer enough to identify a user with certainty.

Handling the changes to fingerprints over time is also a complex problem to solve. The fingerprints of well-behaved users will shift over time for various reasons: browser updates, timezone changes, and new locations are common changes for real users. This requires allowing for small variations in fingerprints from the same user, while still uniquely identifying new sessions and weeding out users who are deliberately trying to mask their fingerprint. False-positive mitigation is required as overzealous security code could lead to real users being locked out of your app.

Evolving anti-fingerprinting defenses further add to the complexities of implementing browser fingerprinting. It's not just attackers and bots that deploy these measures, either: popular web browsers are incorporating these features to address the concerns of privacy-conscious users. Furthermore, keeping your anti-spoofing measures up to date and tuned to be able to discern these users from bots is a significant development project in itself.

The easier, faster, and more secure alternative to DIY JavaScript browser fingerprinting

Development teams are increasingly relying on authentication as a service that includes device fingerprinting for fraud prevention, freeing them of the complexities of implementing highly specialized fingerprinting systems and the management tools for reviewing and fine tuning them so that they do not misidentify (and potentially block) valid users.

How Stytch creates and manages browser fingerprints for you, including decision making.

Device Fingerprinting in Stytch's Fraud and Risk Prevention product uses active JavaScript on the client. It includes browser and canvas fingerprinting combined with other signals to collect as much information as possible about a user. It can then assess their risk and allow, block, or challenge (using strong CAPTCHA) their authentication attempt.

Here's how device fingerprinting can be added to your website’s login flow using Stytch:

<html>
<head>
    <script src="https://elements.stytch.com/telemetry.js"></script>
</head>
<body>
    <button id="login" onclick="login()">Click here to log in!</button>

    <script>
        function login() {
            var publicToken = "PUBLIC_TOKEN"; // public token from Stytch Dashboard
            GetTelemetryID({
                publicToken: publicToken
            }).then((telemetry_id) => {
                var request = new XMLHttpRequest();
                request.open('POST', 'https://www.example.com/login', true);
                request.setRequestHeader('X-Telemetry-ID', telemetry_id);
                request.send(null);
            });
        }
    </script>
</body>
</html>

You can then use Stytch's telemetry ID to look up the user's browser, network, and device fingerprints programmatically using the Stytch API:

curl --request POST \
--url 'https://telemetry.stytch.com/v1/fingerprint/lookup' \
-u 'PROJECT_ID:SECRET'
-d '{
    "telemetry_id": "'${TELEMETRY_ID}'"
  }'

An example API response for this is shown below:

{
    "created_at": "2023-01-01T00:00:00Z",
    "expires_at": "2033-01-01T00:00:00Z",
    "fingerprints": {
        "browser_fingerprint": "browser-fingerprint-0b535ab5-ecff-4bc9-b845-48bf90098945",
        "browser_id": "browser-id-99cffb93-6378-48a5-aa90-d680232a7979",
        "hardware_fingerprint": "hardware-fingerprint-4af7a05d-cf77-4ff7-834f-0622452bb092",
        "network_fingerprint": "network-fingerprint-b5060259-40e6-3f29-8215-45ae2da3caa1",
        "visitor_fingerprint": "visitor-fingerprint-6ecf5792-1157-41ad-9ad6-052d31160cee",
        "visitor_id": "visitor-6139cbcc-4dda-4b1f-b1c0-13c08ec64d72"
    },
    "status_code": 200,
    "telemetry_id": "026ac93b-8cdf-4fcb-bfa6-36a31cfecac1",
    "verdict": {
        "action": "BLOCK",
        "detected_device_type": "UNKNOWN_DEVICE",
        "is_authentic_device": false,
        "reasons": [“USER_AGENT_DECEPTION”, “KNOWN_DATACENTER_IP”]
    }
}

You can use the details in this response along with Stytch's recommended verdict to take actions in your own code by blocking, redirecting, allowing, or challenging requests

Stytch analyzes over 1 billion signals per day from Device Fingerprinting to help you identify good traffic versus bad. The verdict provides advanced warning flags indicating traditional or AI-powered browser automation, VPN or proxy usage, and other suspicious activity like attempts to send a fake user agent.

This not only adds fingerprinting to your website, but also provides you with metrics showing how many of your visitors are real users and the risk associated with them, all visible through the Stytch dashboard or accessible via the API.

Stytch device fingerprinting dashboard view.

Detailed information is readily available through the Stytch dashboard, giving you full insight into all user fingerprints, and why an allow, block, or challenge verdict was reached for each of them.

Stytch dashboard allow block challenge verdicts

By requiring that users authenticate to access any exploitable resources and using short-lived renewable tokens, you can continuously assess users and watch for signs of fraudulent or malicious behavior. At the user level, you can block each attempted action from a bot or known abusive user based on their fingerprint and verdict. Across all your users, the dashboard can identify patterns like a large increase in traffic from an unusual country so that you can investigate further.

Don't leave fraud protection and your broader security strategy to chance

Online fraud and emerging AI threats carry significant risks to your business: Account hijacking, resource and trial abuse, and data leaks can all lead to a loss of reputation, loss of existing and new customers, or legal repercussions in the case that private information is disclosed.

In-house security and authentication code, including browser fingerprinting, can leave your code vulnerable if it’s not developed and constantly updated by security experts. Reliance on open-source libraries or generated code can also lead to security vulnerabilities if the code is not regularly updated and fully understood by those leveraging it.

Stytch provides a comprehensive authentication and fraud prevention solution that is developed, hardened, and managed by security experts — All you need to do is add Stytch to your projects and let us handle the rest. Check out Stytch now.








Share this article