Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

Passwords, Authentication, and Why We’re Still Getting It Wrong

A man sits at a typewriter, thinking about security, keys, and a jumble of letters. A shadowy man lurks in the background.

There is a sentence that appears, in various forms, in the postmortem of almost every major account compromise of the last decade: the attacker used valid credentials.

Not a zero-day exploit. Not a sophisticated piece of malware. Valid credentials. A username and a password that worked, obtained from somewhere the victim didn’t know about, used to walk straight through the front door.

Authentication is the part of security that touches every human being who has ever used a computer. It is also the part that has failed most consistently, most publicly, and most expensively. Not because the underlying problems are unsolved — many of them are — but because the solutions require humans to behave differently, and humans are reliably bad at that.

This article is about why passwords are terrible, what we’ve tried to do about it, where those attempts have succeeded and failed, and where things are actually heading.


Something You Know

Authentication factors are traditionally divided into three categories: something you know, something you have, and something you are. Passwords are something you know. They have been the dominant authentication mechanism since the 1960s, when the Compatible Time-Sharing System at MIT first implemented them, and they have been a source of grief ever since.

The fundamental problem with passwords is that they exist at the intersection of two irreconcilable requirements. Security demands that passwords be long, random, unique per service, and never written down. Human memory demands the opposite of all of these things. The compromise most people reach — a short, memorable word, reused across multiple services, with a number on the end — satisfies neither requirement fully and creates a vulnerability that attackers have been exploiting systematically for decades.

Before we get to the human factors, though, there’s a prior question: even if users chose perfect passwords, how are those passwords stored? The answer, for a depressing number of services, has often been very, very badly.

How Password Storage Works (And How It Goes Wrong)

When you create an account with a password, a well-designed system doesn’t store the password itself. It stores a hash — the output of a one-way mathematical function applied to your password. When you log in, the system hashes what you typed and compares it to the stored hash. If they match, you’re in. The original password is never stored and, in theory, cannot be recovered from the hash.

The problem is that not all hash functions are equal, and the gap between a good and bad choice is catastrophic.

Early systems — and many later ones that should have known better — used general-purpose cryptographic hash functions like MD5 or SHA-1 to hash passwords. These functions are designed to be fast. On modern hardware, an attacker can compute billions of MD5 hashes per second. If you obtain a database of MD5-hashed passwords, you can run a dictionary attack — hashing every word, common substitution, and known password pattern — and crack a significant proportion of them in hours.

The LinkedIn breach of 2012 is the canonical illustration. LinkedIn stored approximately 6.5 million password hashes using unsalted SHA-1. When the database was leaked, crackers got to work. Within days, the majority had been cracked. What the cracked hashes revealed was instructive: enormous numbers of people had used “linkedin” as their password, or variations of it. “password”, “123456”, “abc123” appeared in vast quantities. The breach didn’t just expose credentials — it provided a detailed map of how humans actually choose passwords when left to their own devices.

Salting: The Obvious Fix That Wasn’t Universal

Salting is the practice of adding a unique random value — the salt — to each password before hashing it. The salt is stored alongside the hash. When the user logs in, the system retrieves their salt, appends it to the password they typed, hashes the result, and compares.

The effect is significant: even if two users have the same password, their hashes will be different, because their salts are different. This defeats precomputed rainbow table attacks — massive lookup tables mapping known inputs to their hash outputs — and forces an attacker who obtains a password database to crack each hash individually.

Salting is not complicated. It has been understood since the 1970s. LinkedIn, in 2012, still wasn’t doing it.

But salting alone isn’t sufficient if you use a fast hash function. The correct answer is a hash function specifically designed to be slow: bcrypt, scrypt, or Argon2. These functions are deliberately computationally expensive — you can tune the cost factor so that hashing a single password takes, say, 100 milliseconds on your server. That’s imperceptible to a legitimate user logging in. For an attacker trying to crack millions of hashes, it’s the difference between billions of guesses per second and thousands.

Argon2 won the Password Hashing Competition in 2015 and is the current recommended default. If you’re storing passwords and not using Argon2 (or at minimum bcrypt), you’re doing it wrong.

The Breach Ecosystem

Here is a number worth sitting with: HaveIBeenPwned, Troy Hunt’s database of credentials exposed in public breaches, contains over 17 billion compromised accounts as of 2026. Seventeen billion. The actual number of unique individuals affected is smaller — most people appear in multiple breaches — but the scale gives you a sense of how much credential data is in circulation.

This data doesn’t sit idle. It feeds credential stuffing: automated attacks that take credentials from one breach and try them against other services. If you used the same password for LinkedIn in 2012 and your email account, your email account was potentially compromised the moment the LinkedIn data was dumped, even if your email provider’s security was perfect. The breach you don’t know about, at a service you’d forgotten you’d signed up for, becomes the vector for the account you care about.

The Have I Been Pwned API is publicly accessible and widely used by services to check whether a user’s proposed password has appeared in known breaches. NIST now recommends this check as part of password policy. If you’re building something that handles authentication, it’s a reasonable addition.

The traditional password advice — minimum length, uppercase, number, special character — has also been substantially revised. NIST’s 2017 guidelines dropped complexity requirements in favour of length, and advised against forcing regular password rotation unless there’s evidence of compromise. The reasoning: forced complexity produces predictable patterns (”Summer2024!”), and forced rotation produces incremental variants (”Summer2024!” → “Autumn2024!”). Length is a much better proxy for entropy than complexity theatre.

Password Managers: The Actual Solution

The only technically sound response to the credential stuffing problem is unique, random, long passwords for every service. The only practical way to do that without a photographic memory is a password manager.

Password managers generate and store credentials, typically encrypted with a master password and/or biometric, synced across devices via the provider’s infrastructure. The threat model shifts: instead of having weak credentials everywhere, you have one very strong master credential protecting everything. This is either better or worse depending on your perspective — it concentrates risk onto a single point, but that point is specifically designed to be defended.

Adoption remains stubbornly incomplete. Password managers require behavioural change, an upfront time investment to migrate existing credentials, and a degree of trust in the provider. The 2022 LastPass breach — where attackers obtained encrypted password vaults — illustrated that the provider trust is not unconditional. The vaults were encrypted, but users with weak master passwords were at risk. The breach also revealed that metadata such as URLs and usernames were stored unencrypted, which is a meaningful intelligence exposure even without cracking the vault contents.

Bitwarden (open source, self-hostable) and 1Password are the current recommendations for most users. The built-in managers in browsers and operating systems have improved substantially and are defensible choices for non-technical users who won’t use a dedicated manager.

The Second Factor

Something you know has an inherent weakness: it can be copied. If an attacker obtains your password — through a breach, phishing, shoulder surfing, or a keylogger — they have everything they need. Multi-factor authentication (MFA) addresses this by requiring a second proof of identity, from a different category.

The second factor can be something you have (a device, a token) or something you are (a biometric). The theory is that even if an attacker has your password, they don’t have your phone.

TOTP (Time-based One-Time Passwords), implemented in apps like Google Authenticator and Authy, generate a six-digit code that changes every 30 seconds. The code is derived from a shared secret and the current time. An attacker who intercepts your password in transit also needs to intercept the current TOTP code within its 30-second validity window — significantly harder than just stealing a static password.

TOTP is better than no second factor. It is not, however, phishing-resistant. A sophisticated phishing attack can present a fake login page that relays credentials and TOTP codes to the real site in real time, completing the authentication before the code expires. This has been done, repeatedly, at scale.

SMS-based 2FA is worse still. The code is delivered via the phone network, which is vulnerable to SIM swapping: an attacker social-engineers your mobile carrier into transferring your number to a SIM they control. Suddenly the “something you have” is something they have. SIM swap attacks have been used to compromise cryptocurrency accounts, email accounts, and social media accounts with notable regularity. If SMS 2FA is your only option, it’s better than nothing — but it’s a weak second factor, and anyone with significant assets at stake should avoid it as a primary mechanism.

The Twitter Hack and the Limits of Technical Controls

