What this check reads
Asks the running server for a single setting with SHOW password_encryption, issued as the postgres OS user through psql -X -tAc. That value decides how a password is hashed when CREATE ROLE or ALTER ROLE ... PASSWORD is issued. PASS only if the value is exactly scram-sha-256; anything else, including md5 and the legacy on, is a WARN with the live value quoted in the message.
When it applies
Runs only when PostgreSQL is detected (HAS_PG=1) and reachable as the postgres user through root/sudo (PGOK=1); otherwise SKIP. It asks the running server (SHOW password_encryption), so it reports the value actually in effect, not what is written in postgresql.conf. It says nothing about how existing passwords are already stored - changing this setting does not re-hash them.
What each result means
| Result | When | What it means |
|---|---|---|
| PASS | `scram-sha-256` | Any password set from now on is stored with a modern salted hash. |
| WARN | Anything else (`md5`, or `on` on very old servers) | The live value is reported. New passwords are being written with a weak hash even if pg_hba.conf already asks for scram. |
| SKIP | Not accessible | Nothing was read. The setting is unknown, so the hashing algorithm is unverified rather than fine. |
Why it matters
password_encryption decides how new passwords are hashed in pg_authid. With md5, ALTER ROLE ... PASSWORD stores a crackable unsalted hash even if pg_hba.conf already says scram-sha-256. Default is scram-sha-256 since PostgreSQL 14. CIS PostgreSQL: "Ensure 'password_encryption' is set to 'scram-sha-256'".
Why it fails, and when it is wrong
- Clusters initialised before 14 and upgraded with
pg_upgradekeep the oldpostgresql.confvalue. - Changing the setting does not re-hash existing passwords. A
md5-hashed role cannot authenticate via ascram-sha-256HBA line; rehash before switching HBA. - Check current hash formats:
SELECT rolname, left(rolpassword,5) FROM pg_authid WHERE rolpassword IS NOT NULL;(SCRAMvsmd5).
How to fix it
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();
ALTER ROLE app PASSWORD 'same-or-new-password'; -- per roleVerify the fix
sudo -u postgres psql -X -tAc 'SHOW password_encryption'
# expected: scram-sha-256
# Confirm stored hashes were actually migrated, not just the setting:
sudo -u postgres psql -X -c "SELECT rolname, left(rolpassword,4) AS fmt FROM pg_authid WHERE rolpassword IS NOT NULL"
# expected: every fmt is SCRAM (md5... means that role still has a legacy hash)Debugging
HAS_PG=0 or PGOK=0; see the debugging notes under PostgreSQL Auth Methods for how to test the probe.
Expected. The setting only governs hashes written from now on. The pg_authid query above shows which roles still carry an md5 hash; re-issue ALTER ROLE <role> PASSWORD '...' for each.
ALTER SYSTEM writes postgresql.auto.conf, which is read last and therefore overrides postgresql.conf. If a config-management tool rewrites either file the value can flip back after a reload. Find out which file actually supplied the live value with SELECT name, setting, source, sourcefile FROM pg_settings WHERE name='password_encryption';.
Its stored hash and the HBA method now disagree. Re-hash that role, or temporarily keep the HBA line on md5 until every role is migrated.
Sources
- PostgreSQL: password_encryption (Connection Settings)
- PostgreSQL: pg_authid catalog
- PostgreSQL 14 release notes (scram default)
- PostgreSQL wiki: SCRAM authentication
How the script reads this
Next
Re-run the Authentication audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 8 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 8 Authentication fixes