Skip to content

Use scram-sha-256, not md5

Severity: highApplies to: PostgreSQL 14+Applies to: PostgreSQL 18
The fixpostgresql.conf
Terminal window
password_encryption = scram-sha-256

Then re-set every existing password — this is the part that actually migrates anything:

ALTER ROLE myapp WITH PASSWORD 'the-same-or-a-new-secret';

And switch the rules in pg_hba.conf from md5 to scram-sha-256.

scram-sha-256 has been the default for password_encryption since PostgreSQL 14, and md5 is now on an explicit path to removal. The PostgreSQL 18 documentation states it plainly:

Support for MD5-encrypted passwords is deprecated and will be removed in a future release of PostgreSQL.

PostgreSQL 18 also adds the md5_password_warnings parameter — on by default — which makes CREATE ROLE and ALTER ROLE emit a deprecation warning into the log whenever a password is stored as md5. If your logs have started filling with those, this page is why.

Beyond the deprecation, the cryptography is the point. Postgres’s md5 scheme hashes the password with the role name as salt, which means the stored hash is the credential: anyone who reads pg_authid can replay it to authenticate, without ever cracking anything. SCRAM does not have that property — the stored verifier cannot be replayed.

Changing password_encryption does nothing on its own

Section titled “Changing password_encryption does nothing on its own”

This is the mistake that makes people think they’ve migrated.

password_encryption only decides how a password is hashed when it is set. Changing it does not rewrite existing hashes. A role whose password was stored as md5 in 2019 still has an md5 hash, still authenticates via md5, and is entirely unaffected by the new setting.

The hash is only rewritten when the password is set again. That’s why the ALTER ROLE ... PASSWORD step above is not optional — it is the migration. And it must come after the setting change, or you’ll write another md5 hash.

SELECT rolname,
CASE
WHEN rolpassword LIKE 'md5%' THEN 'md5'
WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'scram-sha-256'
WHEN rolpassword IS NULL THEN 'no password'
ELSE 'other'
END AS method
FROM pg_authid
WHERE rolcanlogin
ORDER BY 2, 1;

Requires superuser — pg_authid is restricted precisely because those hashes are sensitive.