In July 2020, attackers gained access to Twitter’s internal admin tools and used them to take over high-profile accounts — Barack Obama, Joe Biden, Elon Musk, Apple, and others — to run a Bitcoin scam. The accounts had multi-factor authentication. The technical controls were not the failure point.

The attack was pure social engineering: the attackers called Twitter employees, impersonated IT support, and talked their way into credentials for the internal tooling. No vulnerability was exploited. No zero-day was used. A person with a phone and a convincing story walked past every technical control Twitter had in place.

This is the recurring theme in high-profile breaches: technical controls create a perimeter; humans inside the perimeter remain a vector. An attacker who can convince one employee to help them doesn’t need to break anything. Kevin Mitnick, the social engineer who became the most wanted computer criminal in the US in the 1990s, consistently maintained that the human layer was easier to exploit than the technical one. Decades later, nothing has changed.

The operational implication is that authentication systems need to account for the insider and social engineering vectors, not just credential theft. Privileged access management, just-in-time access, and comprehensive audit logging are the tools here — limiting what any single compromised identity can do, and ensuring that unusual access patterns are visible.

Hardware Tokens and the Phishing Problem

The answer to phishing-resistant authentication is to bind the second factor to a specific origin — the domain of the site you’re logging into — so that a fake site can’t collect a valid response.

FIDO U2F (Universal 2nd Factor), now superseded by FIDO2, does exactly this. A hardware token — a YubiKey being the most common, but certainly not the only one, nor the cheapest — uses public key cryptography to sign a challenge from the server. The signature includes the origin (the domain), so a response generated for a phishing site is cryptographically invalid on the real site. An attacker who intercepts the authentication cannot replay it elsewhere.

Hardware tokens are robust. They’re also not free, require physical possession, and create a support burden when users lose them. They’re standard in high-security environments and increasingly offered as an option by consumer services, but they haven’t achieved mainstream adoption.

Push notification MFA — where you approve a login via a prompt on your phone — has become widespread as a more convenient alternative. The two main weaknesses are network connectivity requirements and MFA fatigue. An attacker who has your password floods you with approval requests at 3am until you accidentally approve one, or approve it just to make the notifications stop. This is not theoretical — it was the technique used in the Uber breach of 2022, combined with a WhatsApp message to the target pretending to be IT support explaining why the approvals were being sent. The attacker got in.

The lesson from MFA fatigue is that the approval prompt needs to carry enough context that the user can make an informed decision: what service, from what location, at what time. “Approve login from Lagos at 3:17am” is harder to accidentally approve than a generic notification.

FIDO2, WebAuthn, and Passkeys

The industry’s current answer to the authentication problem is passkeys, built on the FIDO2 standard and the WebAuthn browser API.

The mechanics: when you register with a service, your device generates a public/private key pair. The public key goes to the service; the private key never leaves your device. When you authenticate, the service sends a challenge; your device signs it with the private key, proving possession without transmitting any secret. The signing is authorised by whatever unlock mechanism your device uses — biometric, PIN.

The properties this provides: no password to steal, no shared secret to phish, origin-bound so phishing sites get nothing useful, and biometric on the device replaces the “something you know” with “something you have plus something you are.”

Synced passkeys — where the private key is synced across your devices via iCloud Keychain, Google Password Manager, or a third-party manager — extend this to the “I got a new phone” problem. The trade-off is that syncing a private key means the key is as secure as the sync provider. Some security practitioners are uncomfortable with this; for most users, the security improvement over reused passwords is so dramatic that the trade-off is clearly worth it.

Passkeys are now supported by Apple, Google, and Microsoft, and by a growing list of services. The transition is happening; it’s just slow. Legacy services with existing password-based user bases face significant migration complexity, and many services have implemented passkeys as an option rather than a replacement, meaning users who care about security opt in while the majority continue with passwords, and as we all are aware, a chain is only as strong as its weakest link.

The exit ramp exists. We’re just not all on it yet.

Single Sign-On: Consolidating the Problem

SSO (Single Sign-On) — “Login with Google”, “Login with Apple”, OAuth, SAML — is an attempt to centralise authentication at a provider who can invest heavily in security, rather than having every service implement it independently.

