Securing Login & Signup Flows
Credential stuffing and fake account waves are the two most common attacks on any site with a login form. Here's how to stop both without adding friction for the humans you actually want.
What you're actually defending against
Credential stuffing is the replay of username/password pairs leaked from other sites. Because people reuse passwords, a list that's 0.1% effective against your login form is still a profitable attack — and it looks like thousands of individually plausible login attempts, not one obvious flood.
Fake account creation feeds everything downstream: spam, referral fraud, free-tier abuse, review manipulation. By the time you notice it in your metrics, the accounts already exist and cleanup is expensive.
Both attacks share one property: they're automated. That's the property to test for at the door, before a single credential is checked or a single row is written.
1. Gate the form with Edge Shield
Add the Edge Shield widget to your login and signup forms, and verify the token server-side before your authentication logic runs. Tokens are single-use and expire after five minutes, so a bot can't harvest one token and replay it across a credential list.
// Login route: verify the Shield token before touching credentials
const res = await fetch('https://shield.edge.network/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: process.env.SHIELD_SECRET,
response: req.body['edge-shield-response'],
remoteip: req.ip,
}),
})
const { success, score } = await res.json()
if (!success) return res.status(403).send('Verification failed')
if (score < 40) return requireEmailConfirmation() // step up, don't hard-block
// Only now check the password — bots never reach your auth logic
const user = await authenticate(req.body.email, req.body.password)
Two details matter here. First, pass remoteip —
it lets Shield corroborate the client's network reputation. Second, the ordering:
rejecting before authenticate() means
stuffing attempts never touch your user table, which also keeps them out of your
failed-login metrics and lockout logic.
If you haven't set up a widget yet, the Bot Protection with Edge Shield guide covers it end to end — it's two lines of markup and free forever.
2. Use the score to step up, not just block
Every verification returns a 1–100 humanity score. A hard pass/fail gate wastes that signal. For signups in particular, a graduated policy converts uncertain traffic instead of losing it:
| Signal | Signup policy |
|---|---|
| Score 70–100 | Create the account normally. |
| Score 40–69 | Create the account, but require email confirmation before first use. |
| Score 1–39 | Reject, or hold for review. Watch for verified agents — they may be legitimate automation. |
For logins, treat a low score on a correct password as its own signal — that's the signature of a stuffed credential that happens to be valid. Step up to email confirmation or 2FA rather than letting the session through, and you've turned a successful account takeover into a failed one.
3. Keep the boring layers too
Shield removes the bulk automation, but defence in depth still applies:
- Rate-limit per account, not just per IP — stuffing attacks rotate IPs cheaply; five failed attempts against one account is a better tripwire than fifty from one address.
- Return identical errors for "wrong password" and "no such user", on the same response timing, so the form can't be used to enumerate accounts.
- Check passwords against known-breach lists at signup and password change — most stuffing lists are built from exactly those breaches.
- Offer 2FA — it's the single strongest control against account takeover, and you should enable it on your own Edge account as well.