X Xerobit

Brute Force Password Attacks — How They Work and How to Defend Against Them

Brute force attacks try every possible password combination. Learn how attackers estimate crack times, why GPU speed matters, how bcrypt/Argon2 slow attacks, and what password...

Mian Ali Khalid · · 5 min read
Use the tool
Password Generator
Generate strong random passwords with configurable length, character classes, and exclusions. Real entropy meter, crack-time estimate, bulk mode.
Open Password Generator →

A brute force attack tries every possible password until it finds the right one. The only defense is entropy (randomness) and slow hashing — not complexity rules.

Generate high-entropy passwords with the Password Generator.

How brute force works

Attacker gets your password hash from a breached database.
They run hashing software against millions/billions of candidates per second.
When hash(candidate) == stored_hash → password found.

Types of brute force:
1. Pure brute force: try all combinations (a, b, ..., aa, ab, ...)
2. Dictionary attack: try common words and variations
3. Rule-based: apply transforms (password → P@ssw0rd, p4ssword, ...)
4. Rainbow tables: precomputed hash→password lookup (defeated by salting)

Cracking speeds (2024 hardware)

Algorithm           Hashes/second (RTX 4090)
─────────────────────────────────────────────
MD5                 68,000,000,000 (68 billion/sec)
SHA-1               25,000,000,000 (25 billion/sec)
SHA-256             12,000,000,000 (12 billion/sec)
bcrypt (cost=10)           184,000 (184 thousand/sec)
bcrypt (cost=12)            46,000 (46 thousand/sec)
Argon2id (default)           1,000 (1 thousand/sec)
scrypt (N=32768)             1,500 (1.5 thousand/sec)

bcrypt is 65,000× slower than SHA-256 per guess. This is by design.

Crack time estimates

function crackTime(passwordSpace, hashesPerSecond) {
  const averageGuesses = passwordSpace / 2;  // Find it halfway on average
  const seconds = averageGuesses / hashesPerSecond;
  
  if (seconds < 60) return `${seconds.toFixed(0)} seconds`;
  if (seconds < 3600) return `${(seconds/60).toFixed(0)} minutes`;
  if (seconds < 86400) return `${(seconds/3600).toFixed(1)} hours`;
  if (seconds < 31536000) return `${(seconds/86400).toFixed(0)} days`;
  return `${(seconds/31536000).toFixed(0)} years`;
}

// Password space = charset^length
const lower8 = 26**8;        // 208 billion
const mixed12 = 95**12;      // 540 quadrillion
const diceware5 = 7776**5;   // 28 octillion

// Against SHA-256 (12B/sec):
crackTime(lower8, 12e9);    // "9 seconds"
crackTime(mixed12, 12e9);   // "1,424 years"
crackTime(diceware5, 12e9); // "74 billion years"

// Against bcrypt cost=12 (46K/sec):
crackTime(lower8, 46000);    // "2.6 days"
crackTime(mixed12, 46000);   // "372 million years"

Why bcrypt defeats GPU attacks

GPU advantage: parallel computation
MD5/SHA: massively parallel on GPU (thousands of concurrent hash operations)
bcrypt: memory-hard, sequential operations → GPU offers minimal speedup

bcrypt internal loop: 2^cost iterations of Blowfish key schedule
cost=10: 2^10 = 1024 iterations
cost=12: 2^12 = 4096 iterations
cost=14: 2^14 = 16384 iterations (login ~300ms)

GPU can run many bcrypt instances in parallel,
but each instance takes the same wall-clock time.
Result: bcrypt at cost=12 ≈ 46,000/sec even on RTX 4090
vs SHA-256: 12,000,000,000/sec

260,000× slower per guess = effectively impossible for high-entropy passwords

Entropy thresholds

At bcrypt cost=12 (46,000 hashes/sec):
Entropy    Keyspace          Crack time
─────────────────────────────────────────
40 bits    1.1 trillion       7 hours
50 bits    1.1 quadrillion    290 days
60 bits    1.2 quintillion    810 years
70 bits    1.2 sextillion     830,000 years
80 bits    1.2 septillion     849 million years

Recommendation: 60+ bits of entropy minimum for bcrypt-protected passwords
5-word diceware: 64.6 bits ✓
Random 10-char (mixed): 65.7 bits ✓
Random 12-char (lowercase): 56.4 bits ✗ (borderline)

Dictionary attack defense

# Attackers try common passwords first:
# "password", "123456", "qwerty", "letmein" → cracked in <1 second regardless of hash

# Defense: reject common passwords at registration
import requests

def is_common_password(password: str) -> bool:
    """Check against Have I Been Pwned API (k-anonymity model)."""
    import hashlib
    sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
    prefix = sha1[:5]
    suffix = sha1[5:]
    
    response = requests.get(f'https://api.pwnedpasswords.com/range/{prefix}')
    for line in response.text.splitlines():
        hash_suffix, count = line.split(':')
        if hash_suffix == suffix:
            return True  # Found in breach database
    return False

# At registration:
if is_common_password(password):
    raise ValueError("This password has appeared in data breaches. Choose another.")

Rate limiting defense (online attacks)

Brute force against a live login form (online attack) is slower than offline hash cracking:

// Express: rate limit login attempts
import rateLimit from 'express-rate-limit';  // npm install express-rate-limit

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 10,                     // 10 attempts per window per IP
  skipSuccessfulRequests: true,
  message: { error: 'Too many login attempts. Try again in 15 minutes.' },
});

app.post('/login', loginLimiter, async (req, res) => {
  // ... authentication logic
});

// Per-account lockout (prevents distributed attacks):
async function checkAccountLockout(email) {
  const attempts = await redis.incr(`login:fail:${email}`);
  if (attempts === 1) await redis.expire(`login:fail:${email}`, 900); // 15min TTL
  
  if (attempts > 5) {
    throw new Error('Account locked due to too many failed attempts.');
  }
}

Credential stuffing (not pure brute force)

Credential stuffing ≠ brute force
Attacker uses known email+password pairs from OTHER breaches.
Defense: different password on every site.

Tools defenders use:
- Have I Been Pwned API: check if email/password appears in breaches
- Device fingerprinting: detect bot-like login patterns
- CAPTCHA on failed login threshold
- Magic link / passkey (no password = no credential stuffing)

Related posts

Related tool

Password Generator

Generate strong random passwords with configurable length, character classes, and exclusions. Real entropy meter, crack-time estimate, bulk mode.

Written by Mian Ali Khalid. Part of the Encoding & Crypto pillar.