CWE-836 describes a login design where the product stores password hashes, receives a hash from the client, and compares it with the stored one. MITRE marks it as not language specific.
How it happens
Some designs have the client hash the password, to cut server load or avoid sending the password over the network. The catch: the hash itself becomes the credential. An attacker who gets a copy, through SQL injection or an information exposure, can replay it using a modified client, never learning the password. MITRE's conclusion is blunt: the server side comparison against a client side hash is no more secure than using plain passwords without hashing.
# Vulnerable: the client computes the hash, the server just compares
def login(user, client_hash):
row = db.get(user)
return row.pwd_hash == client_hash # stolen hash works as a password
MITRE's examples: CVE-2009-1283 authenticated with user supplied password hashes that could be obtained through a separate SQL injection flaw (CVE-2009-1282), and CVE-2005-3435 let attackers bypass authentication by supplying another user's password hash.
What an attacker gains
Bypass of the authentication routine, with the ability to gain privileges or assume another identity, without knowing the original password.
How to find it
The record lists no detection methods. Read the login code: does the server receive a secret and hash it itself, or accept something already hashed? Capture a login request in a proxy and see whether the value sent matches what is in your database.
How to fix it
The record gives no formal mitigations. Standard practice:
- Have the server hash the submitted password itself, with a slow, salted password hashing function such as bcrypt, scrypt or Argon2.
- Send credentials only over TLS.
- Treat stored hashes as sensitive data and protect the database against injection.
- If you must avoid sending passwords, use a challenge response protocol designed for it, with a fresh server nonce each time.
import bcrypt
def login(user, password):
row = db.get(user)
return bcrypt.checkpw(password.encode(), row.pwd_hash)
Quick check
Why does a salted hash not help in the vulnerable design?
The attacker does not need the password. The stored hash is itself accepted by the server, so cracking is unnecessary.