Brute Force Detection: Patterns, Thresholds, and Response Strategies
A comprehensive guide to detecting and preventing brute force attacks on your SaaS application with practical implementation examples.

Brute force attacks remain one of the most common threats to SaaS applications. Despite being conceptually simple—try every possible password until one works—they're surprisingly effective when defenses are weak.
The Anatomy of a Brute Force Attack
Modern brute force attacks have evolved beyond simple password guessing:
Credential Stuffing
Attackers use leaked username/password combinations from other breaches, hoping users reused credentials.
Password Spraying
Instead of trying many passwords against one account, attackers try common passwords against many accounts to avoid lockouts.
Distributed Attacks
Requests come from thousands of IPs (botnets), making IP-based blocking ineffective.
Detection Patterns
Pattern 1: High Velocity Single-Target
10:00:01 - auth.login_failed - user@example.com - 203.0.113.50
10:00:02 - auth.login_failed - user@example.com - 203.0.113.50
10:00:03 - auth.login_failed - user@example.com - 203.0.113.50
10:00:04 - auth.login_failed - user@example.com - 203.0.113.50
10:00:05 - auth.login_failed - user@example.com - 203.0.113.50
Indicators:
- Same target account
- Same source IP
- Rapid succession (< 1 second apart)
Detection Rule:
const BRUTE_FORCE_THRESHOLD = 5;
const WINDOW_SECONDS = 60;
async function detectBruteForce(event: SecurityEvent) {
const key = `bf:${event.actor_id}:${event.ip_address}`;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, WINDOW_SECONDS);
}
if (count >= BRUTE_FORCE_THRESHOLD) {
await createAlert({
type: "brute_force",
severity: "critical",
actor_id: event.actor_id,
ip_address: event.ip_address,
attempt_count: count,
});
}
}
Pattern 2: Distributed Single-Target
10:00:01 - auth.login_failed - user@example.com - 203.0.113.50
10:00:02 - auth.login_failed - user@example.com - 198.51.100.23
10:00:03 - auth.login_failed - user@example.com - 192.0.2.78
10:00:04 - auth.login_failed - user@example.com - 172.16.0.45
10:00:05 - auth.login_failed - user@example.com - 10.0.0.123
Indicators:
- Same target account
- Different source IPs
- Coordinated timing
Detection Rule:
const DISTRIBUTED_THRESHOLD = 10;
const WINDOW_SECONDS = 300;
async function detectDistributedBruteForce(event: SecurityEvent) {
const key = `dbf:${event.actor_id}`;
await redis.sadd(key, event.ip_address);
await redis.expire(key, WINDOW_SECONDS);
const uniqueIPs = await redis.scard(key);
if (uniqueIPs >= DISTRIBUTED_THRESHOLD) {
await createAlert({
type: "distributed_brute_force",
severity: "critical",
actor_id: event.actor_id,
unique_ips: uniqueIPs,
});
}
}
Pattern 3: Password Spraying
10:00:01 - auth.login_failed - alice@example.com - 203.0.113.50
10:00:02 - auth.login_failed - bob@example.com - 203.0.113.50
10:00:03 - auth.login_failed - carol@example.com - 203.0.113.50
10:00:04 - auth.login_failed - dave@example.com - 203.0.113.50
10:00:05 - auth.login_failed - eve@example.com - 203.0.113.50
Indicators:
- Different target accounts
- Same source IP
- Common password attempts
Detection Rule:
const SPRAY_THRESHOLD = 5;
const WINDOW_SECONDS = 300;
async function detectPasswordSpray(event: SecurityEvent) {
const key = `spray:${event.ip_address}`;
await redis.sadd(key, event.actor_id);
await redis.expire(key, WINDOW_SECONDS);
const uniqueTargets = await redis.scard(key);
if (uniqueTargets >= SPRAY_THRESHOLD) {
await createAlert({
type: "password_spray",
severity: "high",
ip_address: event.ip_address,
unique_targets: uniqueTargets,
});
}
}
Threshold Tuning
Thresholds need to balance security with user experience:
| Attack Type | Recommended Threshold | Window | False Positive Risk |
|---|---|---|---|
| Single-target | 5 attempts | 1 min | Low |
| Distributed | 10 unique IPs | 5 min | Low |
| Password spray | 5 unique targets | 5 min | Medium |
Adjusting for Your Application
Consider these factors:
- User base size: Larger = higher thresholds
- Password complexity requirements: Stronger = lower thresholds
- MFA adoption: Higher = can be more lenient
- Industry: Financial/Healthcare = stricter
Response Strategies
Immediate Response
- Temporary Account Lock
async function lockAccount(actorId: string, duration: number) {
await redis.set(`lock:${actorId}`, "1", "EX", duration);
await notifyUser(actorId, "account_locked");
}
- IP Block
async function blockIP(ipAddress: string, duration: number) {
await redis.set(`block:${ipAddress}`, "1", "EX", duration);
}
- CAPTCHA Enforcement
async function requireCaptcha(actorId: string) {
await redis.set(`captcha:${actorId}`, "1", "EX", 3600);
}
Progressive Response
Implement escalating responses based on severity:
| Attempt Count | Response |
|---|---|
| 3 | Show CAPTCHA |
| 5 | Delay response by 5 seconds |
| 10 | Lock account for 15 minutes |
| 20 | Lock account for 1 hour |
| 50+ | Require password reset |
async function progressiveResponse(count: number, actorId: string) {
if (count >= 50) {
await forcePasswordReset(actorId);
} else if (count >= 20) {
await lockAccount(actorId, 3600);
} else if (count >= 10) {
await lockAccount(actorId, 900);
} else if (count >= 5) {
await delay(5000);
} else if (count >= 3) {
return { requireCaptcha: true };
}
}
Integration with LiteSOC
LiteSOC provides built-in brute force detection. Simply send your auth events:
await litesoc.track({
event: "auth.login_failed",
actor: { id: userId, email: userEmail },
metadata: { reason: "invalid_password" }
});
LiteSOC automatically:
- Detects all three attack patterns
- Correlates events across time windows
- Sends real-time alerts
- Provides forensic investigation tools
Best Practices Checklist
- Rate limit authentication endpoints
- Implement account lockout after N failures
- Use CAPTCHA after initial failures
- Monitor for distributed attacks (not just per-IP)
- Alert on successful login after failures
- Log all authentication attempts
- Require MFA for sensitive accounts
- Use secure password requirements
- Monitor for credential stuffing patterns
Conclusion
Brute force detection is about pattern recognition and appropriate response. By implementing multiple detection layers and progressive responses, you can protect your users without creating friction for legitimate access.
Want automated brute force detection? Try LiteSOC free and get real-time alerts when attacks happen.
Stay Updated
Get the latest security insights and product updates delivered to your inbox. No spam, unsubscribe anytime.