Multi-layered anti-fraud and bot detection system for registration flows...
A three-layer defense system for registration forms that detects bots while minimizing false positives for legitimate users.
Layer 1: SERVER-SIDE (tamper-proof)
βββ Encrypted timestamp token verification
βββ Device fingerprint matching
βββ Minimum fill time enforcement (3s)
Layer 2: MANIPULATION DETECTION (server comparison)
βββ Client vs Server timing mismatch
βββ Keystroke/input inconsistency
βββ Impossible value detection
Layer 3: CLIENT SIGNALS (informational)
βββ Honeypot fields
βββ Behavioral analysis
βββ Content analysis
Key Principle: Never trust client-side data alone.
// /api/auth/form-token
// Generate AES-256-GCM encrypted token with timestamp
const token = encrypt({ timestamp: Date.now(), fingerprint, nonce });
interface BehaviorSignals {
totalFillTimeMs: number;
fieldTimings: Record<string, number>;
inputMethods: Record<string, 'typed' | 'pasted' | 'autofilled' | 'mixed'>;
keystrokes: KeystrokeData[];
keystrokeVariance: number;
mouseMovements: MouseMovement[];
hasMouseActivity: boolean;
focusSequence: string[];
tabKeyUsed: boolean;
backspaceCount: number;
}
Add hidden fields (CSS hidden, aria-hidden, tabIndex=-1):
website, phone2, address, companyAny content in honeypot β Instant shadow ban
| Trigger | Condition |
|---|---|
| Server timing | Fill time < 3 seconds |
| Token | Invalid or missing |
| Manipulation | High confidence detection |
| Score | >= 80 points |
| Honeypot | Any field filled |
| Disposable domain |
See references/signal-weights.md for complete weight tables.
Critical (+100): HONEYPOT_FILLED, DISPOSABLE_EMAIL
High (+25-40): INSTANT_SUBMIT, ALL_FIELDS_PASTED, BOT_PASSWORD_PATTERN, NO_MOUSE_MOVEMENT
Positive (-5 to -40): PASSWORD_MANAGER_LIKELY, KEYBOARD_ONLY_USER, NATURAL_TYPING_RHYTHM
if (shouldShadowBan) {
await delay(1000 + Math.random() * 2000); // Appear legitimate
return Response.json({ message: 'Registration successful' }, { status: 200 });
// No account created, no backend call
}
const isPasswordManager =
allFieldsAutofilledOrPasted &&
keystrokeCount < 5 &&
fillTime >= 1000 && fillTime < 15000;
const isKeyboardOnly =
tabKeyUsed &&
focusSequence.length >= 2 &&
!hasMouseActivity &&
totalFieldTime > 1000;
src/
βββ lib/anti-fraud/
β βββ index.ts
β βββ types.ts
β βββ constants.ts
β βββ risk-scoring.ts
β βββ server-token.ts
β βββ manipulation-detector.ts
β βββ validators/
β βββ email-validator.ts
β βββ name-validator.ts
β βββ password-validator.ts
βββ hooks/use-behavior-tracking.ts
βββ components/anti-fraud/honeypot-fields.tsx
βββ app/api/auth/
βββ form-token/route.ts
βββ register/route.ts
references/signal-weights.mdreferences/validators.mdreferences/detection-patterns.mdAUTH_SECRET=your-secret-key-for-token-encryption
All decisions logged with [ANTI_FRAUD] prefix:
[ANTI_FRAUD] { timestamp, emailDomain, serverFillTimeMs, summary: 'Risk: 25/100 (low) - allow' }