database hardening basics: MySQL edition
MySQL hardening basics: removing default and anonymous accounts, locking down remote root, requiring TLS, applying least-privilege grants, and enabling audit logging. Written for MySQL 8.0 on a fresh install. Use when provisioning a new database server or auditing an inherited one. Triggers: 'MySQL hardening', 'secure MySQL install'. Not for: Postgres, performance tuning.
database hardening basics: MySQL edition
TL;DR
Fresh MySQL installs ship with loose defaults, so the basics are: kill anonymous users and the test database, lock down root, require TLS for remote connections, and give every app its own least-privilege account. Takes about fifteen minutes and closes the holes scanners find first. Written for MySQL 8.0.
database hardening basics: MySQL editionUse this when
- You just provisioned a MySQL server and it is still on defaults
- You inherited a database and need a quick security pass
- A scanner flagged your MySQL config
- You are writing a runbook for new database servers
Not for this skill when
- You run Postgres (there is a separate skill for that)
- You need performance tuning, not security
- You are on a managed database like RDS (some of these are handled for you, check first)
Steps
1. Run mysqlsecureinstallation as a first pass
The bundled script handles the obvious stuff: root password, anonymous users, remote root, test database.
sudo mysql_secure_installationExpected: prompts walk you through setting a root password, removing anonymous users, disallowing remote root login, and dropping the test database. Answer yes to all of them.
2. Verify no anonymous or wildcard-host users remain
Trust but verify. List every account and its allowed hosts.
mysql -u root -p -e "SELECT user, host FROM mysql.user;"Expected: no rows with an empty user, and no app accounts with host % unless you genuinely need remote access from anywhere. Root should show the loopback host only.
3. Create least-privilege accounts per app
No app should log in as root. Give each service its own user with only the privileges it needs, on only the databases it uses.
mysql -u root -p -e "CREATE USER 'shopapp'@'app-host' IDENTIFIED BY '[a strong password]'; GRANT SELECT, INSERT, UPDATE, DELETE ON shopdb.* TO 'shopapp'@'app-host'; FLUSH PRIVILEGES;"Expected: SHOW GRANTS FOR 'shopapp'@'app-host'; shows only the four data privileges on shopdb, nothing global.
4. Require TLS for remote connections
Sniffing database traffic on a network is trivial, so force encryption for anything not on the same host. MySQL 8.0 enables TLS by default; make sure clients actually use it.
mysql -u root -p -e "SHOW VARIABLES LIKE 'require_secure_transport';"Expected: value ON. If it is OFF, set require_secure_transport=ON in the server config and restart. Then confirm from the app host that connections report TLS in use.
5. Turn on the audit log
You want a record of who connected and what they ran, especially failed logins. Enable the audit log plugin and check it writes.
mysql -u root -p -e "INSTALL PLUGIN audit_log SONAME 'audit_log.so'; SHOW VARIABLES LIKE 'audit_log_file';"Expected: the plugin installs and a log file path is reported. Tail the file after a test login to confirm entries appear.
Why this happens
MySQL's defaults prioritize getting started quickly over being safe, which is fair for local dev but dangerous on a server. Anonymous users, remote root, and cleartext traffic are the first things any scanner or attacker checks. The steps above do not make MySQL bulletproof, they just close the doors that ship open.
Edge cases and pitfalls
- Managed databases (RDS, Cloud SQL) handle root and TLS differently; check their docs before applying these steps blindly.
- Changing the root auth method can break existing backup scripts that connect as root; update those first.
require_secure_transport=ONbreaks old clients without TLS support; test app connections before enforcing.- Audit logs grow fast on busy servers; set up log rotation or you will fill the disk.
- Do not forget replicas: every read replica needs the same hardening, attackers love the forgotten replica.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_XdgDaPUYsX7A8lSYzU7WNg
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.