Orange textured background

Anti-fraud

How to detect duplicate signups and multi-accounting

Email, IP, and cookie checks all fail against a determined multi-accounter. Device identity is the layer that holds.

Multi-accounting survives every identity check users control: new email addresses, VPNs, cookie clears, different browsers. A hardware-bound device fingerprint with cross-browser comparison is the durable key that catches the same device regardless of how the account layer is reset.

Why email and IP checks miss most multi-accounting

The naive defenses against duplicate signups are well known: check for duplicate email addresses, block disposable email domains, rate-limit by IP address, and look for repeated payment methods. Each of these works against unsophisticated attempts. None of them hold against a user who is willing to spend ten minutes on evasion.

A new email address takes thirty seconds to create. A VPN or residential proxy changes the apparent IP. A cookie clear or new browser resets any browser-side identifier. A prepaid card or a different payment method sidesteps payment fingerprinting. Platforms that rely on these checks alone are enforcing a policy that determined abusers can bypass with minimal effort.

The scale can be significant: gaming and rewards platforms that run promotions have reported signup volume anomalies of hundreds or thousands of percent during bonus events, with a large fraction of those accounts controlled by a small number of physical devices.

Device identity as the durable key

The layer that multi-accounters cannot easily reset is the physical device. Creating a new email, changing an IP, and clearing cookies all take seconds. Getting a new device takes money.

A hardware-bound device fingerprint derived from properties of the physical machine (GPU identity, CPU architecture, installed fonts, screen geometry, and related hardware-bound signals) produces a stable identifier that is the same in Chrome, Safari, Firefox, and Brave on the same machine, the same in incognito as in a normal session, and the same after cookies are cleared.

This is the `hardwareFingerprint` field returned by `getFingerprint`. At signup, you collect a fingerprint, store the `hardwareFingerprint` on your backend alongside the new account, and check it against existing account records. A match does not automatically mean fraud, but it is a strong signal worth acting on.

Matching new signups against known devices with compareFingerprints

A raw hash equality check on `hardwareFingerprint` is the simplest approach: if the hash matches an existing account's stored hash, flag for review. This works for most cases but misses one failure mode: minor hardware drift.

Hardware signals can shift on legitimate events: a display resolution change, a major OS version update, an external monitor connecting. If you store a fingerprint and check equality six months later, some legitimate returning users will fail a strict hash match even though the device is the same.

`compareFingerprints` with the `cross-browser` mode solves this. It compares only hardware-bound signals, applies per-signal fuzzy matching for signals like screen dimensions that drift legitimately, and returns a numeric `matchScore` (0 to 100) alongside a `hardwareMatch` boolean. You can calibrate your response based on the score rather than treating every mismatch as a definitive non-match.

npm install doorman-benny
import { getFingerprint, compareFingerprints } from 'doorman-benny';

// At signup: collect a fingerprint and store it with the new account.
async function onSignup(userId: string) {
  const result = await getFingerprint();

  // Send the full FingerprintResult to your backend.
  // Your backend stores hardwareFingerprint and the full result
  // for later comparison.
  await fetch('/api/accounts/fingerprint', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      userId,
      hardwareFingerprint: result.hardwareFingerprint,
      fingerprintResult: result,
    }),
  });
}

// At signup check: compare the new fingerprint against stored records.
async function findDuplicateDevice(
  storedResult: object,   // previously stored FingerprintResult
  currentResult: object,  // just collected FingerprintResult
): Promise<{ isDuplicate: boolean; matchScore: number }> {
  // cross-browser mode: hardware-bound signals only, fuzzy matching on.
  // This is the right mode for 'same device regardless of browser'.
  const comparison = compareFingerprints(
    storedResult as any,
    currentResult as any,
    { mode: 'cross-browser' }
  );

  return {
    // hardwareMatch is string equality of the two hardwareFingerprint fields.
    // match is the weighted similarity verdict using the tuned threshold.
    isDuplicate: comparison.hardwareMatch || comparison.match,
    matchScore: comparison.matchScore,
  };
}

Store the full FingerprintResult, not just the hardwareFingerprint hash. You need the full result to call compareFingerprints later for fuzzy matching and score-based decisions.

Tuning strictness without internal cutoffs

The `compareFingerprints` API exposes a `matchThreshold` option that lets you set the minimum `matchScore` for `match: true`. The library ships a default tuned for typical re-identification workloads. You can override it.

A higher threshold reduces false positives (fewer legitimate users flagged as duplicates) at the cost of more false negatives (some real multi-accounters pass). A lower threshold catches more multi-accounting but increases the rate at which legitimate users trigger a review.

