Insecure Password Hashing Algorithms Still in Production Use

Legacy password code survives hidden in old systems until a breach exposes the cost of inaction.

· 8 min read
Cover illustration for “Insecure Password Hashing Algorithms Still in Production Use”
Cryptographic Failures · October 28, 2026 · 8 min read · 1,865 words

Insecure password hashing stays in production because no one reviews the old code, even though better algorithms are well known. Replacing MD5 or SHA-1 means more than upgrading the main login flow. It means finding every path that handles passwords, closing the weak ones, and keeping them from coming back.

Why MD5 and SHA-1 are still in production despite being broken for decades

A senior engineer opens a legacy codebase and finds MD5 in the password code. The login page works. Nothing in the user experience points to a problem, and the code may have gone untouched for years. MD5 has been known to be insecure since 2004 and SHA-1 since 2005, yet people who work with password hashing still often find MD5 in production in 2026.

The hiding places are usually unglamorous. They include legacy internal applications, old CMS plugins, custom WordPress code, network devices, and embedded systems. Authentication code written before today's security norms can stay in use because it still does the job people see. A working login rarely prompts a sprint to inspect the machinery underneath it. That makes this a technical debt problem, with security consequences that stay hidden until someone obtains the stored hashes.

Why throughput, not collision resistance, is the actual danger for password hashing

Calling MD5 “broken” can point engineers toward the wrong threat. Collision resistance protects uses such as digital signatures and integrity verification, where security fails if two different inputs produce the same hash. Password cracking poses a different question. How cheaply can an attacker test a guess against a stolen hash? MD5 and SHA-1 were designed to run fast, which is exactly the wrong property here.

Three attack classes explain what password storage needs to resist. Rainbow tables are precomputed lookup tables, and a separate salt for each user defeats that shortcut. Brute force tries candidate passwords exhaustively, so a slow key derivation function, or KDF, makes each attempt more expensive. Offline cracking happens after a database breach, when the attacker can run guesses without the application's limits. A slow KDF and per-user salts, optionally backed by an application-layer pepper, address that threat together.

A salt stops precomputed lookups while leaving an attacker able to calculate MD5 guesses for each user individually. Common passwords are still cheap to crack because each guess costs so little to calculate. SHA-256 alone is also unfit for password storage, even though it has no known collision weakness.

What breach data actually shows when MD5-hashed passwords leak

Once MD5 hashes leave the database, cracking becomes a throughput problem. Modern hardware makes the guessing window short, and common passwords can fall within minutes. The attacker does not need to defeat the login page, trigger its controls, or wait for the application to respond. The stolen hashes provide the comparison target. That is why a database breach can turn into plaintext password exposure so quickly.

The 2019 Collection #1 credential-stuffing database showed the downstream scale of that exposure. It contained hundreds of millions of unique email addresses and over 21 million unique passwords, many cracked from MD5 and SHA-1 hashes in earlier breaches. Those earlier failures did not stay isolated. Once cracked, the passwords were used to try to break into unrelated systems. A weak hash therefore creates risk beyond the service that stored it.

Cybernews researchers discovered dozens of datasets containing billions of login records in June 2025, including URLs, usernames, and plaintext passwords. That finding leaves open which hashing algorithm was used for each exposed password. It does show the scale of the pool into which stolen credentials can flow. Fast password hashing feeds that pool by making recovery cheap, while credential stuffing gives recovered passwords another use.

How the Ashley Madison case shows that switching to a strong algorithm doesn't close the gap if the old code path stays alive

Ashley Madison's developers chose bcrypt at cost factor 12, a sound password-hashing choice. The failure was in a parallel path. Before June 2012, a login key variable was generated from users' plaintext passwords through salted MD5. When bcrypt was implemented, the developers did not regenerate those variables for existing users. Accounts created before that date thus still had a fast route for cracking despite the stronger password hash.

The attacker did not have to take the expensive route. The surviving MD5-derived value undermined the protection bcrypt was supposed to provide. This is the technical debt pattern in its most instructive form, because the main algorithm choice was not the mistake. The migration left password-derived material behind. An audit focused only on the main password column could miss the weaker representation.

Inspect every path that processes password material, including the current verifier. A test suite can show that the active login flow uses bcrypt without checking the legacy login keys still in the database. Whitebox review exposes that gap because the reviewer can follow the code and its stored values. Testing only the externally visible login behavior cannot provide the same assurance.

The algorithm hierarchy engineers should be working from in 2026

Argon2id is the best choice when the compliance boundary allows it because the memory it needs makes guessing on a large scale expensive. Each guess needs a lot of RAM as well as a calculation. OWASP's minimum is 19 MiB, and typical production settings range from 19–64 MiB. That is manageable for a server hashing a password at login. Across many parallel guesses on a GPU farm, however, memory becomes an economic bottleneck.

