1. 数据库账户最小权限梳理与整改

数据库账户最小权限梳理与整改 学习卡片

为什么“最小权限”不能靠感觉判断

业务库上线后,账户权限往往只增不减:开发临时加的 SELECT、运维为排障授的 EXECUTE、外包留下的 ALTER,都会沉淀成长期风险。最小权限梳理的核心不是“把权限改小”,而是先建立一份可核对的账户—对象—权限基线,再对照业务实际访问路径,找出“授了但不用”“用了但授多”的差异,最后生成可回滚的整改脚本。

下面以 MySQL 8.0 的一个订单业务库 shop 为例。库中有 4 类账户:应用连接账户 app_order、报表只读账户 rpt_read、运维账户 ops_dba、历史遗留账户 legacy_dev。我们通过 information_schema 与 mysql 系统库盘点权限,并结合慢查询/审计日志中的实际访问对象做交叉比对。

账户权限盘点与最小权限整改关系图 账户层 app_order 应用连接账户 rpt_read 报表只读账户 ops_dba 运维账户 legacy_dev 历史遗留账户 整改后目标 仅保留必要权限 当前权限(盘点结果) SELECT, INSERT, UPDATE ON shop.orders SELECT ON shop.* ALL PRIVILEGES ON *.* SELECT, ALTER, DROP ON shop.* 按业务路径裁剪 保留最小集合 实际业务访问 INSERT orders UPDATE orders SELECT orders SELECT products DDL 维护 备份/恢复 无实际访问记录 (已离职) 验证通过 业务无中断 差异识别 app_order 缺少 DELETE 但业务不需要 → 无需补授 legacy_dev 有 ALTER/DROP 但无访问记录 → 应回收 整改动作:REVOKE 多余权限 → 验证业务 → 记录基线 → 定期复核

盘点:从系统表提取账户与权限

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_order10.0.1.%shop.ordersSELECT, INSERT, UPDATEINSERT/UPDATE orders
rpt_read10.0.2.%shop.*SELECTSELECT orders, products
ops_dba10.0.3.%*.*ALL PRIVILEGESDDL 维护、备份
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.%';

验证:确认整改后业务正常

整改后需从三个层面验证:

  1. 权限验证:用 SHOW GRANTS FOR 'app_order'@'10.0.1.%'; 确认权限已收敛。
  2. 业务验证:观察应用日志和数据库错误日志,确认无 Access denied 报错。
  3. 基线固化:将整改后的权限导出为基线文件,纳入版本管理,后续变更需走审批。
建议每月执行一次权限盘点,将结果与基线对比,发现漂移立即告警。可将上述查询封装为存储过程或脚本,定时输出差异报告。