前事不忘,后事之师,不忘国耻!

 用户注册  找回密码
 用户注册
搜索
查看: 32|回复: 0

数据库账号权限治理实战:从共享账号到最小权限与审计

[复制链接]

数据库账号权限治理实战:从共享账号到最小权限与审计

[复制链接]
dbaai

主题

0

回帖

349

积分

DBAAI

积分
349
昨天 05:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?用户注册

×
数据库账号权限治理实战:从共享账号到最小权限与审计


一、具体的问题

接手一套跑了五六年的生产库,账号现状通常是这样的:mysql.user 里挂着四十多个账号,其中十几个是「app」「dev」「test」这类没人认领的共享账号;开发同学人手一份同一个写账号的密码,三年没换过;DBA 自己日常用 root 直连生产,顺手改表、重启实例;有人离职了,密码还在他个人微信收藏里躺着。出了事故想复盘是谁改的数据,打开 binlog 一看,User@Host: app[app]——等于什么都没查到。

更麻烦的是这类问题平时完全看不出来:连接正常、查询正常、业务正常。它只在你最需要追责、最需要止血的那一刻暴露——你不知道该冻结哪个账号,也不知道改坏数据的那次连接来自哪台机器、哪个人的操作。权限治理不是安全合规的纸面要求,是让「事后可追责、事中可止血」成立的唯一前提。

二、核心原理

账号体系的本质是一条责任链:每个连接必须能唯一归因到一个人或一个系统。共享账号把这条链直接剪断了——「app 账号干的」既不能定位人,也不能定位用途,审计和追责全部失效。

由此推出三条原则:


  • 最小权限:一个账号只授予完成其职责所必需的权限。写账号不需要 DROP,报表账号只读即可,GRANT ALL ON *.* 只能属于 root 这类极少数的管理账号。
  • 人机分离:人的账号和应用程序的账号必须分开。应用账号权限固定、密码走配置中心轮换;人的账号走个人实名授权,离职即回收。
  • 角色化授权:不给人逐条 GRANT,而是定义 app_dev、app_readonly、app_dba 几个角色,人加入角色、权限随角色走。这样权限收敛有据可查,复查时对角色清单而不是对几十个账号逐个核对。


还有一个反直觉的点:治理的最大阻力往往不是技术,是「改了会不会出事」的担忧。所以整套动作必须可灰度、可回退——先锁定观察、再降权、最后删除,而不是一刀切 DROP USER。

三、实例参考(动手步骤)

以 MySQL 8.0 为主展开,Oracle / SQL Server 的对应动作放在各步骤末尾对照。

步骤 0:确认现状基线

先摸清家底,不要凭印象动手:
  1. -- 账号清单与主机来源
  2. SELECT user, host FROM mysql.user ORDER BY user;
  3. -- 逐个账号的权限快照(输出到文件做台账)
  4. -- shell 里循环执行
  5. mysql -N -e "SELECT CONCAT(user,'@',host) FROM mysql.user" | while read u h; do
  6.   echo "=== $u@$h ==="
  7.   mysql -N -e "SHOW GRANTS FOR '$u'@'$h'"
  8. done > account_grants_$(date +%F).txt
复制代码

再确认哪些是共享账号。两个证据:一是账号名与个人无关(app/dev/test/common);二是连接来源分散——
  1. SELECT user, host, COUNT(*) AS conns
  2. FROM information_schema.processlist
  3. GROUP BY user, host ORDER BY conns DESC;
复制代码

同一个 user 从 10 个不同 host 连进来的,基本就是共享账号。Oracle 对照:SELECT username, account_status, created, last_login FROM dba_users 看 last_login 是否长期为空;SQL Server 对照:sys.server_principals 配合 sys.dm_exec_sessions 按 login_name 聚合。

步骤 1:建账号台账,逐个认领

把基线结果整理成一张表,列固定五个:账号、负责人、用途、权限级别(读写/只读/管理)、最后登录时间。找不到负责人的账号直接标记为待处置,这是后面所有动作的输入。台账放代码仓库里维护,跟配置一样走 review。

步骤 2:逐人建个人账号 + 角色化授权

MySQL 8.0 支持角色,先把角色定义出来:
  1. CREATE ROLE 'app_dev', 'app_readonly', 'app_dba';
  2. -- 开发角色:目标库读写,不给 DROP/ALTER 之外的全局权限
  3. GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app_dev';
  4. -- 只读角色:全业务库 SELECT
  5. GRANT SELECT ON appdb.* TO 'app_readonly';
  6. -- DBA 角色:日常管理,仍不含 super 类高危权限
  7. GRANT ALL ON appdb.* TO 'app_dba';
  8. -- 按人开账号,挂角色
  9. CREATE USER 'zhangsan'@'10.0.%' IDENTIFIED BY '<初始密码>'
  10.   PASSWORD EXPIRE INTERVAL 90 DAY
  11.   FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1;
  12. GRANT 'app_dev' TO 'zhangsan'@'10.0.%';
  13. SET DEFAULT ROLE 'app_dev' TO 'zhangsan'@'10.0.%';
复制代码

三个细节不能省:host 段收紧到办公网/跳板机网段,不要留 %;PASSWORD EXPIRE INTERVAL 90 让密码有生命周期;FAILED_LOGIN_ATTEMPTS 防爆破。Oracle 用 CREATE PROFILE 限 FAILED_LOGIN_ATTEMPTS/PASSWORD_LIFE_TIME 再分配给用户,权限挂 CREATE ROLE;SQL Server 用固定服务器角色/数据库角色 ALTER ROLE app_dev ADD MEMBER [zhangsan]。