That does not make every alternative unsafe. scrypt is memory-hard and acceptable, though its design is slightly older than Argon2. Bcrypt also remains a defensible choice, as the sound cost factor in the Ashley Madison case illustrates. Prioritizing the removal of fast hashes and forgotten code paths over replacing an otherwise sound bcrypt implementation is not the same as ignoring security.

Bcrypt silently truncates passwords longer than 72 bytes. If the application allows longer passwords, the library must pre-hash with SHA-256 or SHA-512 before passing them to bcrypt. That use of a fast hash is different from storing SHA-256 alone as the password verifier, because bcrypt still supplies the expensive step.

PBKDF2-SHA256 at a sufficiently high iteration count fills a different role. Of these four options, it is the only FIPS 140-2 and FIPS 140-3 approved algorithm, making it the appropriate choice within relevant FedRAMP or DoD compliance boundaries. It is weaker against GPU attacks than memory-hard alternatives because each iteration is a cheap SHA call. MD5, SHA-1, SHA-256 alone, PBKDF2 below current OWASP/NIST iteration baselines, and custom schemes fall outside the acceptable set.

What compliance frameworks now require, and why most codebases haven't caught up

NIST and PCI DSS have moved password hashing from general guidance into requirements. That raises the stakes for code that has survived simply because nobody revisited it. A working authentication system is not evidence of compliant password storage. The gap is between what the standard requires and what the deployed implementation actually does. A team can overlook forgotten hashing code that an audit flags.

For SaaS companies pursuing SOC 2, HIPAA, or ISO 27001, the practical issue is evidence. A pen test report that checks how the hashing code works can help show an auditor that passwords are stored securely. But the test needs access to the code to establish what is running. A report based only on responses from the login endpoint cannot establish the algorithm, its parameters, or the absence of a weaker parallel path. The scope of the test determines the strength of that evidence.

Where broken hashing hides in a real codebase and how to find it

Start with a repository-wide search for md5(, sha1(, hashlib.md5, hashlib.sha1, MessageDigest.getInstance("MD5"), and createHash('md5'). A string search often reveals calls that unit tests never reach. Inspect matches in authentication-adjacent modules as well as the main authentication service. Legacy modules, migration scripts, and third-party plugins need a check because they may have their own code to handle credentials. The search finds candidates, while reviewing how each value is used establishes the risk.

Follow password material beyond the primary verifier. Password reset flows, API key generation, session token derivation, and OAuth state parameters all belong in the review. CMS plugins and third-party integrations may also maintain their own credential storage outside the central service. The Ashley Madison pattern gives this search a specific target. Look for a session token, login key, or “remember me” cookie generated using the password as an input, even when the main password column already uses a strong algorithm.

Database inspection provides another useful lead. A short, fixed-length hexadecimal value in a user table's hash column suggests MD5 or SHA-1, while a longer value with a bcrypt prefix identifies bcrypt. Treat the format as a clue to follow into the implementation. Even with a primary hash that is easy to recognize, another password-derived value may be stored elsewhere. The review needs to connect stored representations with the code that creates and checks them.

A tester observing the login endpoint can confirm that authentication works but cannot determine the stored hashing algorithm from that behavior alone. Repository access lets a reviewer map password handling in migration utilities, background jobs, and legacy paths that the external API does not expose. OWASP ASVS provides testable checkpoints for that work. ASVS V6 covers authentication requirements, including password hashing, while V11 addresses broader cryptographic algorithm choices.

How to migrate away from broken hashing without locking out existing users

Use a login-time rehash to move from MD5 or SHA-1 to Argon2id without forcing an immediate password reset. When a user successfully authenticates through the old verifier, the supplied plaintext password can be hashed with the new algorithm. Store old and new hashes temporarily during the transition, but stop consulting the old hash once the new one exists. Do not attempt to convert the old hash directly. Argon2id needs the plaintext password, so migration requires user participation.

Accounts that never log in need a separate decision. Track the percentage still using the old algorithm and decide whether to require a reset after a defined transition period. The same login-time pattern applies when raising a bcrypt cost factor below 12. Because the stored bcrypt hash is self-describing, the verifier can detect the old cost and upgrade it on the next login. In FIPS environments, confirm the compliance boundary first and replace Argon2id with PBKDF2-SHA256 at the current NIST-required iteration count.

The comparison code needs attention too. PHP's === and other ordinary string comparisons that work the same way can stop at the first byte that differs, so timing differences over repeated requests can give away information about the correct hash. Use a constant-time comparison function regardless of the hashing algorithm. Choosing an expensive KDF does not fix a leaking comparison.

Record the algorithm, version, and parameters with the deployment so future changes have a known baseline. The name Argon2id alone leaves out how much work each guess needs and when those settings were chosen. After migration, keep checking for new weak paths. A dependency, vendor integration, or new authentication flow can reintroduce the same problem. Scanning pull requests for MD5 and SHA-1 usage keeps that review going, before forgotten code becomes production debt again.

More in Cryptographic Failures