必须用独立数据库+独立账号,共库授表权限完全失效;因information_schema.tables暴露所有表名,且表级权限无法实现行/字段隔离,跨库直查更会彻底泄露数据,唯一安全方案是每业务独占库与账号、严格限定host、权限git化管理并实测验证。

必须用独立数据库 + 独立账号,不能共库授表权限
共用一个数据库但给不同业务账号授不同表权限(比如 GRANT SELECT ON db1.orders 和 GRANT SELECT ON db1.users),看似隔离,实则完全失效。根本原因是:information_schema.tables 会暴露所有表名,只要应用出错、日志泄露或 SQL 注入,攻击者就能枚举出其他模块的表结构;更致命的是,一旦账号有 db1.users 的 SELECT 权限,它执行 SELECT * FROM users 就能全量读取——跨表授权不等于跨行隔离,权限粒度止于表,不是字段或行。
正确做法只有一条:每个业务线独占一个库、一个账号,且账号 host 必须写死网段(如 'app_order'@'10.20.30.%'),严禁 '%'@'%'。
CREATE DATABASE db_order CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;CREATE USER 'app_order'@'10.20.30.%' IDENTIFIED BY 'strong_pwd_2026';GRANT SELECT, INSERT, UPDATE ON db_order.* TO 'app_order'@'10.20.30.%';- 不加
DELETE、DROP、GRANT OPTION,除非业务真需要软删或动态建表
跨库查数据?只能走视图或 API,绝不能直授 SELECT ON other_db.*
订单服务要显示用户昵称,不代表它该知道 db_user 里有 password_hash 或 phone 字段。直接授 SELECT ON db_user.* 就等于把整个用户库拱手交出。
安全路径只有两条:
- 优先走 HTTP API:
/api/v1/users/{id}/basic,由用户服务控制字段和权限,订单服务只消费 JSON - 低延迟强耦合场景才用视图,且视图必须建在本库(
db_order),限定字段和行:CREATE VIEW order_user_profile AS SELECT id, nickname, avatar_url FROM db_user.users WHERE status = 'active'; - 然后只授视图权限:
GRANT SELECT ON db_order.order_user_profile TO 'app_order'@'10.20.30.%'; - 严禁视图
DEFINER = 'root'@'%',否则低权限账号可借 root 身份执行任意逻辑
权限变更必须 Git 化 SQL 脚本,禁止 Navicat 点点点
线上环境任何权限调整,都不允许在终端手敲 GRANT,也不许用图形工具点选授权。人肉操作无法追溯、不可回滚、极易漏掉 REVOKE 旧权限。
必须写成带注释的 SQL 文件提交 Git,例如:20260613_app_order_add_export_permission.sql,内容含:
REVOKE SELECT ON db_order.export_log FROM 'app_order'@'10.20.30.%'; GRANT SELECT ON db_order.export_log TO 'app_order'@'10.20.30.%'; -- WHY: 订单导出功能上线,需读取导出任务状态,不开放写权限
发布时统一执行脚本,DBA 审核通过后方可上线。
验证权限是否生效?登录后实测三步法
很多人执行完 GRANT 就以为完事了,结果应用连不上或报 Access denied for user。关键细节全在运行时行为里。
务必用业务账号实测:
-
mysql -u app_order -p -h 10.20.30.5登录,确认能连 -
SHOW DATABASES;—— 应只列出db_order(其他库名可能显示但无法访问,这是正常行为) -
USE db_user;—— 必须报错ERROR 1044 (42000): Access denied -
SELECT * FROM users LIMIT 1;在db_order下应成功;DROP TABLE users;必须报ERROR 1142 (42000)
真正容易被忽略的是 host 绑定和 FLUSH PRIVILEGES 的时机:MySQL 5.7+ 多数 GRANT 操作自动刷新权限,但如果你手动改过 mysql.user 表,就必须显式执行 FLUSH PRIVILEGES,否则变更不生效。











