Tries everything
Every possible combination of characters, one after another, until one works. The only thing that slows it down is how many combinations there are, so length and complexity are what matter.
Is “Password1!” a strong password? A GPU and I found out.
My research question: how do password length and character complexity affect resistance to brute-force and dictionary attacks? To answer it, I cracked 70 MD5-hashed passwords with hashcat, from random strings to ones built around a made-up person's life.
Every sign-up form gives the same advice: make it long, and mix in an uppercase letter, a number, and a symbol. “Password1!” does all of that. It's also exactly the kind of password people make to get past those rules, and it shows up more than 580,000 times in known data breaches. An attacker would get it almost instantly.
That gap, between a password that follows the rules and one that's actually hard to guess, is what this essay is about. And passwords aren't going anywhere: they're still the most common way we log in to things.
The catch is that “strong” depends on who's attacking. There are two main ways to crack a password, and they care about completely different things.
Every possible combination of characters, one after another, until one works. The only thing that slows it down is how many combinations there are, so length and complexity are what matter.
A list of guesses: common words, leaked passwords, things like “123456”. Here a longer or more complex password isn't automatically stronger. Predictability is what matters.
My answer: length and complexity make brute force exponentially harder, but barely slow a dictionary attack down. Only a password that's long and genuinely unpredictable survives both.
Websites (the good ones) never store your password. They store its hash. So to crack a password, an attacker guesses one, hashes it, and checks whether it matches. Then does that again. And again. Billions of times a second.
Nothing you type leaves this page. Change one letter and the whole hash changes. The other way doesn't work at all: there's no turning a hash back into the password. That's why attackers have to guess.
MD5 was made by Ronald Rivest in 1991, and it was built to be fast. A modern GPU can do over 200 billion MD5 hashes a second. That's great for checking a file, and terrible for passwords: the faster an attacker can hash, the faster they can guess.
Real systems use deliberately slow algorithms like bcrypt or Argon2. I used MD5 on purpose, because its speed spans the whole range, from instant to effectively impossible, all within something I could actually measure.
The keyspace is every password that could exist for a given length and set of characters: charsetlength. Brute force has to search it. On average it finds the password halfway through.
Adding a character multiplies the keyspace by the whole character set. Adding character types only gives each position a few more options. Below, starting from 2 lowercase: double the options for each spot, or add one more spot. Length wins.
In 2009, the RockYou breach leaked over 32 million passwords in plain text. They became rockyou.txt, which is now the default list for generic dictionary attacks, because it's easy to get and full of real habits.
A targeted attack builds a small list about one person (name, pet, birthday) and runs it through mangling rules that turn words into password-shaped guesses: “password” → “Password1”, “p@ssword”. Free tools like CUPP will even build the list for you.
Pick a character set and a length. This uses the speed I actually measured, 5.5 billion guesses a second, and the average case: half the keyspace.
Two sets of passwords, one for each attack. A dictionary attack would crack almost none of a random set, and real human passwords can't show what length and complexity do on their own. So I split it.
# brute force, pinned to the main GPU > hashcat -m 0 -a 3 -d 2 setA.txt ?a?a?a?a?a?a # generic dictionary attack > hashcat -m 0 -a 0 -d 2 setB.txt rockyou.txt # targeted: 13 facts, expanded by rules > hashcat -m 0 -a 0 -d 2 setB.txt benji.txt \ -r OneRuleToRuleThemStill.rule
-d 2 so the slow integrated graphics stayed out of it-m 0)-a 3 brute force, -a 0 dictionary, plus a rule filesecrets, not random: random is built for simulations, and could place passwords in predictable parts of the keyspaceA grid of 5 lengths (6 to 10 characters) by 4 character sets, with 3 passwords in each of the 20 cells. Three per cell, because a single password could land at the very start or end of the keyspace and skew the time.
Measured: crack time. Whether it cracks isn't a question; brute force always gets there eventually.
Seven of the most common real passwords, from NordPass's list, and three built from a fictional person's life.
Measured: cracked or not.
Every detail is made up. It's the kind of thing a real person's social media gives away.
To test a targeted attack, I invented a person. From his “public” details I built three passwords, the way people actually make them:
Steelers24team + birth dateLoki2002!pet + birth year + symbolBenji0424Pghnickname + birthday + hometownThen I played the attacker. The base wordlist was just 13 raw facts, with nothing pre-mangled:
hashcat expanded them with OneRuleToRuleThemStill, a free, public rule file built from testing against real breached passwords. Nothing an actual attacker couldn't get.
hashcat's own benchmark said my GPU did 70.6 billion hashes a second. In every real run, from tiny keyspaces to huge ones, it held a steady 5.5. Not a warm-up thing either: it stayed there on runs over 10 minutes long. So I used 5.5 for every calculation, which made the real times about 13 times longer than the benchmark predicted.
Two alphanumeric 7-character passwords: one sat 6.7% of the way into the keyspace and cracked in 43 seconds, the other sat 94.8% in and took 612. That's why every cell had three passwords, averaged.
9 cells were small enough to crack for real. The other 11 would have taken up to years, so I calculated them from their keyspace. The biggest, 9410 = 5.4 × 1019, overflowed hashcat's 64-bit counter and threw an error before it started.
I double-checked hashcat's clock with PowerShell's Measure-Command: after taking off a 3.4-second startup, the two agreed within 0.2 seconds.
From 0.044 seconds to 155 years. Same GPU, same tool. The only differences were 4 more characters and 68 more possible symbols.
266 ≈ 309 million combinations. Measured: the average of three real cracks.
Pick any cell. Every step right or down multiplies the time, and the two stack: 6 lowercase to 7 mixed case is a 1,316× jump; 8 alphanumeric to 9 full is 2,625×.
The same 10 hashes, two dictionary attacks.
| Password | Type | Genericrockyou.txt | Targeted13 facts + rules |
|---|---|---|---|
| 123456 | Common | Cracked | Cracked |
| 12345678 | Common | Cracked | Cracked |
| password | Common | Cracked | Cracked |
| 111111 | Common | Cracked | Cracked |
| qwerty | Common | Cracked | Cracked |
| iloveyou | Common | Cracked | Cracked |
| abc123 | Common | Cracked | Missed |
| Steelers24 | Persona | Missed | Cracked |
| Loki2002! | Persona | Missed | Cracked |
| Benji0424Pgh | Persona | Missed | Missed |
| Total | 7 / 10 | 8 / 10 |
The 14-million-entry list missed every persona password. Thirteen facts about one person found two of them.
Any strength meter would call it strong. It meets every rule “Password1!” does. It survived the huge generic list, and then fell in seconds to a list of 13 facts.
lokia fact: his dog's nameLokirule: capitalizeLoki2002rule: append another fact, his birth yearLoki2002!rule: add a symbolIt wasn't luck. The rule file was built from real breached passwords, which means it's built from human habits. Word, then personal number, then symbol: that shape is so common it's literally written into the rules. This is called pseudo-complexity: a password that looks complex, and so passes as strong, but is still weak to a small targeted list.
Computer science has a way to measure this: entropy, the number of guesses an attacker needs, in bits (each bit doubles it). A random 9-character password using every character type has about 59 bits, from E = L × log2(C). “Loki2002!” has the same apparent entropy. But an attacker doesn't have to try everything, only the predictable things, so its effective entropy is far lower.
Same length, same character types. Random structure is the difference between seconds and more than a year. “Steelers24” is the same story: a random 10-character alphanumeric password would take 2.42 years.
exploits small keyspaces
beaten by a large keyspace
exploits common passwords
beaten by a unique one
exploits your public info
beaten by a password it can't be built from
A password that's strong against one attacker can be weak against another. Only a long, random password has no structure for any of them to grab.
Here's the real problem: people can't remember unpredictable passwords. What makes a password strong is exactly what our memory is bad at, so we reach for something memorable, which means something predictable. The fix is to stop relying on memory.
crypto.getRandomValues, the same idea as Python's secrets.
Don't look at “Password1!” and think it's strong. Use a password manager, and rest easy.
Length and complexity make a password exponentially harder to brute-force. They do almost nothing if the password is predictable.
This page is the fun version. The full essay has every method detail, table, and citation.
What actually makes a password strong is unpredictability. Predictability is vulnerability.