Database audit fix

How to Change PostgreSQL User Password and Secure Hashing

Change a PostgreSQL password with ALTER ROLE name PASSWORD 'new-secret', which stores the hash using whatever password_encryption is set to at that moment. Set password_encryption to scram-sha-256 first, or you write a crackable md5 hash.

Hiren KalariyaLast reviewed: Aug 30, 2026Check postgresql-password-hashing
Medium
severity
Yes
needs root or sudo
3
results it can return
6 of 8
checks in this audit

Every threshold on this page is transcribed from the database-security-authentication audit script that CtrlOps runs, and a build check fails if the two ever disagree. See all 8 Authentication checks, or how the audit runs.

Medium severityNeeds sudo

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 thresholds this check applies
ResultWhenWhat it means
PASS`scram-sha-256`Any password set from now on is stored with a modern salted hash.
WARNAnything 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.
SKIPNot accessibleNothing 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_upgrade keep the old postgresql.conf value.
  • Changing the setting does not re-hash existing passwords. A md5-hashed role cannot authenticate via a scram-sha-256 HBA line; rehash before switching HBA.
  • Check current hash formats: SELECT rolname, left(rolpassword,5) FROM pg_authid WHERE rolpassword IS NOT NULL; (SCRAM vs md5).

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 role

Verify 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

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
Audit your fleet

Find every one of these on every server, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals