Database audit fix

How to Secure PostgreSQL Authentication Methods

The last column of every pg_hba.conf line is the authentication method: trust accepts any client with no credential, password sends it in the clear, and md5 is crackable offline. Replace all three with scram-sha-256.

Hiren KalariyaLast reviewed: Aug 30, 2026Check postgresql-auth-methods
High
severity
Yes
needs root or sudo
5
results it can return
5 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.

High severityNeeds sudo

What this check reads

Asks the server where its HBA file is (SHOW hba_file), strips comments and blank lines with grep -vE '^[[:space:]]*(#|$)', then counts the remaining lines whose last field ($NF) is trust, password or md5. Evaluated in that order: any trust is FAIL, else any password is FAIL, else any md5 is WARN, else PASS. The threshold on each of the three is a single line, and the count that triggered the result is quoted in the message.

When it applies

Runs only when PostgreSQL is detected (HAS_PG=1: a psql binary, a running postgres process, or /etc/postgresql / /var/lib/pgsql). Reading the HBA file needs PGOK=1 - the script must be able to run psql as the postgres OS user via root/sudo - and then reads the file root-only. It evaluates the file on disk, which is the pending configuration; a file edited but not reloaded is judged before the server has adopted it. Only the file named by SHOW hba_file is read: include, include_if_exists and include_dir directives (PostgreSQL 16+) are not followed.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
SKIPPostgreSQL not installed / not accessibleNothing was read. The two reasons are reported separately, and neither of them is a pass.
FAILAt least one `trust` lineClients matching those lines are logged in as whatever role they ask for, with no credential at all.
FAILAt least one `password` (cleartext) lineMatching clients send their password unencrypted, which is only safe inside TLS. Move those lines to scram-sha-256.
WARNAt least one `md5` lineThose logins still work, but a captured md5 hash is replayable and crackable offline. Migrate them to scram-sha-256.
PASSOnly `peer`, `scram-sha-256`, `cert`, `ident`, `reject`, etc.No line in that file lets a client in without a modern credential check.

Why it matters

trust means any client matching that line is logged in as any requested role with no credential. password sends the password in cleartext (only safe inside TLS). md5 is unsalted MD5 over password+username; captured hashes are replayable and crackable offline. PostgreSQL recommends scram-sha-256 (SASL, RFC 7677) and, since PostgreSQL 18, emits a deprecation warning for md5. CIS PostgreSQL Benchmark: "Ensure the 'trust' method is not used" and "Ensure password encryption is SCRAM".

Why it fails, and when it is wrong

  • Old pg_hba.conf files upgraded in place keep md5. Default files since PostgreSQL 14 use scram-sha-256.
  • Docker postgres image with POSTGRES_HOST_AUTH_METHOD=trust adds host all all all trust.
  • Lines with auth options after the method (md5 map=..., scram-sha-256 clientcert=verify-full, ldap ldapserver=...) are not counted because $NF is the option, not the method. This can mask a trust/md5 line that has trailing options (rare) and a password line with options.
  • include, include_if_exists, include_dir (PostgreSQL 16+) are not followed.
  • A local all postgres trust line is sometimes left by initdb helpers; it is still a FAIL because any OS user can psql -U postgres.

How to fix it

Find the file PostgreSQL is actually using

Ask the running server rather than assuming the packaged path, because a cluster can be pointed at an HBA file anywhere on disk.

HBA=$(sudo -u postgres psql -X -tAc 'SHOW hba_file')

Read the active rules

Strip the comments and blank lines, then read the last column of every line that is left. That column is the authentication method.

sudo grep -vE '^[[:space:]]*(#|$)' "$HBA"

Replace the weak methods

Edit that file and change trust, password and md5 to scram-sha-256 on every line that has them, local lines included, or to peer for local Unix-socket admin access. If any role's password is still stored as an md5 hash, do step 4 first: an md5 hash cannot satisfy a scram line, and those logins break the moment you reload.

SELECT pg_reload_conf();
SELECT * FROM pg_hba_file_rules WHERE error IS NOT NULL;

Set the hashing algorithm before re-hashing

The stored hash format follows password_encryption at the time of the ALTER, so set it and reload before rewriting anything.

ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();

Then re-hash every password-authenticated role, with \password username in psql or ALTER ROLE username PASSWORD '...'.

Verify the fix

# Ask the server itself which rules are live, rather than reading the file:
sudo -u postgres psql -X -c "SELECT type,database,user_name,address,auth_method FROM pg_hba_file_rules WHERE auth_method IN ('trust','password','md5')"
# expected: no rows

sudo -u postgres psql -X -c "SELECT * FROM pg_hba_file_rules WHERE error IS NOT NULL"
# expected: no rows (no syntax errors pending)

sudo -u postgres psql -X -tAc 'SHOW password_encryption'
# expected: scram-sha-256

Debugging

The probe found nothing. Confirm with command -v psql; pgrep -a postgres; ls -d /etc/postgresql /var/lib/pgsql 2>/dev/null.

PGOK=0. Run exactly what the script runs: sudo su -s /bin/sh postgres -c 'psql -X -tAc "SELECT 1"'. Failures are usually a stopped cluster, a non-default port/socket (PGPORT, unix_socket_directories), or no usable sudo.

The check reads the file, so an unreloaded edit shows immediately, but the server has not adopted it. Run SELECT pg_reload_conf(); and compare pg_hba_file_rules (live) with the file.

You probably edited a file the server does not use. sudo -u postgres psql -X -tAc 'SHOW hba_file' prints the only path the check reads. Rules pulled in by include/include_dir are invisible to the check, so a clean report does not prove included files are clean - inspect pg_hba_file_rules, which does resolve includes.

The script matches on the last field ($NF), so md5 map=... is missed. Cross-check with the pg_hba_file_rules query above, which parses the method properly.

Existing passwords hashed under md5 cannot satisfy a scram line. Re-hash first (ALTER ROLE ... PASSWORD ... with password_encryption already set to scram), then change the HBA lines.

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