# Multi-accounting

> When one person runs many accounts: linking accounts by browser setup, and the honest limits of doing so.

Source: https://whiskerjs.com/use-cases/multi-accounting/ (last updated 2026-10-26)

> **In plain words:**
>
> Some people open many accounts where only one is allowed, to get around limits, bans or votes. Each account looks new to the server. If they all come from the same browser on the same computer, WhiskerJS gives them the same device description, so your server can see that the accounts are linked and decide what that means for your rules.

## The problem

Multi-accounting covers sock-puppet accounts, ban evasion, vote or review manipulation and limits that apply per person. New email addresses and new IP addresses are cheap. A browser setup is more work to change completely, which makes it a useful link between accounts. It is still only a link: many innocent situations share a device.

## What WhiskerJS contributes

- **visitorId** as a key: store it with every sign-up and login, and count distinct accounts per id.
- **structuredId** for near matches: an attacker who changes a few things (a new browser profile, a different language) often leaves several groups unchanged.
- **integrityFlags** for tooling: anti-detect browsers and spoofing scripts change values that the checks compare.

## Signals and flags that matter

- **Groups that are hard to change casually**: `gpu` ([`canvas`](https://whiskerjs.com/signals/canvas/), [`webgl_renderer`](https://whiskerjs.com/signals/webgl-renderer/), [`webgl_image`](https://whiskerjs.com/signals/webgl-image/)), `fonts` ([`fonts`](https://whiskerjs.com/signals/fonts/), [`fonts_canvas`](https://whiskerjs.com/signals/fonts-canvas/)) and `device` ([`screen_resolution`](https://whiskerjs.com/signals/screen-resolution/), [`hardware_concurrency`](https://whiskerjs.com/signals/hardware-concurrency/), [`cpu_architecture`](https://whiskerjs.com/signals/cpu-architecture/)).
- **Groups that change easily**: `locale` and `prefs` follow user settings, so a difference there alone means little.
- **Spoofing signs**: [`iframe_mismatch`](https://whiskerjs.com/docs/integrity/#iframe_mismatch), [`prototype_lie`](https://whiskerjs.com/docs/integrity/#prototype_lie), [`native_tampering`](https://whiskerjs.com/docs/integrity/#native_tampering), [`navigator_own_property`](https://whiskerjs.com/docs/integrity/#navigator_own_property) and [`impossible_hardware`](https://whiskerjs.com/docs/integrity/#impossible_hardware). The first four also fire for privacy extensions; `impossible_hardware` is not expected in current browsers.
- **Noise as a signal**: a `noisy` canvas or WebGL component, or [`canvas_readback`](https://whiskerjs.com/signals/canvas-readback/) reporting `altered`, means something randomises readback. That is normal in some privacy setups and also common in tools built to vary fingerprints.

## Using structuredId similarity

Exact matches find the simple cases. For the rest, count how many groups two accounts share:

```ts
import { ID_GROUP_ORDER } from 'whiskerjs';

/** Groups shared by two structuredIds of the same idVersion. */
function sharedGroups(a: string, b: string): string[] {
  const pa = a.split('_');
  const pb = b.split('_');
  if (pa[0] !== pb[0]) return [];
  return ID_GROUP_ORDER.filter((_, i) => pa[i + 1] === pb[i + 1]);
}

// Example review rule, to tune on your own data:
// flag a pair of accounts for review when they share gpu, fonts and device.
const STRONG = ['gpu', 'fonts', 'device'];
const linked = (a: string, b: string) => STRONG.every((g) => sharedGroups(a, b).includes(g));
```

Comparing every account with every other one does not scale. Index each group part separately (for example a table of `(idVersion, signalsVersion, group, part, account)`) and look up candidates by part. Popular configurations, such as a common laptop model with default settings, will share many groups; weigh a shared part by how common it is on your own traffic.

## Anti-replay

A tool that pastes one good result into every sign-up would create many accounts with one perfect device. The binding makes each result single-use: a fresh nonce per form, checked once, with the digest recomputed on your server. See [Anti-replay binding](https://whiskerjs.com/docs/integration/#anti-replay-binding).

## What must happen on your server

1. Store per sign-up and login: `visitorId`, `structuredId`, `idVersion`, `signalsVersion`, flags, account and time.
2. Count accounts per `visitorId` and per strong group combination, within a time window you choose.
3. Combine with other links: payment instruments, email patterns, IP ranges, behaviour.
4. Send links to review or apply your rule, and record why. Shared devices are legitimate in many households and workplaces; avoid acting on the device link alone.
5. When you upgrade WhiskerJS, ids change. Never compare ids across versions; instead link them: for one release after a bump the result carries `previousVisitorId` and `previousStructuredId`, the ids the version before would have given, so you can find the stored row and keep the old and new ids side by side (see [previous ids](https://whiskerjs.com/docs/identifiers/#previous-ids-linking-across-a-version-bump)).

## Limits

- Identical machines with default settings can collide. A shared id is a lead, not a conclusion.
- Separate devices, separate virtual machines or a determined anti-detect setup can defeat device linking entirely.
- Under Brave farbling, Firefox and Safari fingerprinting protection, Firefox resistFingerprinting and Tor Browser some components are excluded, which makes those devices more alike.
- No collision or uniqueness figures have been measured for WhiskerJS. Look at how many accounts share ids on your own traffic before you set a rule.

## Questions and answers

### Does a shared visitorId mean the accounts belong to one person?

No. It means the same browser setup. Families, shared computers, internet cafes and company fleets of identical machines can share an id. Use it as a link to review, not as proof.

### What about users who switch browsers to make a second account?

Different browsers on one device usually differ in the browser group but often share groups such as fonts, device or gpu. Comparing structuredId groups can surface that, as a hint.

### Can I store ids forever to catch old accounts?

Keep retention short and tied to the purpose, and remember ids change with WhiskerJS versions. See the privacy pages for what a site has to consider.

## Related pages

- [Bonus and promotion abuse](https://whiskerjs.com/use-cases/bonus-abuse/): Multi-accounting aimed at offers.
- [Signals](https://whiskerjs.com/signals/): Every component and its group.
- [structuredId](https://whiskerjs.com/docs/identifiers/#structuredid): The seven groups and their order.
- [GDPR and ePrivacy](https://whiskerjs.com/privacy/gdpr-eprivacy/): Consent and lawful basis questions.
