人人都会AI编程

23.3 权限授予与回收、最小权限原则

更新时间:2026-07-11

权限管理的核心目标不是“把门锁上”,而是让每个角色只能做它该做的事。一个合理的权限分配,能在源头上防止误操作、阻止越权访问,甚至在应用层出现 SQL 注入时降低损失。这一节介绍 MySQL 中权限的具体授予与回收方式,以及贯穿始终的最小权限原则。

23.3.1 权限授予:GRANT 语句

MySQL 使用 GRANT 语句为已存在的用户授予权限。基本语法如下:

GRANT 权限列表 ON 权限范围 TO '用户名'@'主机' [WITH 选项];

权限列表可以是 ALL PRIVILEGES(赋予指定级别的全部权限),也可以是一个或多个具体权限,用逗号分隔,比如 SELECT, INSERT, UPDATE。MySQL 的权限种类非常多,从数据操作(SELECTINSERTUPDATEDELETE)到结构管理(CREATEALTERDROP),再到运维管理(SUPERPROCESSSHUTDOWN),覆盖了所有操作需求。常见的权限参见 23.2 节的权限表。

权限范围决定了这个权限作用的层级,它有五种典型写法:

  • . — 全局级别,所有数据库的所有表。
  • db_name.* — 数据库级别,指定库下的所有表。
  • db_name.table_name — 表级别,指定某个表。
  • db_name.table_name.column_name — 列级别,仅作用于指定列(只能是 SELECTINSERTUPDATE 权限)。
  • 存储过程、函数、代理用户等特定对象也有其对应的范围写法。

'用户名'@'主机' 必须精确匹配已有的用户标识,GRANT 不会自动创建用户(除非显式使用 IDENTIFIED BY 子句,但官方已不推荐)。在授予权限之前,需要先用 CREATE USER 创建用户。

WITH 选项 可选,常见的有:

  • WITH GRANT OPTION — 让该用户可以将自己拥有的权限再授予其他用户。这是一把“能够传递权限”的钥匙,需要非常谨慎地发放。
  • MAX_QUERIES_PER_HOURMAX_UPDATES_PER_HOURMAX_CONNECTIONS_PER_HOUR 等资源限制选项,用于限制用户每小时执行的查询次数、更新次数或连接次数。

示例:

-- 创建用户
CREATE USER 'app_user'@'%' IDENTIFIED BY 'strong_password';
-- 授予对 app_db 库的所有权限,并允许将该权限授予他人
GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'%' WITH GRANT OPTION;
-- 授予只读权限,仅限所有库所有表(注意:*.* 是全局级,会覆盖所有数据库)
GRANT SELECT ON *.* TO 'readonly_user'@'%';
-- 授予对特定表的列级权限,只允许查看用户名和邮箱
GRANT SELECT (username, email) ON app_db.users TO 'api_user'@'%';

执行 GRANT 后,最好紧接着执行 FLUSH PRIVILEGES; 让权限生效。不过从 MySQL 5.1 起,直接用 GRANT 已经会自动刷新权限,但执行显式刷新是一个好习惯,特别在使用了 INSERT 直接修改系统表(如 mysql.user)的旧式操作时。

23.3.2 权限回收:REVOKE 语句

权限可以给,就可以收。REVOKE 语法与 GRANT 几乎对称,只是将 GRANT 换成 REVOKE,并且 TO 换为 FROM

REVOKE 权限列表 ON 权限范围 FROM '用户名'@'主机';

示例:

-- 回收 app_user 对 app_db 的 DELETE 权限
REVOKE DELETE ON app_db.* FROM 'app_user'@'%';
-- 回收用户的所有全局权限
REVOKE ALL PRIVILEGES ON *.* FROM 'admin_user'@'localhost';
-- 回收 GRANT OPTION(注意,必须用单独的 REVOKE GRANT OPTION ON ...)
REVOKE GRANT OPTION ON app_db.* FROM 'app_user'@'%';

回收时需要注意几点:

  • 一个用户可能通过多个账户定义(不同主机)拥有权限,回收必须精确匹配 '用户名'@'主机'
  • 权限可以分层级,如果你对全局授予了 SELECT,后来又在某些库上执行了 REVOKE,则需要仔细验证最终效果;有时残留的全局权限仍然使回收不彻底,建议用 SHOW GRANTS FOR '用户名'@'主机' 进行确认。
  • 如果用户拥有 WITH GRANT OPTION,你既可以回收具体权限,也可以单独回收 GRANT OPTION,后者会保留用户自身的权限,仅移除了授权他人能力。

