Skip to main content
A Sybil attack is one person wearing many faces: in a crypto airdrop, a governance vote or a per-person quota, the rule is one human, one wallet, but one person spins up dozens of wallets to claim the reward many times over. They all trace back to the same machine or network. ShieldLabs treats each wallet as an account, links it to the devices and network behind it, and detects Multi-accounting across your wallets, so you see how many wallets one person runs before the reward pays out. The Device ID holds through cleared cookies, incognito mode and IP changes.

What is a Sybil attack?

A Sybil attack is when a single actor forges many distinct identities (wallets, addresses or accounts) to gain disproportionate influence over a system that assumes each identity is a separate person. It is the standard way airdrops get farmed, on-chain votes get swayed, and one-per-customer quotas get drained.

How ShieldLabs surfaces it

ShieldLabs ties each claim to the wallet’s account, detects when many wallets are run by one person, and scores the claim itself. Four layers answer four questions: The anchor for the count is the Device ID, derived server-side from the browser environment, so a cookie clear, a private window or a rotated VPN IP does not reset it. Each wallet carries its own hashed User HID; the Visitor ID changes when cookies are cleared, but the Device ID holds, and the distinct wallets behind it are the “separate” wallets on one machine. When the person rotates the public IP per wallet, the Local IP, the address the browser itself reports, can expose the network behind the exit.

Prevent Sybil attacks

The eligibility policy: at claim time, read the wallet’s account, the claim’s risk_score and signals, and the distinct wallets behind its device_id (History API) and its local_ip.ip (your own webhook store). When the count crosses your one-human-one-wallet limit, or the wallet has a Multi-accounting event, whether it reached you through the API or webhooks or you reviewed it in the analytics dashboard, route the claim to verification or reject it. Weigh the Risk Score (0-100) and its signals as evidence of masking: a single clean wallet pays out, while a machine or network already behind a cluster of wallets, or a Dangerous-band masked claim, is held. ShieldLabs stops Sybil farming by linking the wallets and scoring the claim; you choose pay, verify or reject for each case. The steps below wire it up.

Build it

1

Identify the claim in the browser

Add the snippet to the page where the wallet claims the reward (the claim button, vote screen or quota form). Each wallet is an account to ShieldLabs: pass a hash of the wallet address as its User HID, never the raw address. Pass a hashed User HID with checkAuthenticatedUser on every signed-in page. Users, account-level risk and all four High-Risk Events are built on it. Within one visit (while a page of your site stays open in the browser, across route changes in a single-page app and across open tabs), checkAnonymous and checkAuthenticatedUser run at most one identification every five minutes for the same user. A call inside that window posts nothing, counts nothing, and its onInitialized handler receives { status: "not_initialized" }. On a multi-page site, a full page load in the only open tab can start a new visit with its own identification. So call forceCheckAuthenticatedUser when the claim page opens: it runs an identification every time, keeps the current Session ID and restarts the five-minute window, so the claim always gets its own request ID. onInitialized fires when the check starts, before the snippet has sent the identification, so do not navigate away inside it: store the request ID and let the form submit normally.
claim.html
2

Read the scored result on your server

The scored result arrives by webhook. Verify the X-Shield-Signature HMAC, cache it by request_id, and read it back with the shared waitForScore helper, which falls back to a History API read by request_id. The fields the claim decision needs:
public_ip is the public IP and country a VPN can fake; local_ip is the address the browser itself reports, which can differ from the public IP behind a VPN or proxy. Group claims by local_ip.ip as a second key alongside device_id: wallets that share one Local IP across different devices and public IPs are the network-level Sybil shape. The History API has no Local IP search, so keep that grouping in your own store. Keep the Local IP server-side; it is for your own logic, not for end users.
3

Read the wallet behind the claim

