Users, privileges & security
CREATE USER, GRANT, REVOKE, roles, least privilege, authentication plugins and SQL injection.
A database usually holds a company's most valuable data, so it's a prime target for attackers. MySQL security comes down to three ideas: authenticate every connection, give each account the least privilege it needs, and keep untrusted input out of SQL code. This lesson covers accounts, privileges, roles and the habits that keep a server safe.
Accounts are user + host#
- The host part says where the user may connect from:
localhost(this machine), an IP, a pattern like10.0.0.%, a hostname, or%(anywhere). 'app'@'localhost'and'app'@'%'are different accounts, each with its own password and privileges. When several match, MySQL uses the most specific host.PASSWORD EXPIREforces Alice to choose a new password at first login.
List accounts:
MySQL 8's default authentication plugin is caching_sha2_password. It's secure, but very old client libraries may not support it. (In 8.4 the legacy mysql_native_password plugin is disabled by default.)
Managing accounts#
Remove an account with DROP USER 'alice'@'%';. Users change their own password with ALTER USER USER() IDENTIFIED BY '...';.
Privileges with GRANT and REVOKE#
Privileges can be granted at several levels:
GRANT USAGE ON *.* just means "can log in". Take privileges away with REVOKE:
Common privileges: SELECT, INSERT, UPDATE, DELETE (data); CREATE, ALTER, DROP, INDEX, REFERENCES (schema); EXECUTE, CREATE ROUTINE, TRIGGER, EVENT (programs); PROCESS, RELOAD, REPLICATION SLAVE (admin). MySQL 8 also adds fine-grained dynamic privileges such as BACKUP_ADMIN and CONNECTION_ADMIN. ALL PRIVILEGES grants everything at that level except GRANT OPTION, which lets a user pass privileges on.
Roles (MySQL 8.0+)#
Instead of granting the same list of privileges to dozens of accounts, grant them to a role and grant the role to people:
Roles are inactive until activated. SET DEFAULT ROLE (or SET ROLE within a session) does that. Change a role's privileges once and every member is updated.
Least privilege in practice#
A typical production setup:
Rules of thumb:
- Never let applications connect as root. If the app is compromised, so is every database on the server.
- No
%hosts for powerful accounts. Restrict them to specific IPs orlocalhost. - One account per application or service, so you can audit and revoke individually.
- Review grants regularly (
SHOW GRANTS,SELECT * FROM mysql.user).
SQL injection#
SQL injection happens when user input is concatenated into SQL text. Imagine this login code:
If an attacker types the email ' OR 1=1 -- , the query becomes:
OR 1=1 matches every row and -- comments out the password check, so the attacker logs in as the first user. With a different payload, they could read other tables or delete data.
The fix is parameterised queries (prepared statements). The SQL and the data travel separately, so input can never become code:
The same mechanism exists in SQL itself, which is useful inside stored procedures:
Placeholders work for values only, not for table or column names. If those must be dynamic, check them against an allow-list in your code. Least privilege is your second line of defence: an injected DROP TABLE fails if the account has no DROP privilege.
Server hardening checklist#
- Run
mysql_secure_installationon new servers (removes anonymous users and the test database). - Network: bind to a private interface (
bind-address = 10.0.0.5, or127.0.0.1if only local apps connect), and firewall port 3306 so only app servers can reach it. Never expose MySQL directly to the internet. - Encryption in transit: MySQL 8 sets up TLS automatically. Require it per account with
ALTER USER 'app'@'%' REQUIRE SSL;, or for everyone withrequire_secure_transport = ON. - Encryption at rest: InnoDB supports tablespace encryption (
ENCRYPTION='Y') with a keyring component. - Passwords: enable the
validate_passwordcomponent for strength rules, and never store application passwords in plain text. Store password hashes made with bcrypt or Argon2 in your application, not with MySQL'sMD5()/SHA1(). - Secrets: keep DB credentials in environment variables or a secrets manager, not in Git.
- Files:
secure_file_privlimits whereLOAD DATAandSELECT ... INTO OUTFILEcan read and write. - Patching and auditing: apply minor updates promptly, watch the error log for failed logins, and consider an audit-log plugin for regulated data.
Cleaning up#
What's next#
Security also means being able to recover. Next: backing up and restoring MySQL with mysqldump and binary logs.
Check your understanding
Quick quiz
1.In MySQL, what identifies an account?
2.Which is the best defence against SQL injection?
3.What does
GRANT SELECT, INSERT ON shop.* TO 'app'@'%';allow?
Finished reading?
Mark this lesson complete to track your progress.