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.