只读账号必须显式授予SELECT权限并彻底回收所有写权限,否则“只读”形同虚设;read_only参数对账号权限无约束力,验证必须通过实际执行INSERT等操作报错确认。

只读账号不能靠 read_only 参数兜底,必须显式授 SELECT 权限并回收所有写权限,否则“只读”形同虚设。
只读账号必须只授 SELECT,别用 GRANT ALL 再回收
MySQL 的权限模型是叠加的,GRANT ALL ON db.* 会一次性赋予包括 INSERT、UPDATE、DROP 在内的全部权限;之后即使 REVOKE INSERT, UPDATE,也容易漏掉 CREATE、ALTER、INDEX 等隐式写权限项。
- 正确做法:创建用户后,**只执行
GRANT SELECT ON `db_name`.* TO 'user'@'host';**(注意数据库名用反引号包裹) - 紧接着回收全局写权限:
REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES ON *.* FROM 'user'@'host'; - 检查结果:
SHOW GRANTS FOR 'user'@'host';输出中应仅含SELECT相关语句,无任何 DML/DDL 权限 - 别忽略
FLUSH PRIVILEGES;——尤其在旧版本或权限缓存未自动刷新时,这步不可省
read_only = ON 对只读账号完全无效,别把它当保险
read_only 是服务端全局开关,只限制连接用户的「操作类型」,不改变账号本身的权限。一个只有 SELECT 权限的账号,哪怕 read_only = OFF,也无法执行 DELETE;反之,若账号误授了 DROP 权限,read_only = ON 也拦不住它删表——因为该参数默认只对非 SUPER 用户起作用,而权限检查发生在更底层。
- 验证只读是否真生效,唯一可靠方式是:用该账号登录,执行
INSERT INTO t VALUES (1);—— 应报错ERROR 1142 (42000): INSERT command denied -
read_only和账号权限是两套机制,混用反而增加误判风险。生产环境应以账号权限为第一道防线 - 临时表(
CREATE TEMPORARY TABLE)和系统表(如mysql.general_log)不受read_only限制,但受账号权限约束——所以只授SELECT能天然封住这些路径
限制访问来源 + 精确到库/表级授权,防横向越权
只读账号若允许从任意 IP 连接('user'@'%'),或授予 *.* 权限,可能无意暴露敏感库(如 mysql、information_schema)甚至跨库查询能力。
- 缩小主机范围:
'user'@'192.168.10.%'比'user'@'%'安全得多 - 避免
GRANT SELECT ON *.*—— 即使业务需要查多个库,也应逐个显式授权:GRANT SELECT ON `sales_db`.* TO ...、GRANT SELECT ON `reporting_db`.* TO ... - 如只需查几张表,直接授表级权限:
GRANT SELECT ON `app_db`.`users` TO ...、GRANT SELECT ON `app_db`.`orders` TO ... - MySQL 8.0+ 可配合角色(
ROLE)统一管理:CREATE ROLE readonly_role;→GRANT SELECT ON db.* TO readonly_role;→GRANT readonly_role TO 'user'@'host';,便于批量维护
别忽略隐式写操作:SELECT ... FOR UPDATE 和函数调用
表面看是 SELECT,实际可能触发写行为。例如:
-
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE需要SELECT+ 表锁权限,但默认只读账号无锁权限,会直接报错——这是好事,说明权限控制起效了 - 若账号被额外授予
LOCK TABLES,又调用含写逻辑的存储函数(如函数内部执行INSERT INTO log_table),则仍可能污染数据 -
SELECT ... INTO OUTFILE本身不写表,但若搭配触发器或 UDF,也可能间接触发写入——这类场景需审计函数定义,而非依赖账号权限 - 真正安全的只读账号,应确保
SHOW GRANTS中不出现LOCK TABLES、EXECUTE(除非明确需要调用只读函数)、FILE等高危权限
最易被忽略的一点:权限配置后没验证真实连接行为,而是只查 @@global.read_only 就以为万事大吉。只读账号的安全性,最终取决于它用什么权限连进来、能执行什么语句——不是服务器开了什么开关。


















