Stop Account Takeover: Credential Stuffing & Stolen Sessions
Leaked password lists are replayed through rotating residential proxies, one attempt per address, so per-IP limits never trip. Stolen session cookies skip the login form and are replayed in fake (antidetect) browsers. Maskbreak checks the network and the browser behind each login, password reset and account change in one server-side call, with nothing for your customer to solve.
What you're up against
Credential stuffing behind residential proxies
Stolen credential lists get replayed through rotating residential proxies, so every attempt arrives from a different clean home IP. Rate limits and IP blocklists count the one thing the attacker changes with every attempt.
Stolen sessions replayed in fake browsers
Attackers import hijacked cookies into fake (antidetect) browsers such as Kameleo or GoLogin and imitate the victim's browser well enough to pass naive fingerprint checks. The session is genuine and never touches your login form, so the check that catches it sits at the next sensitive action.
Automation that paces itself like a human
Headless browsers with stealth patches spread attempts across hours and thousands of exit IPs, staying under every velocity threshold. By the time login-failure dashboards show a spike, most of the list has already been tested.
Takeovers you learn about from support tickets
A successful takeover looks like a normal login in your logs. You find out when the victim writes in — after the email was changed, the payout redirected, or the stored card used — and each of those tickets costs trust a refund won't win back.
What Maskbreak does for you
- See the network behind every attempt: proxy (residential exits included), VPN, cloud server or Tor, with the VPN or proxy service named when known
- Flag fake browsers and automation at login, before the password compare and before a session exists
- Send the account ID and device.linked_accounts counts how many of your accounts one device has tried: the signature of a tool working a stolen list
- Store device.visitor_id at each sign-in, so a device the account has never used, arriving over a VPN, gets a one-time code instead of a session
- One server-side call per login, reset and account change: nothing for a legitimate customer to solve, and a one-time code only where you choose review
can carry a credential-stuffing run and stolen-card checkouts alike, because a residential exit passes IP reputation checks. A worked model of the card side on a D2C store and what the verdict returns at checkout. Illustrative scenario, not a customer result.
Read the PlaybookWhere the check goes
In the login handler the check goes after the account lookup and before the password compare, so the verdict carries the account ID and no password hash is computed for an attempt you will refuse. On block, answer exactly like a wrong password: same status, same body. A different answer tells the attacker which attempts were caught, and wherever the check runs after the password compare, which passwords were right. Add the same call to password reset (the request and the completion) and to email, phone and payout changes, where a stolen session does its damage. The endpoint-by-endpoint guide covers all six call sites with the rule for each. The example starts in watch mode: it logs each verdict, lets every attempt through and fails open if Maskbreak is unreachable. After a few days, compare the log with the Events in your console, then uncomment the enforce line; the integration guide has the full enforce checks.
// Login: after the account lookup, before the password compare and the session // Watch mode first: log the verdict, always continue, fail open const body = req.body || {}; // sentinel.js adds monocle + sentinel_fp to your form let risk = null; try { const verdictRes = await fetch('https://maskbreak.com/v1/evaluate', { method: 'POST', signal: AbortSignal.timeout(5000), headers: { 'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ token: body.token || body.monocle, // network evidence fingerprintEventId: body.fingerprintEventId || body.sentinel_fp, // device evidence tz: body.tz || body.sentinel_tz, accountId: user?.id // the account looked up by email; adds device.linked_accounts }) }); if (verdictRes.ok) risk = await verdictRes.json(); } catch {} // unreachable or timed out: your handler still runs console.log('[maskbreak]', JSON.stringify({ decision: risk?.decision ?? null, reasons: risk?.reasons ?? null, risk_score: risk?.risk_score ?? null, degraded: risk ? !!risk.degraded : null, visitor_id: risk?.device?.visitor_id ?? null, linked_accounts: risk?.device?.linked_accounts ?? null })); // Enforce later: if (risk?.decision === 'block') return res.status(401).json({ error: 'Invalid email or password' }); // your exact wrong-password answer
The signals that matter here
Network context on every attempt. A stuffing run rents rotating residential proxies so each attempt arrives on a household address no blocklist has seen. Maskbreak's network intelligence judges the address as a relay, not by its reputation: when it knows the exit, the response lists proxy_detected, with network.proxy true and network.residential false, and the default decision is block. Each attempt is judged on its own, so spreading the list at one attempt per address does not dilute it. More on how residential exits are detected.
One device, many accounts. Takeover tooling works a stolen list from one machine. Send the looked-up account's ID as accountId and the response adds device.linked_accounts: how many of your accounts this device has been seen with. Above one, the reasons include multi_account_device. The links are one-way hashes kept per customer and never cross to another customer. The reason does not change the decision on its own, because families share laptops; set your threshold in code. A Rules-tab multi-accounting action applies to that signal only: it can raise the answer, but it never lowers the proxy or automation block on the same attempt. (device.times_seen is different: it counts sightings across all Maskbreak customers, so it says whether a device is new to Maskbreak, not to your account.)
The takeover shape: a new device on an old account. An account that has signed in from the same laptop for a year suddenly authenticates from a visitor_id it has never used, over a VPN. Store device.visitor_id with each successful sign-in and compare. A VPN alone returns review, never block, and a first-seen device is not proof either; people buy laptops. Together they are the moment for a one-time code, which real customers pass and a stolen password alone does not. Add automation_detected or antidetect_browser and the default is already block. Routing review to step-up covers the thresholds.
Stolen sessions, caught at the next sensitive action. A stolen session cookie is genuine and never passes your login form. The browser replaying it often is not: fake browsers leave inconsistencies that show as device.antidetect, antidetect_browser in the reasons and a higher tampering_score. So check again at email, phone, password and payout changes, compare the visitor_id with the one that signed in, and hold the change on review. The account's owner never sees a CAPTCHA, because the verdict is an API response, not a challenge.
Example response for one attempt in a stuffing run: a proxy exit, a scripted browser, and a device that has now tried 38 of your accounts (excerpt).
{
"decision": "block",
"risk_score": 90,
"reasons": ["proxy_detected", "automation_detected", "multi_account_device"],
"network": { "vpn": false, "proxy": true, "datacenter": false, "tor": false, "residential": false, "service": "<proxy service when known, else null>" },
"device": { "automation": true, "antidetect": false, "tampering_score": 0, "visitor_id": "<visitor ID>", "linked_accounts": 38, "multi_account": true }
}Common questions
Start protecting your login endpoint
Free plan: 10,000 visitor checks a month, no card. Paid plans from €29 a month. Flags proxies (residential exits included), fake browsers and scripted logins.