Chapter 12 · Encryption, TLS, Secrets, Auditing, and Security Hardening
Hardening Checklist: Network Exposure, Local Files, Plugins, Accounts, and Patch Discipline
Convert security guidance into an evidence-driven MySQL hardening review covering network exposure, secure transport, local-file controls, plugins/components, accounts, privileges, logging, key material, backups, and patch discipline.
Learning outcomes
Hardening is not a list of magical settings. A MySQL server may be safe on a loopback-only developer laptop with one configuration and unsafe on an Internet-reachable host with the same configuration. The final Chapter 12 exercise therefore starts from exposure and evidence, ranks risks, applies the minimum useful change, and records what still remains.
Inventory network/listen exposure, TLS enforcement, local-file capabilities, plugins/components, accounts, privileges, logs, and patch level.
Explain why local_infile, FILE privilege, secure_file_priv, and OS filesystem permissions form different security boundaries.
Identify anonymous/test accounts, wildcard administrative grants, unnecessary components/plugins, and stale identities without blindly deleting working dependencies.
Prioritize patching and release verification using the actual installed server/client versions and current Oracle security/release information.
Produce a repeatable hardening record with owner, evidence, decision, rollback, verification, and residual risk.
Start with exposure, not a canned checklist
The first question is “who can reach this server?” bind_address, network interfaces, container port publication, host firewall rules, cloud security groups, proxies, VPNs, and service meshes all participate. MySQL can only report part of that picture, so combine server variables with operating-system/network evidence.
-- Run as a local administrator on a disposable MySQL instance.CREATE DATABASE IF NOT EXISTS servicehub_security_lab CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;CREATE TABLE IF NOT EXISTS servicehub_security_lab.security_events ( event_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, event_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), actor VARCHAR(80) NOT NULL, event_type VARCHAR(40) NOT NULL, detail VARCHAR(255) NOT NULL) ENGINE=InnoDB;INSERT INTO servicehub_security_lab.security_events(actor,event_type,detail)VALUES ('chapter12','LAB_START','Chapter 12 disposable security lab');SELECT COUNT(*) AS event_rowsFROM servicehub_security_lab.security_events;SELECT VERSION() AS server_version, @@hostname AS server_host, @@port AS port, @@GLOBAL.bind_address AS bind_address, @@GLOBAL.require_secure_transport AS require_secure_transport, @@GLOBAL.skip_name_resolve AS skip_name_resolve;SHOW SESSION STATUS LIKE 'Ssl_cipher';SHOW GLOBAL STATUS LIKE 'Ssl_%';A wide bind address is not automatically a vulnerability if an effective firewall restricts reachability, but it expands the consequences of a firewall mistake. Conversely, binding to loopback does not protect a database exposed through an SSH tunnel or proxy. Record the full path that clients use.
Local file features cross the SQL/filesystem boundary
MySQL has two frequently confused mechanisms. LOAD DATA LOCAL asks the client to send a local file; server and client both participate in enabling it. The global FILE privilege allows server-side file operations such as LOAD DATA without LOCAL and SELECT ... INTO OUTFILE, constrained by the mysqld operating-system identity and secure_file_priv.
SHOW GLOBAL VARIABLES LIKE 'local_infile';SHOW GLOBAL VARIABLES LIKE 'secure_file_priv';SELECT GRANTEE, PRIVILEGE_TYPE, IS_GRANTABLEFROM INFORMATION_SCHEMA.USER_PRIVILEGESWHERE PRIVILEGE_TYPE = 'FILE'ORDER BY GRANTEE;MySQL 8.4 disables server-side local_infile by default. Keep it disabled unless an actual workflow needs it, then constrain the client side as well. secure_file_priv is non-dynamic; a directory value limits relevant server-side import/export operations to that directory, while NULL disables them. An empty value imposes no such directory restriction and is explicitly considered insecure by the server documentation.
Do not grant FILE to an application just because an import feature is convenient. FILE can expose files readable by mysqld and can write files where mysqld has permission, subject to secure_file_priv. Prefer application-mediated uploads/staging when possible.
Inventory accounts, roles, and dangerous breadth
SELECT User, Host, plugin, account_locked, password_expiredFROM mysql.userORDER BY User, Host;SELECT FROM_USER, FROM_HOST, TO_USER, TO_HOSTFROM mysql.role_edgesORDER BY FROM_USER, TO_USER;-- Review grants for each service/operator account individually.SHOW GRANTS FOR 'svc12_secret_b'@'127.0.0.1';Look for anonymous users, obsolete test/service identities, hosts broader than the network requirement, locked accounts that should be removed after retention policy, and accounts with ALL ON *.* where a narrow role would work. Do not automate deletion solely from a name pattern; first map accounts to owners, applications, definers, events, replication, and backup jobs.
-- As the narrow application account, this should fail:CREATE DATABASE should_not_be_allowed;-- The correct response is NOT to grant global CREATE.-- Verify the account can still perform only its intended application work.SELECT COUNT(*) FROM servicehub_security_lab.security_events;An expected denial is a security test result. Preserve it in the hardening record.
Plugins and components: reduce attack surface without breaking dependencies
SHOW PLUGINS;SELECT component_id, component_group_id, component_urnFROM mysql.componentORDER BY component_id;“Uninstall everything unfamiliar” is not hardening. Authentication, keyring, password validation, cloning, audit, and other capabilities may be operational dependencies. For each loaded plugin/component, record why it is present, which edition/package supplied it, who owns it, how it is patched, and what breaks if it is removed. Remove only after dependency and rollback testing on a disposable or staging instance.
Check logging, key material, and backup exposure together
SHOW GLOBAL VARIABLES WHERE Variable_name IN ( 'general_log','slow_query_log','log_output','log_raw', 'binlog_encryption','innodb_redo_log_encrypt','innodb_undo_log_encrypt', 'default_table_encryption','require_secure_transport');SELECT component_urn FROM mysql.componentWHERE component_urn LIKE '%keyring%';A hardened server can still leak data through readable backups or logs. Ensure backup destinations, keyring files/services, TLS private keys, option files, and log directories have explicit ownership and retention. Test restores with the same key dependencies you expect during a disaster. Chapter 13 will turn that into a full backup and point-in-time-recovery workflow.
Patch discipline is a control, not an annual maintenance ritual
Start with the version actually running, then compare it with Oracle's current 8.4 download/release and security information. At the time this lesson was generated, Oracle's live Community Server page exposes 8.4.10 LTS; the online 8.4.11 release-note page is currently marked “Not yet released.” That is why this chapter declares 8.4.10 rather than freezing the sequential guide's earlier 8.4.11 note.
SELECT VERSION() AS server_version;-- Run outside SQL as applicable:mysql --versionmysqlsh --versionServer and client versions are independent. Connectors, MySQL Shell, Router, operating-system packages, OpenSSL, and plugins/components have their own lifecycles. A security review should inventory each component that processes credentials or database traffic.
Security patches are important, but production changes still require compatibility review, backups, restore confidence, staged rollout, and rollback/fail-forward planning. “Patch quickly” and “change safely” are complementary goals.
Turn findings into a prioritized hardening record
| Finding | Evidence | Decision pattern |
|---|---|---|
| Plaintext TCP possible | negative --ssl-mode=DISABLED test succeeds | Fix clients/trust, then enforce secure transport |
| Application has global privileges | SHOW GRANTS | Replace with scoped role/object grants |
| FILE granted to app | USER_PRIVILEGES | Revoke unless explicitly required; constrain secure_file_priv |
| local_infile enabled without use | @@global.local_infile | Disable after confirming no workflow depends on it |
| Unknown plugin/component | SHOW PLUGINS / mysql.component | Identify owner/dependency; remove only after test |
| Backups unencrypted or untested | artifact inspection + restore test | Protect artifact and verify restore/key chain |
| Old patch level | VERSION() + vendor advisory | Plan staged supported update |
Rank by plausible exploit path and impact, not by how easy a setting is to change. A remotely reachable passwordless/global-admin account is more urgent than a low-risk cosmetic finding. Record residual risk when a business dependency prevents immediate remediation.
Hands-on acceptance test
-- 1. Identity and privilege evidenceSELECT CURRENT_USER(), USER();SHOW GRANTS;-- 2. Transport evidenceSHOW SESSION STATUS LIKE 'Ssl_cipher';SHOW GLOBAL VARIABLES LIKE 'require_secure_transport';-- 3. File boundary evidenceSHOW GLOBAL VARIABLES LIKE 'local_infile';SHOW GLOBAL VARIABLES LIKE 'secure_file_priv';-- 4. Extensibility / keyring evidenceSELECT component_urn FROM mysql.component ORDER BY component_urn;-- 5. Log postureSHOW GLOBAL VARIABLES WHERE Variable_name IN ('general_log','slow_query_log','log_output','log_raw');-- 6. Patch identitySELECT VERSION();Save the output with the date, environment name, reviewer, and intended exceptions. Do not include passwords, private keys, or secret values in the evidence bundle. A hardening review is repeatable when another operator can rerun the same observations and understand why each exception exists.
Knowledge check
- Why is bind_address alone insufficient to prove network exposure?
- What is the difference between local_infile and FILE privilege?
- What does secure_file_priv=NULL do?
- Why should unknown plugins not be removed automatically?
- Why does patch discipline include restore planning?
Reveal answers
- Firewalls, containers, proxies, tunnels, and external network controls also determine reachability.
- local_infile controls the client-to-server LOCAL loading capability, while FILE authorizes server-side filesystem import/export operations.
- It disables the relevant server-side import/export file operations controlled by secure_file_priv.
- They may be dependencies for authentication, keyring, policy, or operations; removal can break the server or recovery path.
- A security update is still a production change. Safe rollout depends on compatibility checks, backups, recovery confidence, and a rollback/fail-forward plan.
Summary and bridge to Chapter 13
Security is now a layered operational system: authenticated least-privilege identities, verified TLS, controlled secrets, explicit at-rest/key boundaries, protected logs, constrained filesystem features, known plugins, and current patch state. Chapter 13 builds the recovery side of that security model—backups, restores, binary logs, and point-in-time recovery—because a secure database that cannot be recovered is not production-ready.