The security argument is reasonable: Google has better authentication security than most small services, so delegating to Google reduces the overall attack surface. You also reduce the number of credentials to manage.

The risk is concentration. Your Google account becomes the master key to everything that uses “Login with Google.” If it’s compromised, everything is. This makes the security of the SSO provider’s account — and particularly the recovery mechanisms — critically important. It also creates an availability dependency: when the SSO provider has an outage, every service that delegates to them is inaccessible.

The privacy implication is also worth noting: the SSO provider can observe which services you authenticate to, and when. “Login with Apple” specifically addresses this by offering to generate a unique relay email address per service, limiting the provider’s visibility. Most OAuth implementations are less careful.

For enterprise environments, SSO combined with strong MFA at the identity provider is the current best practice — it centralises enforcement of authentication policy, makes provisioning and deprovisioning tractable, and provides a single audit log of authentication events. For personal use, it’s a reasonable trade-off provided the central account is properly secured.

Zero Trust: Acknowledging the Perimeter Is Dead

Traditional network security assumed a hard perimeter: the internal network was trusted, the external network wasn’t. Authenticate once to get inside the perimeter and you could move freely.

The perimeter has been dissolving for two decades — remote work, cloud infrastructure, SaaS, mobile devices — and the assumption of implicit trust inside the network has become untenable. Zero trust is the architectural response: no implicit trust based on network location, every access request authenticated and authorised, least privilege enforced, everything logged.

“Never trust, always verify” is the slogan. In practice it means: every service-to-service call is authenticated (mTLS, as discussed in Article 2); user identity is verified continuously rather than just at login; device posture is checked (is this a managed device? Is it patched?); access is granted just-in-time for specific resources rather than broadly.

Zero trust is an architecture, not a product, despite what every vendor selling “zero trust solutions” would like you to believe. It’s also not a binary state — it’s a direction of travel. Most organisations are somewhere on the journey, with some segments of their infrastructure properly isolated and others still running on implicit network trust.

The authentication layer is the foundation. You cannot do zero trust without strong, phishing-resistant authentication. Passkeys and hardware tokens at the user layer, mTLS at the service layer, backed by a robust identity provider — that’s the stack and a key principle of security; defence-in-depth.

The Human Layer Remains the Hard Problem

Authentication has improved substantially in the last decade. Hardware tokens, passkeys, and phishing-resistant MFA represent genuine advances. The tooling exists to do this properly.

And yet the Twitter attack was a phone call. The Uber attack was a WhatsApp message. The most sophisticated authentication infrastructure in the world doesn’t help if you can convince the right person to hand over access directly.

This is the part that doesn’t have a clean technical solution. It requires security culture, training, scepticism about unsolicited contact, and processes designed with the assumption that social engineering will be attempted. It requires treating “someone from IT called me” with the same suspicion as an unexpected email with an attachment.

It also requires accepting that the human failure mode is not a bug to be patched but a permanent feature of systems that humans operate. The goal isn’t to eliminate it — that’s really not achievable — but to limit the blast radius when it happens. Privileged access management, just-in-time permissions, comprehensive audit logging, and rapid revocation capabilities all serve this purpose. When the social engineer gets in, they should find themselves in a limited environment with a short clock.

The maths, again, is the easy part. The hard part is the humans — both the ones using the system and the ones trying to subvert it.

Technically, it doesn’t have to be 30 seconds, is also doesn’t have to be 6 digits. The algorithm as deployed only defaults to these values.

Yeah, I still call it Twitter. I still think of X as being an unknown that I need to find. Also, how do you pronounce it? My favourite is Xitter with the X pronounced as an Sh… but maybe that is just my immature side.

Not the only risk though. As a developer it can be painful to test things that use OAuth from things like playwright. Google for example refuses to authenticate as your browser is ‘not secure’ — which is kinda the point. I actually want to peer inside the page to debug it… :(

Like an armadillo… “Crunchy on the outside, smooth on the inside”

As the phrase goes “Make something idiot-proof and the world evolves a better idiot”