The claim is one identification. The wallet behind it has a history. The shared accountView helper reads the wallet’s earlier identifications from the History API by user_hid: the worst band across them shows how risky the wallet has been, and its distinct Device IDs and public IPs are the devices and networks it is linked to. A wallet whose worst band is Dangerous goes to verification, and so does a wallet with a Multi-accounting event, recorded in your own system when it arrives through the API or webhooks or when you review it in the analytics dashboard. The handler in the next step reads both.In the analytics dashboard, the wallet’s user card shows the same view: its band for the selected period, its High-Risk Events, each pill coloured by its confidence, and each linked device, visitor and IP with the band of the identifications it shares with the account. User, device, visitor and IP cards describes each part.
4

Count the wallets behind the device and decide

The Risk Score tells you whether one claim looks masked. How many wallets sit behind the machine is a separate count, off the durable device_id. The shared accountsBehindDevice helper reads the History API by device_id and counts every distinct wallet that device touched, leaving out "anonymous". Then choose the action.
Read a device's history
api/claim.js
detection_flags.ip_mismatch marks two different addresses. It is informational, adds nothing to the Risk Score and can be ordinary on mobile networks, so the handler compares the two countries and weighs them with the wallet count rather than branching on the flag alone.The analytics dashboard shows the same count: open a Device ID from Analytics, and Linked accounts lists every wallet seen on it, each with the band of its identifications on that device.
The device card for Device ID d290f1ee-6c54-4b01-90e6-d701748f0851 in the analytics dashboard: band Dangerous, 14 identifications, and Linked accounts open with 6 accounts: 3 Dangerous, 1 Suspicious and 2 Trusted.The device card for Device ID d290f1ee-6c54-4b01-90e6-d701748f0851 in the analytics dashboard in the dark theme: band Dangerous, 14 identifications, and Linked accounts open with 6 accounts: 3 Dangerous, 1 Suspicious and 2 Trusted.

One Device ID in the analytics dashboard, with every account linked to it.

5

See the cluster over time

The per-claim check catches a claim right now. The standing view is the Multi-accounting High-Risk Event, which ShieldLabs detects directly with Medium or High confidence and makes available in the analytics dashboard, the API and webhooks. It covers both Sybil shapes:
One device linked to many distinct wallets: the core Sybil shape. A cleared cookie or a private window does not reset the Device ID.
Many wallets claiming through one network, even when each rotates its public IP. Catches a person spread across several devices behind one router.
When a Multi-accounting event arrives for a wallet through the API or webhooks, or when you review it in the analytics dashboard, record it against the wallet in your own system, and the handler above routes its next claim to review; you choose the action for each case. At the claim itself, the Risk Score and risk signals of the identification remain the input.
History API reads never count against your included identifications. For a high-volume airdrop, keep your own counters (wallets per Device ID and per local_ip.ip from the webhook) as the fast path, and reserve live History reads for the borderline claims worth the cost.
6

Tune to your claim traffic

Start in logging-only mode and watch how real claims distribute before you raise friction. A real participant on a corporate VPN can reach the Suspicious band, and one wallet on a shared office network is not a farm. Decide on the Risk Score, its signals and the wallet count together.

Test it

You do not need a real farm to see this work. Connect a wallet and claim once in your normal browser, and note the device_id on the webhook. Then play the Sybil farmer: clear cookies or open a private window, and claim again with a different wallet. The cookie_id and visitor_id change each time, but the same device_id returns, and the distinct-wallet count on that device climbs with every run, exactly the count your claim endpoint gates on. A second browser gets its own Device ID; the network the wallets share can still link them. Toggling a VPN lights up the risk signals without changing the Device ID. Then search Analytics in the analytics dashboard for that Device ID and open it: Linked accounts lists every wallet you used in the test, each with the band of its identifications on that device.

Next

Catch Multi-Accounting

The same one-person-many-accounts shape outside crypto: count the accounts behind a device at signup and at action time.

Stop Promo Abuse

Gate a per-customer reward on the account count behind the device: the web2 cousin of an airdrop farm.

High-Risk Events

Multi-accounting and the other events ShieldLabs detects on your users, each with Medium or High confidence.

Webhooks

The payload your server reads, with the device_id, risk_score, signals and detection_flags fields the claim decision uses.