Back to all articles
Security

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.

Amirol AhmadAmirol Ahmad
March 5, 2026
5 min read
Share on X
Brute Force Detection: Patterns, Thresholds, and Response Strategies

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 TypeRecommended ThresholdWindowFalse Positive Risk
Single-target5 attempts1 minLow
Distributed10 unique IPs5 minLow
Password spray5 unique targets5 minMedium

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

  1. Temporary Account Lock
async function lockAccount(actorId: string, duration: number) {
  await redis.set(`lock:${actorId}`, "1", "EX", duration);
  await notifyUser(actorId, "account_locked");
}
  1. IP Block
async function blockIP(ipAddress: string, duration: number) {
  await redis.set(`block:${ipAddress}`, "1", "EX", duration);
}
  1. CAPTCHA Enforcement
async function requireCaptcha(actorId: string) {
  await redis.set(`captcha:${actorId}`, "1", "EX", 3600);
}

Progressive Response

Implement escalating responses based on severity:

Attempt CountResponse
3Show CAPTCHA
5Delay response by 5 seconds
10Lock account for 15 minutes
20Lock 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.