Safeguard
Application Security•

CWE-836 Use of Password Hash Instead of Password for Authentication

If the client sends the hash and the server compares it to the stored hash, a stolen hash is as good as the password. Here is how CWE-836 works.

Safeguard Security Team
2 min read

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:

  1. Have the server hash the submitted password itself, with a slow, salted password hashing function such as bcrypt, scrypt or Argon2.
  2. Send credentials only over TLS.
  3. Treat stored hashes as sensitive data and protect the database against injection.
  4. 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.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.