SEC.AUTH.ROLE_DEPRECATED_PASSWORD_HASH — The account's password is stored under a retired verifier
- Category: security
- Severity: high
- Level: 0
- Confidence: deterministic
- Downtime class: none — the finding is about an account, not about a statement
- Stability: stable
- Suites: audit
- Applies to: PostgreSQL 18 · MySQL 8.4
One rule, two engines, two different problems
The reader maps PostgreSQL's md5 and MySQL's mysql_native_password onto the same value, because
they are the same decision: the older of the two verifiers each engine offers. What they cost is not
the same, so the finding says something different depending on where it fired.
On PostgreSQL, the md5 verifier is salted with the role name and nothing else. A digest
captured anywhere is crackable offline, and — this is the part that surprises people — it is usable
as the password itself against any server where a role of that name exists. Two databases that
both have an app role share the exposure.
On MySQL, there is a second problem that has nothing to do with cryptography:
mysql_native_password is disabled by default in 8.4 and removed in 9.0. The account stops being
able to log in at the next major. Nothing in the connection string says so, and the usual moment to
discover it is the upgrade window.
Bad
-- MySQL: the plugin 9.0 removes
ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';
Good
-- MySQL: current verifier, and one that still exists after the next major
ALTER USER 'app'@'%' IDENTIFIED WITH caching_sha2_password BY 'secret';
On PostgreSQL the order matters and is easy to get backwards: the verifier is written when the
password is set, so changing password_encryption alone leaves every existing role exactly as it
was. Set the parameter, then have each role set its password again.
How an account gets here at all
Not by being created today. On a stock MySQL 8.4 the plugin reports plugin_status = DISABLED, and
CREATE USER … IDENTIFIED WITH mysql_native_password is refused outright with ERROR 1524. An
account reaches this state by having been created on an older server and carried forward through an
upgrade — which is exactly the case worth reporting, and exactly why a fresh installation shows
nothing.
What is not reported
The engine's own accounts (mysql.sys, mysql.session, mysql.infoschema, and their PostgreSQL
equivalents). They are marked as system accounts by the reader, and a finding a project cannot act on
is noise whichever way it is right. Measured: on a stock 8.4 all three are caching_sha2_password
anyway.
An account whose verifier could not be read is reported as undetermined, never as a current one. "Not deprecated" and "we could not see it" are the same sentence to a reader who cannot tell them apart, and only one of them is good news.
Related
SEC.AUTH.ROLE_DEPRECATED_PASSWORD_HASH_LOCKED— the same verifier on an account that cannot log in as it stands.SEC.AUTH.HBA_MD5— PostgreSQL'spg_hba.confasking for md5 on the wire, which is a different statement from what a role has stored.