SQL Server数据库加固:账号口令基线与权限最小化检查表
简介面向数据库管理员、安全运维与等保测评人员的 Sql Server 安全加固规范文档针对账号权限过大、认证策略薄弱、日志缺失和传输链路暴露等常见风险给出可对照执行的安全基线。压缩包内为 1 个 doc 文件约 1.73MB按 SHG-Mssql 编号体系分四类组织检查项逐条列明实施目的、问题影响、系统当前状态核查、配置步骤、回退方案、判断依据、实施风险与重要等级便于落地整改和审计留痕。内容覆盖为不同管理员分配独立账号、删除或锁定无效账号、限制启动账号权限、密码长度与有效期、登录失败锁定、双因素认证、数据库操作日志记录、SSL/TLS 传输加密、访问权限限制、防火墙与入侵检测以及防病毒软件部署和定期安全检查更新等要求。已有 245 人学习可作为 SQL Server 基线核查、安全加固和整改复盘的参考手册。1. 从一份带编号的检查表说起接手过一套跑了六年的 SQL Server交接文档三页纸其中一页是这份《Sql Server 数据库系统加固规范》的目录截图。它真正好用的地方不在技术深度而在组织方式每个加固点一个编号SHG-Mssql-01-01-01 这种编号下固定九栏——实施目的、问题影响、系统当前状态、实施步骤、回退方案、判断依据、实施风险、重要等级、备注。整套 SqlServer 加固规范分四块账号管理与认证授权、日志配置、通信协议、设备其他安全要求合计十几条。它不教你怎么装库只回答一个问题这台库现在离「能过审计」还差哪几步。适合手上有生产库、被要求出加固报告又不知从哪下手的人也适合做整改的运维拿去当检查表。下面按条目往下拆给能直接抄的 SQL 和参数。2. 账号盘点与口令基线从 syslogins 到 sys.sql_logins 的三步替换2.1 盘点语句要换掉syslogins 已经在多数实例上失效规范第一条 SHG-Mssql-01-01-01 要求「为不同的管理员分配不同的账号」判断依据写的是「询问管理员是否按需分配用户账号」系统当前状态给的是use master; Select name,password from syslogins order by name。这句话在 SQL Server 2000 上没问题放到 2005 以后的实例上syslogins作为兼容视图还在但password字段取出来是一串不可读的二进制靠它判断口令状态等于白看。我一般直接换成sys.server_principals和sys.sql_logins的组合查询一次把类型、启用状态、策略套用情况全拉出来。USE master; GO -- 1) 登录名全景类型、是否禁用、是否套用密码策略、创建与改密时间 SELECT p.name, p.type_desc, -- SQL_LOGIN / WINDOWS_LOGIN / WINDOWS_GROUP p.is_disabled, p.create_date, p.modify_date, sl.is_policy_checked, -- 是否继承主机/域密码策略 sl.is_expiration_checked -- 是否跟踪口令有效期 FROM sys.server_principals AS p LEFT JOIN sys.sql_logins AS sl ON p.principal_id sl.principal_id WHERE p.type IN (S,U,G) -- 排除证书、密钥、服务器角色 ORDER BY p.type_desc, p.name;这段的逻辑是先从服务器级主体表取全部登录再左连 SQL 登录专有表补上策略字段。type IN (S,U,G)这个过滤条件很关键不加会把CERTIFICATE、ASYMMETRIC_KEY这些非登录主体也带出来清单一下膨胀两倍后面交接时对方根本看不懂。is_disabled 1的是被禁用的登录通常就是规范第二条要清理的对象is_policy_checked 0的 SQL 登录是重点它意味着这个账号的密码长度、复杂度、锁定阈值完全不受主机策略约束设成六位纯数字也能过。有一个坑要提前说password_hash为 NULL 不等于空口令。Windows 登录本来就没有口令哈希之前有同事写了WHERE password_hash IS NULL去筛空口令账号结果把一排名正言顺的 Windows 账号全标成了高危项报告交上去被退回来重做。真正的空口令检测在 2.3 节。2.2 删无效账号之前先确认三件事规范第二条 SHG-Mssql-01-01-02 是「删除或锁定无效账号」原文给的路径是企业管理器里右键删除实施风险标「高」。风险等级标得没错删除登录不可逆而且删掉的可能是一个应用的连接串里写死的账号第二天业务就起不来。我一般按三件事确认完再动手。第一件最近有没有登录。sys.dm_exec_sessions只能看到当前会话不是登录审计别拿它当依据。要判断「最近三十天有没有活动」得靠已经打开的登录审核或者 Server Audit 的输出否则只能凭应用的连接串和负责人确认。第二件这个登录是不是某个对象或作业的属主。这一点最容易被跳过也最容易出事。-- 2) 找孤儿风险对象属主、作业属主挂在待删登录上的 SELECT SCHEMA_OWNER AS ref_type, s.name AS ref_name, p.name AS owner_name FROM sys.schemas AS s JOIN sys.server_principals AS p ON s.principal_id p.principal_id WHERE p.type IN (S,U) UNION ALL SELECT AGENT_JOB, j.name, p.name FROM msdb.dbo.sysjobs AS j JOIN sys.server_principals AS p ON j.owner_sid p.sid WHERE p.type IN (S,U);逻辑说明schema 的属主在服务器级主体表里记的是principal_id作业的属主记的是owner_sid所以两段查询的连接键不一样别图省事统一写principal_id。查出来的结果里如果某个 schema 的属主是待删登录删完之后这个 schema 下的对象访问会出问题需要先ALTER AUTHORIZATION ON SCHEMA::xxx TO dbo转移如果某个 Agent 作业的属主是待删登录作业会变成孤儿图形界面里显示「无法确定」跑起来报权限错误需要先sp_update_job job_name Nxxx, owner_login_name Nsa改属主。第三件先禁用观察别直接删。-- 3) 先禁用观察一到两周确认无影响再 DROP ALTER LOGIN [old_app_user] DISABLE; -- 观察期结束后确认无误再执行 -- DROP LOGIN [old_app_user];禁用是可逆的删除不是。这套流程看着慢但比业务方半夜打电话来问「为什么连不上数据库」要省事得多。规范原文把删除和锁定并列实操里我几乎总是选锁定。2.3 空口令与强口令PWDCOMPARE 配合 CHECK_POLICY规范第六条 SHG-Mssql-01-01-06 的判断语句是select name,Password from syslogins where password is null修改口令用exec sp_password 旧口令,新口令,用户名。这两句都得换sp_password在 2012 以后已经被标记为弃用新写法是ALTER LOGIN。空口令检测用PWDCOMPARE它拿明文和哈希做比对返回 1 表示匹配不改数据、不写日志做核查是安全的。-- 4) 空口令检测空串命中即为空口令只对 SQL 登录有效 SELECT name, is_disabled, is_policy_checked FROM sys.sql_logins WHERE PWDCOMPARE(, password_hash) 1;然后是口令本身。规范原文只要求「sa 账号至少 10 位强口令」这个下限放在今天偏松。实操里我按 12 位以上、大小写字母数字符号四类里至少占三类、90 天有效期、连续 5 次失败锁定来配多数内控口径能对上。-- 5) 改口令并套用策略CHECK_POLICY 打开后继承主机的长度/复杂度/锁定策略 ALTER LOGIN [app_user] WITH PASSWORD N替换成实际强口令, CHECK_POLICY ON, CHECK_EXPIRATION ON; -- 交付给应用方时强制首次登录改密 ALTER LOGIN [app_user] WITH MUST_CHANGE;参数说明CHECK_POLICY ON才让这个登录继承主机或域的密码策略CHECK_EXPIRATION ON才会跟踪有效期两个是独立的开关只开前一个的话口令永不过期。这里有个边界要知道这两个开关依赖主机的安全策略如果主机策略被放宽成「密码永不过期、无复杂度要求」数据库这层也跟着松它约束的是「跟主机一致」不是「一定够强」。真要卡死得在应用侧或者定期核查脚本里补。sa账号单独处理-- 6) sa先设强口令再决定是否禁用 ALTER LOGIN [sa] WITH PASSWORD N替换成实际强口令, CHECK_POLICY ON; ALTER LOGIN [sa] DISABLE;注意禁用 sa 之前先确认实例上至少有两个可用的 sysadmin 成员其中一个必须是能登录的 Windows 账号。单用户模式下只有 sysadmin 能进如果 sysadmin 里只剩一个被禁用的 sa这个实例就只能靠重启进单用户模式救了。3. 权限最小化的三层收口服务启动账号、服务器角色与数据库角色3.1 服务启动账号SQLRunAs 为什么不能待在管理员组规范第三条 SHG-Mssql-01-01-03 讲的是启动账号权限过高的问题原话是「启动 mssql 的账号权限过高会导致其子进程具有相同权限」。这句话是整个加固清单里性价比最高的一条因为服务账户的权限是 SQL Server 所有外联操作的天花板——xp_cmdshell执行的命令、BULK INSERT读的文件、备份写出去的目录权限都来自这个账户。服务账户是本地管理员那么从数据库里打出去的任何一条命令也是管理员权限。所以加固顺序上先降服务账户再谈别的。# 7) 核对 SQL Server 各服务的登录身份 Get-CimInstance Win32_Service -Filter Name LIKE MSSQL% | Select-Object Name, StartName, State # 8) 核对专用账户落在哪些本地组里 $acct DOMAIN\svc_sqlrunas Get-LocalGroup | ForEach-Object { $g $_ try { $hit Get-LocalGroupMember -Group $g.Name -ErrorAction Stop | Where-Object { $_.Name -eq $acct } if ($hit) { [pscustomobject]{ Account $acct; Group $g.Name } } } catch { } }StartName就是服务的登录身份。如果输出是LocalSystem、NetworkService或者某个域管理员账号说明还没做这一层。Get-LocalGroupMember逐组遍历的写法笨但兼容性好Administrators、Users、Performance Monitor Users这几个关键组都能覆盖到。规范原文写的是「新建 SQL server 服务账号后建议将其从 User 组中删除且不要把该账号提升为 Administrators 组的成员」。前半句放在 SQL Server 2000 那个年代的 Windows 上成立放到现在的版本要打个折扣现代 SQL Server 服务账户需要「作为服务登录」「替换进程级令牌」「绕过遍历检查」「管理审核和安全日志」这几项权限配置管理器在改服务身份时会自动补齐如果手工把账户从 Users 组摘掉某些依赖 Users 组默认 ACL 的目录比如备份目录、错误日志目录可能读不了排查起来很费时间。我的做法是保留 Users 组、坚决不进 Administrators再单独对数据文件、日志文件、备份目录做 ACL 收紧——只给这个账户必要的目录权限比硬摘组更可控。注意改服务登录身份一定用 SQL Server 配置管理器不要用 services.msc。配置管理器会顺手把上面那几项权限配好用服务管理器改很可能因为缺「作为服务登录」权限起不来报错误 1069。改完之后重启实例再跑一遍第 7 段脚本确认StartName已经变了这一步没有捷径。3.2 服务器角色sysadmin 之外还有几个准管理员规范第四条 SHG-Mssql-01-01-04「权限最小化」和第五条「数据库角色」讲的是同一件事的两层。服务器角色这一层绝大多数人只盯sysadmin实际要一起看的至少有四个。-- 9) 高权服务器角色成员清单 SELECT r.name AS role_name, m.name AS member_name, m.type_desc, m.is_disabled FROM sys.server_role_members AS rm JOIN sys.server_principals AS r ON rm.role_principal_id r.principal_id JOIN sys.server_principals AS m ON rm.member_principal_id m.principal_id WHERE r.name IN (sysadmin,securityadmin,serveradmin,processadmin) ORDER BY r.name, m.name;为什么这四个要一起看securityadmin能 GRANT 任意权限、能给登录改密码实际能力接近 sysadmin只清 sysadmin 等于没清serveradmin能改实例级配置包括把xp_cmdshell重新打开等于绕过了第 5 章的加固processadmin能 KILL 任意会话属于运维角色业务账号不该在里面。清理动作很简单ALTER SERVER ROLE sysadmin DROP MEMBER [账号名]但删之前要和业务方确认尤其是那些用管理员账号连库的老应用。还有一个历史遗留项BUILTIN\Administrators这个 Windows 组登录。旧版本安装时默认会把它加成 sysadmin新版本安装向导里不再默认勾选但从老实例升级上来的库经常还在。这个登录意味着主机上任何一个本地管理员都能直接以 sysadmin 身份进数据库是清单里最容易被漏掉的一个入口建议单独查一遍。3.3 数据库角色按 schema 授权别按表授权数据库这一层规范给的做法是「创建角色、给角色授权、把角色赋给用户」这个思路是对的落地时我一般按 schema 而不是按表来授。-- 10) 按 schema 建角色授权新加的表自动继承 USE [SalesDB]; GO CREATE ROLE app_reader AUTHORIZATION dbo; CREATE ROLE app_writer AUTHORIZATION dbo; GRANT SELECT ON SCHEMA::sales TO app_reader; GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::sales TO app_writer; GRANT EXECUTE ON SCHEMA::sales TO app_writer; -- 审计表禁止应用侧删除DENY 优先级高于 GRANT用来兜底 DENY DELETE ON SCHEMA::audit TO app_writer; ALTER ROLE app_reader ADD MEMBER app_user; ALTER ROLE app_writer ADD MEMBER app_user;按 schema 授的好处是新增表不用再补授权坏处是权限边界变粗所以敏感表要单独用DENY兜一层。DENY的优先级高于GRANT这是 SQL Server 权限模型里少数不需要犹豫的规则用来防「应用账号顺手把审计表清了」这类事故很有效。反向检查看谁不该在db_owner里-- 11) 数据库高权角色成员db_owner 能 DROP 库业务账号不该有 SELECT dp.name AS db_user, r.name AS db_role FROM sys.database_role_members AS drm JOIN sys.database_principals AS r ON drm.role_principal_id r.principal_id JOIN sys.database_principals AS dp ON drm.member_principal_id dp.principal_id WHERE r.name IN (db_owner,db_securityadmin,db_ddladmin) ORDER BY r.name, dp.name;最后别忘 public本文还有配套的精品资源点击获取