新建数据库后必须立即创建专用应用账号并授予最小权限。需用create user建带业务前缀的账号,grant select/insert/update等必要权限,revoke drop/alter等危险权限,启用default_password_lifetime等密码策略并定期审计。

MySQL 新建数据库时必须限制用户权限范围
新建数据库不等于默认安全,CREATE DATABASE 本身不绑定任何用户权限,执行完后如果用 root 或高权限账号连上去操作,后续所有表、数据都继承该账号的越权能力。真实风险常出现在开发提需求“先建个库试试”,DBA顺手用 root 执行完就交付,结果应用直接以 root 连接——等于把锁孔钥匙焊死在门把手上。
- 建库后立刻用
CREATE USER单独建应用专用账号,用户名带业务前缀(如app_order_user),避免泛用dev、test这类模糊名 - 授权只给最小集合:
GRANT SELECT, INSERT, UPDATE ON `order_db`.* TO 'app_order_user'@'10.20.%';禁止用GRANT ALL PRIVILEGES,哪怕临时调试也不行 - 显式禁用危险权限:
REVOKE DROP, ALTER, CREATE VIEW, EXECUTE ON `order_db`.* FROM 'app_order_user'@'10.20.%',防止误删表或注入视图绕过逻辑 - 若应用真需要建临时表,单独开
CREATE TEMPORARY TABLES权限,且限定在会话级,不写进全局授权
定期清理失效账号和密码策略要落地到 MySQL 配置层
权限清单不是静态文档,是活的检查项。很多团队有“每季度review账号”的流程,但没配 default_password_lifetime 或 password_history,结果人工漏审一个账号,它就能永久存活。
- 在
my.cnf中强制启用密码策略:default_password_lifetime = 90、password_history = 5、password_reuse_interval = 365 - 用
SELECT user, host, password_last_changed FROM mysql.user WHERE password_last_changed 查过期账号,注意 <code>password_last_changed在 MySQL 5.7+ 才有,8.0+ 才支持password_history - 删除账号不用
DROP USER就完事,必须补一句FLUSH PRIVILEGES,否则内存缓存可能让旧权限继续生效几分钟 - 对已存在的弱密码账号(比如空密码、
123456),用ALTER USER 'xxx'@'%' IDENTIFIED BY '强密码' PASSWORD EXPIRE IMMEDIATE强制下次登录改密
备份账号不能有写权限,且连接必须走专用网段
备份脚本用的账号一旦被泄露或误配置,就是全量数据外泄入口。见过最典型的错误:运维把 mysqldump 命令写进 crontab,账号却和应用共用一个 app_rw_user,结果某次 dump 失败日志里明文打出连接串,Git 历史里全量可查。
- 专设备份账号,如
bkp_reader@'172.16.10.%',只授SELECT和LOCK TABLES(mysqldump --single-transaction可免后者) - 备份命令中禁止出现明文密码:用
mysql_config_editor存加密凭据,调用时用--login-path=bkp;或者用MYSQL_PWD环境变量(仅限本地脚本,别放 CI/CD) - 数据库防火墙或安全组必须限制该账号 IP 段,严禁开放到
0.0.0.0/0或开发网段 - 物理备份(xtrabackup)账号同样适用此规则,
xtrabackup不需要写权限,给RELOAD, PROCESS, LOCK TABLES足够
权限变更必须走 SQL 变更工单,禁止直接在生产库执行 GRANT
线上权限调整不是“执行一条命令就完事”,而是变更链中最脆弱的一环。曾有团队 DBA 在凌晨修复问题,顺手给某个监控账号加了 SHOW DATABASES,结果该账号被上游服务误当成管理接口调用,触发大量元数据扫描,拖慢主库响应。
-
GRANT和REVOKE是 DDL 操作,会触发隐式提交,无法回滚;必须提前在预发环境验证语句效果,尤其注意@'%'和@'localhost'是两个完全独立账号 - 工单里必须写清:授权账号、目标库表、权限类型、生效 IP 段、有效期(是否临时)、关联业务方及负责人
- 执行后立刻验证:
SHOW GRANTS FOR 'xxx'@'yyy'看结果,再用该账号实际连一次,跑SELECT COUNT(*) FROM information_schema.TABLES测试是否真受限 - 自动化运维平台若封装了授权功能,后端必须校验 SQL 模板,禁止传入变量拼接库名/权限名,防 SQL 注入绕过
权限不是设完就稳的,是每次连接、每次查询、每次备份都在重新验证的动态边界。最容易被忽略的是权限继承关系——比如给 app_user 授了 SELECT,但它所属的 app_role 角色又被悄悄加了 INSERT,这种嵌套授权在 MySQL 8.0+ 很常见,查 SHOW ROLE GRANTS FOR 'app_user'@'%' 才能看清全貌。










