Database audit fixes

Credential Storage: fixing all 4 checks

The credential storage audit finds database passwords in files other accounts can read: .env and wp-config.php in web roots, .git directories beside them, and client files like .my.cnf and .pgpass.

Two of the four checks skip when their search comes up empty: Web Config File Permissions only inspects the files the previous check found under /var/www, /srv/www or /usr/share/nginx/html, and Database Client Credentials skips when no .my.cnf, .pgpass, .mongoshrc.js, .dbshell or .rediscli_auth exists. A site under /home/user/public_html, or a service account whose home sits outside /home, therefore produces a SKIP, which records an unscanned path rather than a clean one.

4
checks in this audit
3
rated high severity
1
need root or sudo
~10s
automated run time

Transcribed from the database-security-credential-storage audit script. For what the audit covers rather than how to fix it, see the Credential Storage checklist.

Every check

Find your finding

Listed in the order the audit runs them. Take the check name from the third field of the result line and open its page.

  • Config Files In Web Root

    Soon

    Checks common web roots for credential-bearing config files such as .env and wp-config.php

    MediumNo root2 results
  • Web Config File Permissions

    Soon

    Checks whether credential-bearing config files inside web roots are world-readable

    HighNo root3 results
  • Git In Web Root

    Soon

    Checks for .git directories inside web roots that would expose repository history and committed credentials

    HighNo root2 results
  • Database Client Credentials

    Soon

    Checks that database client credential files are readable only by their owner (600)

    HighNeeds root3 results
Shared script behaviour

How the Credential Storage audit reads your server

Every check in this audit runs after the same preamble, and several of its results only make sense once you know what that preamble could and could not see.

Three run unprivileged; the fourth needs root for full coverage.

Engine detection: HAS_MY, HAS_PG, HAS_MG, HAS_RD are set from client binaries, server processes or config directories. MYOK=1 only if mysql -N -e 'SELECT 1' works as root over the socket; PGOK=1 only if su postgres -c 'psql -X -tAc "SELECT 1"' works. RDCONF is /etc/redis/redis.conf or /etc/redis.conf; MGCONF is /etc/mongod.conf. Containerised databases are not detected by these probes; audit them with the Docker checks or exec into the container.

Why checks skip

A SKIP is never a pass

A skipped check verified nothing at all. The most common cause is privilege: 1 of the 4 checks in this audit need root for a complete result. Before every run, each script works out which of four privilege modes it is in.

The four privilege modes an audit script can run in, and what each one means for checks that need root
ModeHow it is reachedWhat it means for your results
rootThe audit runs as UID 0.Every privileged branch runs directly. No check skips for lack of permission.
nopasssudo -n true succeeds, so the account has passwordless sudo.Privileged commands run through sudo -n.
passA sudo password was supplied in the CtrlOps audit settings and accepted.Privileged commands run through sudo -S.
noneNone of the above worked.CAN_ROOT=0. Every privileged branch returns SKIP with a "Need root" message rather than guessing.

If many checks skip with a “need root” message, run the audit as root, grant the audit account sudo (with or without NOPASSWD), or supply the sudo password in the CtrlOps audit settings.

FAQ

Credential Storage questions

It is reported as a WARN rather than a FAIL for exactly that reason. The search covers /var/www, /srv/www and /usr/share/nginx/html four levels deep, so a project root inside /var/www is matched even when the served directory is public/ one level below it and the file is never reachable over HTTP. Confirm with curl -I against /.env on your own hostname and expect a 403 or 404, and read the web server Hidden File Exposure finding alongside it. Moving the credentials to environment variables or to a path outside the tree is still worth doing, because one stray alias or a change of document root turns it into a real exposure.
It means five filenames did not turn up in three directories. The check looks for .env, wp-config.php, config.php, settings.php and database.yml under /var/www, /srv/www and /usr/share/nginx/html, to a depth of four, and stops after the first 20 matches. Sites served from /home/user/public_html or /opt are never scanned, other credential filenames are not matched, and a file the audit user cannot read is passed over silently. Run the audit with sudo for the widest coverage this check can give.
The two checks read different digits. Web Config File Permissions looks only at the last digit of the mode, so 640 is accepted and 644 is a FAIL, which suits a config file the web server has to read through its group. Database Client Credentials reads the group and other digits and wants 600, because .my.cnf and .pgpass hold plaintext passwords that no group needs. When you tighten a web config file, set the ownership first with chown root:www-data and then chmod 640, or PHP-FPM loses access and the site starts returning 500s.
Audit your fleet

Run all 4 Credential Storage checks, 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