应用账号须遵循最小权限原则:禁用root及全局权限,按需授予列级只读、表级写入权限,动态管理运维脚本权限,并持续审计权限变更。

为什么应用账号不能直接用 root 或全局 SELECT
root 账号或 GRANT SELECT ON *.* 这类宽泛授权,等于把整栋楼的门禁卡和电梯卡都交出去。一旦应用被注入或凭证泄露,攻击者能直接 SHOW DATABASES、SELECT * FROM mysql.user、甚至拖走 information_schema.columns 全量元数据。生产环境里,连 test 库和匿名用户都该清掉——它们不是“没用”,而是“默认开着后门”。
创建只读应用账号时,列级授权要显式列出字段
MySQL 的列级 SELECT 权限只在显式声明字段时生效,且不自动继承表权限。漏掉这步,账号可能意外获得整表读取能力。
- 正确写法:
GRANT SELECT (id, username, email) ON myapp.users TO 'web_ro'@'192.168.10.%'; - 必须补上撤销语句:
REVOKE SELECT ON *.* FROM 'web_ro'@'192.168.10.%';(否则隐式继承可能生效) - 验证是否干净:
SHOW GRANTS FOR 'web_ro'@'192.168.10.%';,输出里不该出现ON myapp.*或ON *.* - 注意:
DESCRIBE users和SHOW CREATE TABLE users仍会失败——它们需要information_schema或SHOW VIEW权限,别为了“方便 ORM 探测”而全开
写操作账号必须按表隔离,且禁用 DDL 权限
MySQL 不支持可靠的字段级 UPDATE 限制(比如“只允许改 status 字段”),SET 子句会绕过列权限。真正可控的是表边界。
- 为订单服务单独建账号:
CREATE USER 'app_writer'@'10.0.2.%' IDENTIFIED BY 'StrongP@ss_2026'; - 只授必要表的 DML:
GRANT INSERT, UPDATE, DELETE ON myapp.orders TO 'app_writer'@'10.0.2.%'; - 明确禁止高危操作:
REVOKE DROP, ALTER, CREATE, INDEX ON myapp.* FROM 'app_writer'@'10.0.2.%'; - 敏感字段如
password_hash、deleted_at必须靠应用层逻辑拦截——权限系统管不了“改什么值”,只管“能不能发这条 SQL”
运维脚本权限不能长期持有,得动态授予+显式回收
给脚本用的账号长期挂着 SUPER 或 PROCESS 权限,等于在保险柜上焊一把永不拔的钥匙。MySQL 5.7+ 支持角色,但更轻量的做法是执行前授、执行后收。
- 备份脚本启动时:
GRANT RELOAD, LOCK TABLES ON *.* TO 'backup_user'@'localhost'; - 备份完成后立即:
REVOKE RELOAD, LOCK TABLES ON *.* FROM 'backup_user'@'localhost'; - 避免用
GRANT ALL PRIVILEGES——它包含GRANT OPTION,一旦泄露,攻击者可自行扩权 - 所有权限变更必须记录到审计日志,
mysql.general_log或外部 SIEM 系统里得能查到谁、何时、给谁加了什么权限
最小权限不是配一次就完事的事。应用加了个新报表查询,ORM 自动生成了 JOIN information_schema;上线个新微服务,悄悄连了老库的 myapp.audit_log 表——这些都会让原本安全的权限模型瞬间失效。定期跑 SELECT user, host, privilege_type, is_grantable FROM role_edges JOIN roles USING(role_id) WHERE ... 类似的权限链分析,比任何配置检查都实在。











