mysql如何限制用户只读权限_mysql最小权限实践

来源:这里教程网 时间:2026-02-28 20:36:09 作者:

只给 SELECT 权限是否真能保证只读?

不能直接只授

SELECT
就完事。MySQL 的只读控制有两层:显式权限(GRANT)和隐式行为(如事务、临时表、存储过程调用)。即使用户只有
SELECT
,若拥有
EXECUTE
或能访问含
SELECT ... FOR UPDATE
的视图,仍可能间接触发写操作或锁表。

实操建议:

显式回收所有非必要权限:
REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, TRIGGER, CREATE VIEW, SHOW VIEW, EXECUTE ON *.* FROM 'user'@'%';
避免授予
USAGE
以外的全局权限(
ON *.*
),必须按库粒度授权,例如
ON `app_db`.*
确认用户无
PROCESS
SUPER
权限——它们可绕过部分只读限制

如何防止用户通过视图或存储过程越权写入?

MySQL 视图默认以定义者(DEFINER)权限执行,但若视图定义者是高权限账号,且用户有

EXECUTE
SELECT
视图权限,就可能借视图执行底层写操作(比如视图基于
INSERT ... SELECT
的 MERGE 算法,或包含触发器逻辑)。

安全做法:

创建视图时强制指定低权限
DEFINER = 'readonly_user'@'%'
,并确保该用户本身无写权限
禁用
SQL SECURITY DEFINER
,改用
SQL SECURITY INVOKER
,让视图执行时校验调用者权限
对生产环境视图,用
SHOW CREATE VIEW view_name
检查是否含
FOR UPDATE
、子查询含
INSERT/UPDATE
或依赖函数(如
UUID_SHORT()
可能触发系统表写入)

SET SESSION TRANSACTION READ ONLY 有用吗?

这个语句只对**当前会话**生效,且仅阻止显式 DML(

INSERT
/
UPDATE
/
DELETE
),不防 DDL、临时表、用户变量赋值,也不影响其他会话。它无法替代权限控制,仅适合应用层主动声明语义,不能作为安全边界。

典型误用场景:

在连接池初始化 SQL 中执行
SET SESSION TRANSACTION READ ONLY
—— 多数连接池(如 HikariCP)不保证每次获取连接后重置会话状态
依赖它防御恶意 SQL 注入 —— 攻击者可先执行
SET SESSION TRANSACTION READ WRITE
再写入
未配合
read_only=ON
全局参数 —— 单纯会话级设置对复制线程、root 用户无效

最小权限组合推荐(生产可用)

真正最小、可控、可审计的只读用户权限不是“只给 SELECT”,而是明确限定对象 + 显式禁止副作用操作。以下为经过验证的 GRANT 模板:

CREATE USER 'report_user'@'192.168.1.%' IDENTIFIED BY 'strong_pass';
GRANT SELECT ON `sales_db`.* TO 'report_user'@'192.168.1.%';
GRANT SELECT ON `log_db`.`daily_summary` TO 'report_user'@'192.168.1.%';
GRANT SHOW VIEW ON `sales_db`.* TO 'report_user'@'192.168.1.%';
FLUSH PRIVILEGES;

关键点:

不授
USAGE
以外的全局权限(
ON *.*
不授
EXECUTE
,除非明确需要调用特定只读函数(如
MD5()
)且已验证函数无副作用
若需
SELECT
系统表(如
information_schema.TABLES
),单独授权,避免
SELECT ON *.*
定期用
SELECT * FROM mysql.tables_priv WHERE User='report_user';
核对实际权限表,防止 GRANT 覆盖遗漏

最易被忽略的是:MySQL 8.0+ 的角色(ROLE)机制虽方便批量管理,但

SET ROLE
可动态切换权限,若未禁用(
SET PERSIST role_restrictions = ON
),仍存在运行时提权风险。

相关推荐