Skip to main content

SEC.AUTH.HBA_MD5 — md5 authentication, whose verifier is the password

  • Category: security
  • Severity: high
  • Level: 0
  • Confidence: deterministic
  • Downtime class: none — the finding is about how the server authenticates, not about a statement
  • Stability: stable
  • Suites: audit
  • Applies to: PostgreSQL 18

The role name is the only salt​

PostgreSQL's md5 verifier is md5(password || rolname). The role name is public, so the digest is effectively unsalted: a captured verifier can be attacked offline at enormous speed. Worse, the protocol accepts a client that knows only the digest — so the verifier IS a password equivalent, and recovering the original is optional.

scram-sha-256 fixes both, and PostgreSQL has shipped it since version 10.

The order matters, or nobody can log in​

Changing the line first leaves every role holding an MD5 verifier the server will no longer accept.

-- 1. new passwords will be stored as SCRAM verifiers
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();

-- 2. every role sets its password AGAIN, which is what actually rewrites the verifier
ALTER ROLE app PASSWORD 'a new one';

-- 3. confirm before touching pg_hba.conf
SELECT rolname FROM pg_authid WHERE rolpassword LIKE 'md5%';

Only then change the line.

Bad​

host all all 10.0.0.0/8 md5

Good​

hostssl all app 10.0.4.0/24 scram-sha-256
  • SEC.AUTH.HBA_CLEARTEXT — the method that sends the password itself.
  • What each role currently has stored is a separate question from what a line negotiates, and the two do not have to agree: a role can hold a SCRAM verifier while a line still offers md5.