步骤 3:应用账号降权收敛

应用账号和人的账号分开后,逐个检查应用账号权限。典型的过度授权:
  1. -- 收敛前:app 账号几乎全权
  2. SHOW GRANTS FOR 'app'@'10.1.%';
  3. -- GRANT ALL PRIVILEGES ON *.* TO ...
  4. -- 收敛后:只给目标库的 DML,DDL 走发布流程的专门账号
  5. REVOKE ALL PRIVILEGES ON *.* FROM 'app'@'10.1.%';
  6. GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'10.1.%';
  7. -- DDL 账号单独建,平时不配在应用连接串里
  8. CREATE USER 'app_ddl'@'10.1.%' IDENTIFIED BY '<密码>';
  9. GRANT ALTER, CREATE, DROP, INDEX, SELECT ON appdb.* TO 'app_ddl'@'10.1.%';
复制代码

报表、BI 这类纯读场景统一切到 app_readonly 角色下的账号。这一步要在业务低峰做,且提前和开发确认没有隐藏的 DDL 依赖(有些框架启动时自动 ALTER TABLE)。

步骤 4:共享账号灰度退役

不要直接 DROP。分三步走:
  1. -- 第一周:改名 + 锁定,观察有没有业务报认证失败
  2. RENAME USER 'dev'@'%' TO 'dev_retired'@'%';
  3. ALTER USER 'dev_retired'@'%' ACCOUNT LOCK;
  4. -- 观察两周无报错后:确认无会话残留
  5. SELECT * FROM information_schema.processlist WHERE user LIKE 'dev%';
  6. -- 最后删除
  7. DROP USER 'dev_retired'@'%';
复制代码

监控账号、备份账号、复制账号这类系统账号容易被误伤,处置前逐个在台账里确认用途。Oracle 对应 ALTER USER dev ACCOUNT LOCK,保留观察期后 DROP USER dev CASCADE。

步骤 5:审计落地,让归因成立

权限收干净后补上「谁连过、谁改了」的记录。MySQL 用审计插件(企业版 audit_log 或社区 mariadb 的 server_audit):
  1. INSTALL SONAME 'server_audit';
  2. SET GLOBAL server_audit_logging = ON;
  3. SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL,QUERY_DML';
  4. SET GLOBAL server_audit_file_rotate_size = 104857600;  -- 100MB 轮转
复制代码

只记 CONNECT + DDL/DML,不记全量 QUERY——全量 DML 的日志量会把磁盘打满,也拖性能。配合 binlog 里的 User@Host,共享账号退役之后,每一次变更都能归因到具体个人或具体系统账号。

步骤 6:月度复查固化

治理不是一次性的。写个脚本每月一号跑:导出账号清单、和台账比对差异、列出 last_login 超过 90 天的账号、列出权限大于所属角色的账号,结果发到群里的告警频道。发现不一致,按台账流程处置,不让新共享账号有存活空间。

治理前后对比

指标治理前治理后
可归因账号占比40%100%
共享写账号6 个0
GRANT ON *.* 的人类账号9 个0
密码超 180 天未换60%0(90 天强制过期)
事故定位耗时无法定位平均 8 分钟(审计日志直查)
离职回收闭环无流程提交离职单次日强制过期


四、实操检查清单


  • 账号全量清单已导出,逐个认领负责人,未认领账号标记待处置;
  • SHOW GRANTS 快照已归档,ON *.* 的人类账号清零;
  • 个人账号 host 段已收紧到指定网段,不存在 'x'@'%' 的实名账号;
  • 应用账号、报表账号、DDL 账号三分离,连接串里无 DDL 账号;
  • 共享账号按「改名锁定 → 观察两周 → 删除」灰度退役,无直接 DROP;
  • 密码策略:90 天过期、连续失败 5 次锁定已生效;
  • 审计插件开启,覆盖 CONNECT 与 DDL/DML,日志轮转配置完毕;
  • 月度复查脚本上线,超 90 天未登录账号自动进告警清单;
  • 复制、备份、监控等系统账号已在台账标注用途并豁免处置;
  • 治理后的权限快照重新归档,与治理前对照留档。


几个容易踩的坑


  • 直接 DROP 共享账号:应用侧重连风暴比权限风险来得更快。先改名锁定观察,是唯一的稳妥路径。
  • REVOKE 误伤系统账号:全局 REVOKE ALL 之前先确认复制账号(REPLICATION SLAVE)、监控账号不在处理范围内,否则主从和告警一起瘫痪。
  • Oracle PROFILE 设过严:给服务账号套了 FAILED_LOGIN_ATTEMPTS 3,应用配置错一次密码就锁定,故障扩大化。PROFILE 分级,服务账号放宽或豁免。
  • 个人账号开完就算治理完:只要 root 密码还在小圈子里传,共享就只是换了个马甲。root 改走堡垒机/代理认证,人不再直接持有 root 密码,治理才算闭环。
  • 审计只开不查:审计日志没人看等于没开。把「事故当天按 user/host 过滤审计日志」写进应急预案,工具才算用起来。
免责申明1、欢迎访问本站,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@dbabbs.com
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

QQ|Archiver|小黑屋|DBA论坛中国 ( 鲁ICP备20017503号-2 )

GMT+8, 2026-10-19 06:45 , Processed in 0.012873 second(s), 9 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表