对于已经不再需要某些权限的用户,及时回收是安全维护的关键一步。切忌保留已离职开发者的高权限账户,也不要让测试用户拥有生产环境的写权限。

23.3.3 查看用户权限

授予和回收前后,都需要确认用户当前拥有的确切权限。常用命令:

-- 查看当前登录用户的权限
SHOW GRANTS;
-- 查看指定用户的权限
SHOW GRANTS FOR 'app_user'@'%';

输出类似 GRANT SELECT, INSERT, UPDATE ON app_db.* TO 'app_user'@'%',清晰列出每个级别的权限。这比直接查询 mysql.usermysql.db 等表更直观。

23.3.4 最小权限原则

最小权限原则(Principle of Least Privilege)是安全领域的黄金法则:每个用户(或服务)仅应被授予完成其任务所必需的最小权限集合。这一点在数据库权限管理中尤其重要,因为数据库通常是攻击者最感兴趣的目标。

在 MySQL 中实践最小权限原则,可以从这几个维度着手:

1. 角色隔离,按需分配

不要给所有开发者或应用账号授予 ALL PRIVILEGES ON .。应根据实际使用场景建立清晰的权限角色模型。例如:

  • 应用程序连接账号:通常只需要对业务库进行数据操作(SELECT, INSERT, UPDATE, DELETE),完全不需要 CREATE, DROP, ALTER 等结构修改权限,更不需要 FILESUPER 等系统权限。如果不涉及跨库关联查询,甚至只局限于一个库 app_db.*,减少影响范围。
  • 只读查询账号:用于报表或数据分析类应用,仅授予 SELECT,且只限于特定库或表。如果读取敏感字段(如用户手机号、密码哈希),可进一步细化到列级别。
  • 运维管理账号:分配给 DBA 或自动化脚本,拥有所有全局管理权限。但要严格限制登录主机(如仅限 localhost 或特定管理网段),并启用强密码和审计。
  • 开发测试账号:可以使用独立的测试实例,或在生产库上仅授予读取权限;绝对禁止授予 DROPALTER 等危险权限。

2. 限定访问来源

'用户名'@'主机' 中的主机部分也是最小权限的一部分。应用程序账号如果只在特定应用服务器上运行,就指定具体的 IP 或 IP 段,比如 'app_user'@'192.168.10.101',而不是 'app_user'@'%'。即使账号密码泄露,攻击者从其他机器也无法连接。

3. 区分读写、结构修改与管理权限

常见误区是“为了方便”,把 ALL PRIVILEGES 一把抛给应用账号。结果一个 SQL 注入就被执行了 DROP TABLE。应该只给应用账号 CRUD 权限,对数据表结构的变动(如添加索引、修改字段)统一通过 DBA 或部署系统进行。这样即使应用层被注入,攻击者也只能在数据层面读写,无法清库。

4. 定期审计与回收

权限不是一劳永逸的。项目初期可能图方便给了很多宽松权限,随着系统稳定,应该逐步回收多余权限。例如,数据迁移完成后,回收 FILE 权限;不再需要持有 SUPER 的账号,及时收回。

建议定期执行以下检查:

  • 列出拥有全局权限(.)的用户,确认是否必要。
  • 找出密码过期或长期未活跃的用户并禁用。
  • 检查 GRANT OPTION 是否被不当分配。

5. 善用 MySQL 8.0 的角色(Role)功能

MySQL 8.0 引入了角色,使得权限管理更加模块化。你可以先创建角色,再把角色授予用户。

CREATE ROLE 'app_read', 'app_write';
GRANT SELECT ON app_db.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';
-- 将只读角色授予只读账号,将读写角色授予业务账号
GRANT 'app_read' TO 'report_user'@'%';
GRANT 'app_read', 'app_write' TO 'app_user'@'%';

这样当权限变更时,只需修改角色,所有拥有该角色的用户自动继承变动,降低管理成本,也更易贯彻最小权限原则。

总结

权限授予与回收操作并不复杂,真正考验的是管理意识:在你给出一个权限之前,先问自己“这个账号真的需要这个权限吗”。养成这样的习惯,比任何复杂的安全插件都更有效。最小权限原则不是一根筋地拒绝所有权限,而是让每个权限都有其明确且必要的理由,这才是生产环境数据库安全的第一道闸门。