1. 数据库账户最小权限梳理与整改
为什么“最小权限”不能靠感觉判断
业务库上线后,账户权限往往只增不减:开发临时加的 SELECT、运维为排障授的 EXECUTE、外包留下的 ALTER,都会沉淀成长期风险。最小权限梳理的核心不是“把权限改小”,而是先建立一份可核对的账户—对象—权限基线,再对照业务实际访问路径,找出“授了但不用”“用了但授多”的差异,最后生成可回滚的整改脚本。
下面以 MySQL 8.0 的一个订单业务库 shop 为例。库中有 4 类账户:应用连接账户 app_order、报表只读账户 rpt_read、运维账户 ops_dba、历史遗留账户 legacy_dev。我们通过 information_schema 与 mysql 系统库盘点权限,并结合慢查询/审计日志中的实际访问对象做交叉比对。
盘点:从系统表提取账户与权限
MySQL 中账户信息在 mysql.user,对象级权限在 mysql.tables_priv、mysql.columns_priv、mysql.db 等表中。下面查询列出所有非系统账户及其在 shop 库上的权限:
-- 1. 列出所有非 root、非 mysql.session 等系统账户
SELECT user, host, account_locked, password_expired
FROM mysql.user
WHERE user NOT IN ('root','mysql.sys','mysql.session','mysql.infoschema')
ORDER BY user, host;
-- 2. 查看 shop 库上的库级权限
SELECT user, host, db, select_priv, insert_priv, update_priv,
delete_priv, create_priv, drop_priv, alter_priv
FROM mysql.db
WHERE db = 'shop';
-- 3. 查看 shop 库上的表级权限
SELECT user, host, db, table_name, table_priv
FROM mysql.tables_priv
WHERE db = 'shop';
-- 4. 查看列级权限(如有)
SELECT user, host, db, table_name, column_name, column_priv
FROM mysql.columns_priv
WHERE db = 'shop';
执行后得到如下盘点结果(示例数据):
| 账户 | 主机 | 权限范围 | 权限内容 | 实际业务访问 |
|---|---|---|---|---|
| app_order | 10.0.1.% | shop.orders | SELECT, INSERT, UPDATE | INSERT/UPDATE orders |
| rpt_read | 10.0.2.% | shop.* | SELECT | SELECT orders, products |
| ops_dba | 10.0.3.% | *.* | ALL PRIVILEGES | DDL 维护、备份 |
| legacy_dev | % | shop.* | SELECT, ALTER, DROP | 无访问记录 |
比对:识别超出业务需要的权限
将盘点结果与实际访问记录逐条比对,得到三类差异:
- 授了但不用:
legacy_dev拥有ALTER、DROP,但审计日志中无任何访问记录,且该账户对应人员已离职,应直接回收或锁定。 - 用了但授多:
app_order只有shop.orders的 SELECT/INSERT/UPDATE,业务实际只做 INSERT/UPDATE,SELECT 可保留用于订单查询,但需确认是否有查询需求;若没有,可回收 SELECT。 - 范围过大:
rpt_read拥有shop.*的 SELECT,实际只查 orders 和 products,可缩小到具体表。
注意:回收权限前必须确认业务无依赖。建议先在测试库执行 REVOKE,观察一个完整业务周期(如 24 小时)无报错后再上生产。
整改:生成最小权限脚本
根据比对结果,生成如下整改脚本。脚本按“先回收、后验证、再固化”的顺序组织,并保留回滚语句:
-- ===== 整改脚本(在业务低峰期执行) =====
-- 1. 回收 legacy_dev 全部权限并锁定账户
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'legacy_dev'@'%';
ALTER USER 'legacy_dev'@'%' ACCOUNT LOCK;
-- 2. 缩小 rpt_read 权限范围到具体表
REVOKE SELECT ON shop.* FROM 'rpt_read'@'10.0.2.%';
GRANT SELECT ON shop.orders TO 'rpt_read'@'10.0.2.%';
GRANT SELECT ON shop.products TO 'rpt_read'@'10.0.2.%';
-- 3. 确认 app_order 是否需要 SELECT(若不需要则回收)
-- REVOKE SELECT ON shop.orders FROM 'app_order'@'10.0.1.%';
-- 4. 刷新权限
FLUSH PRIVILEGES;
-- ===== 回滚脚本(如业务异常) =====
-- GRANT SELECT, ALTER, DROP ON shop.* TO 'legacy_dev'@'%';
-- ALTER USER 'legacy_dev'@'%' ACCOUNT UNLOCK;
-- GRANT SELECT ON shop.* TO 'rpt_read'@'10.0.2.%';
验证:确认整改后业务正常
整改后需从三个层面验证:
- 权限验证:用
SHOW GRANTS FOR 'app_order'@'10.0.1.%';确认权限已收敛。 - 业务验证:观察应用日志和数据库错误日志,确认无
Access denied报错。 - 基线固化:将整改后的权限导出为基线文件,纳入版本管理,后续变更需走审批。
建议每月执行一次权限盘点,将结果与基线对比,发现漂移立即告警。可将上述查询封装为存储过程或脚本,定时输出差异报告。