Database audit fix

How to Remove Default Databases and Insecure Defaults

A fresh MySQL install ships a test database that historical grants left writable by any account, and PostgreSQL before 15 let every role create objects in the public schema. Drop test, and revoke CREATE on schema public from PUBLIC.

Hiren KalariyaLast reviewed: Sep 9, 2026Check default-databases
Low
severity
Yes
needs root or sudo
3
results it can return
2 of 7
checks in this audit

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

Low severityNeeds sudo

What this check reads

For MySQL the check asks the server SHOW DATABASES LIKE 'test' through the shared myq helper; any row at all means the installer's test database still exists. For PostgreSQL it runs SELECT has_schema_privilege('public','public','CREATE') through pgq, against the postgres maintenance database, and a returned value of exactly t means the PUBLIC pseudo-role can still create objects in the public schema. MongoDB has no probe: it is always recorded as "review by hand", because its default test database needs an interactive mongosh session. The verdict then aggregates across engines - one default found anywhere gives WARN and names the defaults alongside any engines that came back clean, while no defaults on at least one assessed engine gives PASS and lists both the clean engines and the ones that could not be audited.

When it applies

Runs per engine detected. The MySQL branch needs MYOK=1 (root socket access), the PostgreSQL branch needs PGOK=1; without them that engine is listed as "not audited" rather than judged. MongoDB is never assessed - it is always reported as needing a manual mongosh review - and Redis has no branch at all. The PostgreSQL probe runs only in the postgres maintenance database, so per-database public grants elsewhere in the cluster are not examined. A host with an engine present but nothing assessable produces SKIP, which is not a pass.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
WARN`test` DB exists, or PUBLIC has CREATE on `public`An installer default is still in place on an engine the check could query. The message names the engine and the default, and lists any engines that were clean.
PASSAssessed engines are cleanEvery engine the check could actually query is free of the default it looks for. Engines listed as not audited in the same message were not assessed at all.
SKIPNo engine, or none assessableNothing was verified: either no engine was detected, or the ones present could not be queried, so no default was ruled out.

Why it matters

  • MySQL's historical test database came with grants in mysql.db allowing any user (including anonymous) to create tables in any database named test or test_%. mysql_secure_installation removes it. CIS MySQL "Ensure the 'test' database is not installed".
  • PostgreSQL before 15 let every role CREATE in public. This enables search-path hijacking (CVE-2018-1058): a low-privilege role creates a function or operator that a superuser later calls. PostgreSQL 15 removed the default grant. CIS PostgreSQL "Ensure the public schema CREATE privilege is revoked".

Why it fails, and when it is wrong

  • A database legitimately named test in production triggers the MySQL branch. Rename it or accept.
  • The PostgreSQL probe runs in the postgres maintenance database only. Databases created before an upgrade to 15 keep the old grant even on a 15+ server, and those are not checked. Check each: psql -d <db> -c "SELECT has_schema_privilege('public','public','CREATE')".
  • Some ORMs/migrations tools assume they can create in public; revoking requires granting CREATE to the application owner role explicitly.

How to fix it

-- MySQL
DROP DATABASE test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'; FLUSH PRIVILEGES;
-- PostgreSQL (run in each database)
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
GRANT CREATE ON SCHEMA public TO app_owner;   -- only the role that needs it

Verify the fix

# MySQL - the test database must be gone, along with its grants:
sudo mysql -N -e "SHOW DATABASES LIKE 'test'"                       # expected: no output
sudo mysql -N -e "SELECT COUNT(*) FROM mysql.db WHERE Db LIKE 'test%'"  # expected: 0

# PostgreSQL - check EVERY database, not just the one the check looks at:
sudo -u postgres psql -X -tAc \
  "SELECT datname FROM pg_database WHERE datallowconn" | while read -r db; do
    printf '%s: %s\n' "$db" "$(sudo -u postgres psql -X -tAd "$db" -c \
      "SELECT has_schema_privilege('public','public','CREATE')")"
  done
# expected: f for every database

Debugging

Nothing matched the shared engine probe on this host.

An engine is installed but could not be queried (MYOK/PGOK are 0), or the only engine present is MongoDB, which this check never assesses. Test access with sudo mysql -N -e 'SELECT 1' / sudo -u postgres psql -X -tAc 'SELECT 1'.

Expected. The check probes only the postgres database. Databases created before an upgrade to PostgreSQL 15 keep the pre-15 grant; use the per-database loop above.

The MySQL branch matches the name only. Rename the database, or accept the finding; it is LOW severity for this reason.

The application role needs the privilege explicitly: GRANT CREATE ON SCHEMA public TO app_owner;. Revoking from PUBLIC is not the same as revoking from your app.

Sources

How the script reads this

Next

Re-run the Configuration & Hardening audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 7 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.

All 7 Configuration & Hardening 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