只授SELECT权限是创建只读账号的唯一可靠方式;必须分两步执行CREATE USER和GRANT SELECT,禁用FILE、LOCK TABLES等危险权限,限定host与库名(用反引号),额外授权INFORMATION_SCHEMA才能执行SHOW CREATE TABLE。

只授 SELECT 权限是创建只读账号的唯一可靠方式,其他“禁写”思路(比如先给全权限再 REVOKE)极易漏掉 TRUNCATE、LOCK TABLES 或 FILE 等隐蔽入口。
必须分两步:先 CREATE USER,再 GRANT SELECT
MySQL 8.0 不允许在一条语句里同时建用户和赋权。常见错误是写成 CREATE USER ... IDENTIFIED BY ... GRANT SELECT ...,会直接报 ERROR 1064 (42000)。
- 先建用户,指定可信 host(别盲目用
'%'):CREATE USER 'analyst_ro'@'192.168.5.0/24' IDENTIFIED BY 'StrongPass!2026'; - 再授权,数据库名必须用反引号包裹(尤其含短横、数字时):
GRANT SELECT ON `sales_2026`.* TO 'analyst_ro'@'192.168.5.0/24'; - MySQL 8.0+ 大多数情况下可省略
FLUSH PRIVILEGES,但若你手动改过mysql.user表,就必须执行
为什么不能用 GRANT SELECT ON *.*?
它会让用户能查 information_schema、performance_schema、sys 这些系统库——前者暴露表结构,后者可能泄露性能指标甚至密码哈希(sys 库某些视图依赖 mysql.user)。
- 如果用户需要执行
SHOW CREATE TABLE,得额外加一句:GRANT SELECT ON INFORMATION_SCHEMA.* TO 'analyst_ro'@'192.168.5.0/24'; - 绝对不要给
mysql库授任何权限;sys库默认也不该开放 - 若业务库命名有规律(如都以
app_prod、app_staging开头),可用角色 + 模式匹配:GRANT SELECT ON `app\_prod`.* TO 'readonly_role';
容易被忽略的危险权限:FILE、CREATE TEMPORARY TABLES
只授 SELECT 并不等于安全。用户仍可能通过 SELECT ... INTO OUTFILE 往服务器写文件,或用临时表构造恶意中间数据。
- 检查是否误授:
SHOW GRANTS FOR 'analyst_ro'@'192.168.5.0/24';,若返回含FILE,立刻收回:REVOKE FILE ON *.* FROM 'analyst_ro'@'192.168.5.0/24'; - 同样建议收回
CREATE TEMPORARY TABLES:REVOKE CREATE TEMPORARY TABLES ON *.* FROM 'analyst_ro'@'192.168.5.0/24'; - 注意:
SELECT ... FOR UPDATE即使只有SELECT权限也会失败,报错ERROR 1142 (42000): SELECT command denied,因为触发了锁机制,需额外LOCK TABLES
验证是否真只读:用实际语句测,别只信 SHOW GRANTS
SHOW GRANTS 只显示显式授予的权限,不反映隐式继承或角色叠加。最稳妥的是登录后实操:
- 成功:
SELECT COUNT(*) FROM `sales_2026`.orders; - 应失败:
INSERT INTO `sales_2026`.orders VALUES (...);(报ERROR 1142) - 应失败:
SELECT * FROM mysql.user;(报ERROR 1142) - 应失败:
SHOW DATABASES;中若出现mysql或sys,说明系统库权限没控住
真正麻烦的不是建账号,而是确保它在所有边界场景下都不越界——比如新表、跨库引用、系统视图、锁操作。这些点一旦漏掉,只读就只是心理安慰。


















