直接给外包开MySQL账号即使仅授SELECT权限仍不安全,因SHOW DATABASES默认开启且information_schema可读,致库名、表结构等元数据泄露;应改用视图封装+显式授权+禁用SHOW DATABASES+IP/密码/连接数限制,并验证权限效果。

直接给外包人员开 MySQL 账号,哪怕只给 SELECT,也大概率暴露不该看的库名、表结构甚至系统元数据——这不是权限设窄了,是边界根本没划清。
为什么只用 GRANT SELECT ON db_name.* 仍不安全
MySQL 的权限模型里,SELECT 权限本身不控制“能看到哪些库”,而依赖 SHOW DATABASES 权限和 information_schema 的可读性。外包连上后执行 SHOW DATABASES,立刻看到所有库名;查 information_schema.TABLES,就能推断出表字段、索引、甚至注释。
- 默认情况下,只要用户能登录,
SHOW DATABASES就是开启的(除非显式REVOKE SHOW DATABASES ON *.* FROM 'user'@'%') -
information_schema是全局只读库,但TABLES、COLUMNS、STATISTICS这几张表一旦可查,等于把整个数据库的骨架摊开 - 某些旧版 Navicat 或 DBeaver 会自动执行
SELECT COUNT(*) FROM information_schema.COLUMNS来渲染表结构,权限一开,它们真就去查了
用视图封装 + 显式授权,切断元数据泄露链
真正隔离数据可见范围的方式,不是靠“不让他们查系统表”,而是让他们**根本不需要查**——把需要的数据提前投影成视图,只授这个视图的 SELECT,并禁掉所有其他访问入口。
- 先创建视图,限定字段和行:
CREATE VIEW `project_x_vw_orders` AS SELECT id, order_no, status, created_at FROM project_x.orders WHERE status IN ('paid', 'shipped'); - 删掉原表权限(如果之前已授):
REVOKE SELECT ON project_x.orders FROM 'outsourcer'@'%'; - 只授视图权限:
GRANT SELECT ON project_x.project_x_vw_orders TO 'outsourcer'@'%'; - 显式关闭元数据暴露:
REVOKE SHOW DATABASES ON *.* FROM 'outsourcer'@'%';
注意:视图必须在目标库下创建(如 project_x),且不能跨库引用未授权的表,否则授权会失败。
配套限制:IP、密码有效期、连接数一个都不能少
视图解决的是“能看什么”,但“谁能在哪连、连多久、连几次”得靠账号层硬约束,否则视图再干净也没用。
- 创建账号时绑定 IP 段比
%安全得多:CREATE USER 'outsourcer'@'203.124.55.%' IDENTIFIED BY 'p' PASSWORD EXPIRE INTERVAL 30 DAY; - 资源限制防滥用:
WITH MAX_QUERIES_PER_HOUR 200 MAX_USER_CONNECTIONS 2 - 绝对不要授予
FILE、SUPER、PROCESS等全局权限,哪怕只读账号 - 8.0.19+ 才支持
PASSWORD EXPIRE INTERVAL,低于此版本只能靠人工巡检清理过期账号
验证是否真的“只读且受限”
别信配置完就 OK,要亲自用外包账号登录后跑几条关键命令,看报错是否符合预期:
-
SHOW DATABASES;→ 应返回空或仅显示被授权的库名(取决于是否REVOKE SHOW DATABASES) -
SELECT * FROM information_schema.TABLES LIMIT 1;→ 应报ERROR 1142 (42000): SELECT command denied -
SELECT * FROM project_x.orders;→ 应报拒绝,而SELECT * FROM project_x.project_x_vw_orders;可成功 -
INSERT INTO project_x_vw_orders VALUES ();→ 视图默认不可写,应报错
真正的难点不在建视图,而在厘清“外包到底需要哪几列、哪几行、从哪个时间点起”,这些业务规则一旦模糊,视图就容易变成新漏洞的温床。