A practical starting pattern: use `hardwareMatch` as a high-confidence duplicate signal and queue for automatic review, use `matchScore` above a custom threshold as a medium-confidence signal and queue for manual review, and use low scores as clean. This gives your operations team a workable triage queue rather than a binary decision.

You can also exclude signals that drift frequently in your user population using the `excludeSignals` option, or add the `includeSignals` option to restrict comparison to the subset you trust most.

import { compareFingerprints } from 'doorman-benny';

function assessDuplicateRisk(
  stored: object,
  current: object,
): 'high' | 'medium' | 'low' {
  const result = compareFingerprints(
    stored as any,
    current as any,
    {
      mode: 'cross-browser',
      // Raise the bar to reduce false positives in your population.
      // Start with the default, measure your false-positive rate, then tune.
      matchThreshold: 90,
    }
  );

  // hardwareMatch: string equality of the two hardwareFingerprint fields.
  // Highly specific; treat as high-confidence.
  if (result.hardwareMatch) return 'high';

  // matchScore: weighted similarity from fuzzy comparison.
  // Good for catching near-matches where one signal drifted.
  if (result.matchScore >= 75) return 'medium';

  return 'low';
}

Three-tier triage: hardwareMatch for automatic flagging, high matchScore for manual review, low score for clean. Adjust the score band to match your false-positive tolerance.

What to do on a match

A device match at signup is a risk signal, not a definitive fraud verdict. The same device can legitimately produce two accounts: a parent and child sharing a computer, a developer testing with two accounts, or a user who deleted their old account and signed up again.

Recommended responses scaled by confidence: for a `hardwareMatch` (exact hash equality), trigger step-up verification such as email confirmation, a phone check, or a brief manual review before activating the account. For a high `matchScore` without exact hash equality, add to a review queue and monitor the account's first few actions. For ambiguous scores, log the match data and re-evaluate if the account exhibits subsequent suspicious behavior.

Pair the device match signal with `result.consistency.spoofLikelihood`. A device match from an account running a 'high' spoof likelihood is a materially stronger fraud signal than a device match from a clean browser environment.

Response actions by match confidence

  • hardwareMatch: true (exact hash equality) - trigger step-up verification before account activation: email confirmation, phone check, or brief manual review.
  • matchScore high, hardwareMatch: false (near-match with minor drift) - add to a review queue and monitor the account's first actions for abuse patterns.
  • matchScore moderate - log the signal, tag the account for monitoring, and re-evaluate if subsequent behavior is suspicious.
  • matchScore low - treat as clean for the device-identity check; other signals (email domain, payment method, behavior) take over.

Frequently asked questions

Will a VPN defeat the device fingerprint check?

No. The fingerprint is collected from browser APIs that measure hardware and engine properties, not network properties. The IP address is not part of the fingerprint. A user can change their VPN, their IP address, or their apparent geographic location without affecting the hardwareFingerprint.

Can someone defeat this by using a different device?

Yes. If a user creates accounts on genuinely different physical devices, each device will produce a different hardwareFingerprint and the comparison will not match. This is the correct behavior: the goal is to detect the same device reusing multiple accounts, not to detect a person across devices. Cross-device multi-accounting requires additional signals such as shared payment methods or behavioral patterns.

How do I handle the case where a legitimate user gets a new device?

When a user purchases a new device and signs up again, they will get a different hardwareFingerprint. If their old account is in good standing, this is a normal account-creation event. If the old account was flagged or suspended, a new device signup from a different fingerprint is still a legitimate user. The device fingerprint check is one signal in a broader risk assessment, not a permanent block.

How does this compare to ThumbmarkJS for this use case?

ThumbmarkJS returns a single hash but does not provide a built-in comparison API or a separate cross-browser hardware hash. For multi-accounting detection across browser switches, you would need to build the comparison logic yourself and handle the fact that the same user in Chrome and Firefox will produce different ThumbmarkJS hashes. Benny's cross-browser mode and hardwareFingerprint are designed specifically for this scenario.

Is storing device fingerprints compliant with GDPR?

Storing device fingerprints for fraud prevention purposes is generally covered by legitimate interests under GDPR and fraud-prevention exemptions under ePrivacy frameworks, provided the processing is proportionate and disclosed. The laws/fingerprinting-anti-fraud-exemption page covers the legal landscape for this use case. This post is engineering documentation and not legal advice.

Get started

Add device-level duplicate detection to your signup flow

One npm install. Cross-browser hardware matching and a built-in comparison API in the free tier. Start catching multi-accounting that email and IP checks miss.

Get startedcompareFingerprints API reference

Related posts

Last reviewed June 9, 2026