等保测评进场后,SQL Server 这块的检查往往最花时间。不是说它命令有多难,而是测评项分散得很——身份鉴别、访问控制、安全审计、数据完整性、备份恢复,几乎每一项都要在数据库上取证据。更麻烦的是 SQL Server 自身的权限体系复杂,sysadmin、serveradmin 这些固定服务器角色一旦多给了人,整库就等于裸奔。这篇文章我把在测评现场实际用到的 SQL Server 检查命令整理成一套能复现的流程,按这个顺序敲下来,该查的查完,证据链也就齐了。不光是等保测评能用,日常做数据库安全自查、加固后复查也一样用得上。
1. 等保测评中SQL Server检查的整体思路与准备
1.1 SQL Server要检查的核心维度有哪些
等保测评对数据库的要求,归纳起来其实就是五个层面:身份鉴别、访问控制、安全审计、数据完整性、备份恢复。老版本 SQL Server 和新版本在这几个层面的检查手段基本一致,差异主要体现在命令输出字段和部分功能的可用性上,比如 SQL Server 2012 以后审计功能才真正完善,SQL Server 2016 之后 TDE 透明数据加密也更容易配置。
身份鉴别重点看三件事:登录模式是混合模式还是仅 Windows 身份验证、SQL 登录账号是否强制了密码策略、是否存在空口令或弱口令账号。访问控制主要看 sysadmin、db_owner 这类高权限角色的成员是否过多,guest 用户是否开着,sa 账号是否还在用默认配置。安全审计看两点:SQL Server 审计功能是否启用、错误日志中能否找到登录失败记录。数据完整性看数据库页面校验和是否开启,近期有没有做过 DBCC CHECKDB。备份恢复看 msdb 里有没有完整备份记录,完整备份、差异备份、日志备份的频率是否符合要求。
这五个层面并不是孤立检查的。比如身份鉴别里发现问题,通常会连带影响访问控制的判定;审计没开,就意味着登录失败的证据只能从错误日志里找。所以实际操作时我习惯按维度查,但输出结论时会把问题项关联起来看。
1.2 进场后先摸清环境再动手
很多刚入行的测评人员拿到库就连上去敲命令,第一个坑就出在这儿:不先确认版本和权限,有些命令在不同版本上行为不一样,权限不足时还会报错,现场浪费时间。
我的习惯是先跑这几条把环境和身份认清楚:
SELECT @@SERVERNAME AS ServerName, @@VERSION AS VersionInfo; SELECT SERVERPROPERTY('ProductVersion') AS ProductVersion, SERVERPROPERTY('Edition') AS Edition, SERVERPROPERTY('ProductLevel') AS ProductLevel; SELECT name, state_desc, recovery_model_desc, is_encrypted FROM sys.databases; SELECT IS_SRVROLEMEMBER('sysadmin') AS IsSysAdmin;第一段看实例名和版本。不同版本对应的测评基线判定会有一点差异,比如 SQL Server 2012 以后审计功能完整,但 SQL Server 2008 里只能靠扩展存储过程或触发器,写报告时依据就不一样。第二段看所有数据库的当前状态、恢复模式、是否加密,state_desc 如果是 OFFLINE、RESTORING、RECOVERING,后面备份和完整性检查的判定都要调整。第三段确认当前连进来的账号是否有 sysadmin 权限,等保检查中大量命令需要 VIEW SERVER STATE 权限,权限不足时后续记录没法取到有效证据,不如一开始就跟客户提清楚要一个合适权限的账号。
环境摸清后再决定用什么工具。SSMS 图形界面适合看配置,但证据截图不如命令输出清晰,所以现场我基本都是 SSMS 开查询窗口用结果到文本模式,或者直接 sqlcmd 连。多实例环境用 sqlcmd -S 服务器名\实例名 连接,单实例直接连默认端口即可。
2. 身份鉴别与访问控制:登录、口令与权限的检查命令
2.1 身份验证模式与密码策略怎么查
SQL Server 的登录模式决定了身份鉴别的基础。Windows 身份验证模式下,数据库自己不管口令,完全依赖域或本地操作系统账号体系;混合模式则允许 SQL 登录账号,数据库需要独立管理口令复杂度和有效期。等保测评里经常遇到的一个问题就是:客户开了混合模式,但 SQL 登录账号的口令复杂度策略没启用,这就属于身份鉴别方面的风险项。
查看登录模式的命令:
SELECT CASE SERVERPROPERTY('IsIntegratedSecurityOnly') WHEN 0 THEN 'Mixed Mode' WHEN 1 THEN 'Windows Only' END AS LoginMode;IsIntegratedSecurityOnly 返回 0 表示混合模式,返回 1 表示仅 Windows 身份验证。混合模式下必须重点看 SQL 登录账号的密码策略。
查看所有 SQL 登录账号是否强制密码策略和过期策略:
SELECT name, is_policy_checked, is_expiration_checked, is_disabled FROM sys.sql_logins ORDER BY name;is_policy_checked 表示该登录名是否强制了 Windows 密码策略,is_expiration_checked 表示是否强制密码过期策略。如果大量账号这两个字段都是 0,说明口令可能长期不换、复杂度也不受约束。测评时只要有一条账号是 0,就可以对应到“身份鉴别信息未定期更换、复杂度不符合要求”这类问题项。
判断是否存在空口令账号,用 PWDCOMPARE 函数:
SELECT name, CASE WHEN PWDCOMPARE('', password_hash) = 1 THEN 'Empty Password' ELSE 'Not Empty' END AS BlankPasswordCheck FROM sys.sql_logins WHERE password_hash IS NOT NULL;PWDCOMPARE 的用法很简单:第一个参数传明文密码,第二个参数传 sys.sql_logins 里的 password_hash,返回 1 说明匹配。这里用空字符串判断 SQL 登录是否为空口令,实测非常有效。注意 PWDCOMPARE 不能用于 Windows 登录账号,那是另一套验证体系,也不需要在这里判断。
2.2 登录账号、sa账号与高权限角色检查
把所有登录账号列出来是访问控制检查的基础。命令:
SELECT name, type_desc, is_disabled, default_database_name, create_date, modify_date FROM sys.server_principals WHERE type IN ('S', 'U') AND name NOT LIKE '##%' ORDER BY name;type_desc 为 SQL_LOGIN 的是 SQL 账号,为 WINDOWS_LOGIN 的是 Windows 账号。重点看有没有明显不属于业务需要的账号、长期不用的账号、已离职人员账号。等保测评一般还会看账号是否定期清理,所以 create_date 和 modify_date 也有参考价值。
sa 账号需要单独查:
SELECT name, is_disabled, is_policy_checked, is_expiration_checked FROM sys.server_principals WHERE name = 'sa';如果 sa 启用、密码策略没开,就属于高风险。很多历史库是安装时默认配置,sa 一直没动过。整改建议里最直接的就是:要么改名(ALTER LOGIN sa WITH NAME = 其他名称),要么直接禁用(ALTER LOGIN sa DISABLE),然后换强口令。如果业务系统确实依赖 sa 登录,那至少要把密码策略打开、口令换成高强度复杂度,并在整改建议中强调应用改造优先于继续沿用 sa。
看哪些账号拥有高权限服务器角色:
SELECT p.name AS LoginName, p.type_desc, r.name AS ServerRole FROM sys.server_principals p JOIN sys.server_role_members rm ON p.principal_id = rm.member_principal_id JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id WHERE p.is_disabled = 0 ORDER BY r.name, p.name;这条查询把 sysadmin、securityadmin、serveradmin、processadmin 等固定服务器角色的成员列出来。sysadmin 成员实际上拥有实例全部权限。如果某业务系统的普通运维账号挂在 sysadmin 里,或者开发人员账号直接给了 systemadmin,这就是典型的越权。报告里的建议一般是:按最小权限原则拆分账号,普通业务账号只授予所需数据库的 db_datareader、db_datawriter 等角色,而不是直接给服务器级最高权限。
2.3 数据库级权限与最小化授权判断
服务器级权限查完还要落到数据库级。很多公司服务器角色管得不严,但数据库角色也一样混乱,最常见的是开发账号全部挂在 db_owner 里。对每个业务库执行:
USE [AdventureWorks]; GO SELECT r.name AS RoleName, m.name AS MemberName FROM sys.database_role_members rm JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id WHERE r.name IN ('db_owner', 'db_securityadmin', 'db_accessadmin') ORDER BY r.name, m.name;db_owner 在单库内拥有全部权限,能改表结构、加索引、删数据,人员一旦多,后期出问题都分不清是谁改的。db_securityadmin 可以管理用户和权限,也属于高危角色。这里建议看返回值里是否有非 DBA 的普通业务账号。
另外还要看数据库的所有者和 guest 用户:
SELECT SUSER_SNAME(owner_sid) AS OwnerLogin, name AS DatabaseName FROM sys.databases; USE [AdventureWorks]; GO SELECT name FROM sys.database_principals WHERE name = 'guest' AND is_disabled = 0;guest 用户启用意味着任何没有有效数据库用户的登录名,都能通过 guest 映射进入库,相当于把访问控制的大门留了一条缝。正常情况下业务库建议禁用 guest。
3. 安全审计与日志:审计配置、错误日志和登录取证
3.1 SQL Server审计功能是否真的在记录
等保测评里安全审计的要求覆盖到每个用户,也就是说登录成功、登录失败、权限变更、关键操作都应该有记录。SQL Server 自带审计功能从 2012 年开始比较成熟,之前的老库更多靠错误日志和触发器。
查看审计配置的命令:
SELECT name, is_enabled, has_old_backward_compatible_audit FROM sys.server_audits; SELECT name, is_enabled FROM sys.server_audit_specifications; SELECT name, is_enabled FROM sys.database_audit_specifications;如果 sys.server_audits 查询结果为空,说明实例级别根本没创建审计。翻翻是否有 SQL Server 登录触发器在记录登录行为:
SELECT name, is_disabled, create_date FROM sys.server_triggers;有些客户的 DBA 会用触发器把登录成功、失败信息写进自定义表,这可以作为审计覆盖的替代证据,但说明书写起来要额外注意,触发器方案和原生审计相比,在权限变更审计方面覆盖不全。
如果客户已经配置了审计,建议把审计事件也确认一遍。服务器级别审计规范里一般应包含 FAILED_LOGIN_GROUP 和 SUCCESSFUL_LOGIN_GROUP,数据库级别审计规范里应包含 SCHEMA_OBJECT_ACCESS_GROUP 或 DATABASE_PRINCIPAL_CHANGE_GROUP 之类的关键事件。只开了审计但没配事件,等于没开。
3.2 错误日志与登录记录排查
没有原生审计时,错误日志是最直接的登录取证来源。SQL Server 错误日志会记录“Login failed for user”这类信息,默认保留几个历史文件。命令:
EXEC xp_readerrorlog 0, 1, N'%Login failed%', NULL, NULL, NULL, N'DESC'; EXEC xp_readerrorlog 0, 1, N'%Login succeeded%', NULL, NULL, NULL, N'DESC';xp_readerrorlog 参数含义:第一个参数是日志文件编号,0 表示当前日志;第二个参数 1 表示错误日志,2 是代理日志;第三、四个参数是要过滤的字符串,第五、六个是起始和结束时间;最后一个参数 N'DESC' 表示按时间倒序。执行后能看到失败的登录账号、来源 IP、失败时间,审计证据就有了。
如果不能直接跑这个存储过程,查一下错误日志文件所在的路径:
SELECT SERVERPROPERTY('ErrorLogFileName') AS ErrorLogPath;再去对应目录把 ERRORLOG 和 ERRORLOG.1 等文件打开搜索 login failed。这个方法在客户权限管控严格、不想给查询权限时也能用。
当前连接的会话信息也值得看一眼:
SELECT session_id, login_name, host_name, program_name, login_time FROM sys.dm_exec_sessions WHERE is_user_process = 1;这条能看当时有哪些账号在连、从哪里连的、用的什么客户端工具。如果发现一个未知账号正连着库,结合登录失败记录一起分析,基本能判断是否存在可疑访问。
3.3 高危扩展与危险配置检查
入侵防范在数据库层的体现,主要是看有没有不必要的扩展存储过程、危险配置项和未加密的连接。SQL Server 里最容易出问题的配置是 xp_cmdshell、OLE Automation Procedures、CLR 和远程访问。查询:
SELECT name, value_in_use FROM sys.configurations WHERE name IN ('xp_cmdshell', 'Ole Automation Procedures', 'clr enabled', 'remote access', 'contained database authentication');xp_cmdshell 一旦开启,SQL 登录账号就可能通过这个扩展执行操作系统命令。 etc 测评里看到 value_in_use 为 1 的 xp_cmdshell 基本就是高风险项,整改建议一般是确认业务不需要后立即关闭。OLE Automation Procedures 允许在 SQL 里调用 COM 对象,CLR 则允许运行 .NET 程序集,这两个在业务软件没有明确依赖的情况下都应该保持关闭。contained database authentication 开启后,包含数据库用户可以不用实例登录名直接连接,等于绕开了服务器级身份鉴别,一般不建议开。
还可以查一下是否存在可疑的扩展存储过程开放情况:
SELECT name, is_disabled FROM sys.server_triggers WHERE is_disabled = 0; EXEC sp_helprotect;sp_helprotect 返回当前数据库中对象的权限列表,内容可能很多,但能帮助筛查是否存在对 public 角色开放的敏感对象权限。
4. 数据完整性和备份恢复:备份历史、校验和与DBCC检查
4.1 数据库状态、文件和页面校验
数据完整性检查首先要看数据库本身状态和恢复模式。恢复模式如果是 SIMPLE,日志备份就没有意义,等保测评里备份策略的判定要从完整备份开始算。
SELECT name, state_desc, recovery_model_desc, is_encrypted, page_verify_option_desc FROM sys.databases;sys.databases 里没有直接的 page_verify_option_desc 列,这是一个容易记混的地方。页面校验和需要看 sys.database_files:
USE [AdventureWorks]; GO SELECT name, physical_name, type_desc, size, page_verify_option_desc FROM sys.database_files;page_verify_option_desc 只有两个常见值:CHECKSUM 和 NONE(老库常见 TORN_PAGE_DETECTION)。SQL Server 默认在新建数据库时启用 CHECKSUM,但一些从 2005、2008 迁移过来的老库可能还是 NONE。如果页面校验和没开,数据库在 I/O 错误面前就没有自检能力,恢复时也可能发现损坏页,所以这个项在等保测评里通常建议开启:
ALTER DATABASE [AdventureWorks] SET PAGE_VERIFY CHECKSUM;这条属于整改命令,测评现场可以只在报告里建议,等客户确认业务窗口后再执行。
4.2 DBCC CHECKDB怎么用而不踩坑
DBCC CHECKDB 是数据库完整性的终极检查手段,能检测逻辑和物理一致性。但等保测评现场不能乱跑,尤其业务大库,全库 CHECKDB 的 I/O 开销非常高。我通常在测评时只做两件事:一是查数据库最近的 DBCC 执行记录或维护计划,二是确有必要时才在低峰窗口跑一个物理一致性快速检查。
查看维护计划里的数据库一致性检查作业:
USE msdb; GO SELECT j.name AS JobName, j.enabled, j.date_created, j.description FROM sysjobs j ORDER BY j.name;如果作业列表里有“数据库完整性检查”类似的作业且 enabled = 1,说明客户有例行机制。如果作业里没有,再结合备份记录判断。这里要注意一个细节:很多维护计划作业是用 dbo 账号建的,如果该账号密码到期或被禁用,历史记录可能停更,所以最好同时看作业最近是否成功执行过:
SELECT j.name, j.enabled, jh.run_date, jh.run_time, jh.run_status FROM msdb.dbo.sysjobs j LEFT JOIN msdb.dbo.sysjobhistory jh ON j.job_id = jh.job_id ORDER BY jh.run_date DESC;run_status 为 1 表示成功。如果作业最后执行时间已经是两三个月之前,完整性检查这项就没法判合规。
如果确实需要现场跑 CHECKDB,我建议用物理模式控制开销:
DBCC CHECKDB ('AdventureWorks') WITH NO_INFOMSGS, PHYSICAL_ONLY;NO_INFOMSGS 减少输出,PHYSICAL_ONLY 只检查页结构、链指针等物理方面,不检查逻辑一致性,性能开销比完整 CHECKDB 小很多但仍有 I/O 压力。一定要避业务高峰期,并在执行前和客户 DBA 确认。
4.3 备份策略与备份记录核对
备份恢复检查是等保测评里特别容易和客户起争论的环节。很多客户说“我们做了备份”,但实际用的是虚拟化快照或第三方备份软件,这些备份不一定写入 msdb 的备份历史表。所以第一步还是先查库里的备份记录:
SELECT database_name, type AS BackupType, backup_start_date, backup_finish_date, backup_size / 1024 / 1024 AS Size_MB FROM msdb.dbo.backupset WHERE database_name = 'AdventureWorks' ORDER BY backup_start_date DESC;type 字段的含义:D 完整备份、I 差异备份、L 日志备份。正常等保三级要求里,完整备份至少每周一次,日志备份按恢复点目标而定,业务系统一般建议每天差异加定期日志。测评时可以看最近一次完整备份是否在合规周期内,比如一个月内没有完整备份,恢复点就无法覆盖,显然不合格。
备份设备位置也要看:
SELECT b.database_name, b.backup_start_date, f.physical_device_name FROM msdb.dbo.backupset b LEFT JOIN msdb.dbo.backupmediafamily f ON b.media_set_id = f.media_set_id WHERE b.database_name = 'AdventureWorks' ORDER BY b.backup_start_date DESC;physical_device_name 能看出备份是写到磁盘、磁带还是网络共享路径。如果备份路径在本机 C 盘,磁盘故障时备份一样丢,恢复性就无从谈起。报告里可以建议备份介质与数据库文件分离存储。
第三方备份没写入 msdb 的情况要单独处理。我的做法是:msdb 查不到记录,先不急着判不合格,直接问客户备份怎么做的,让 DBA 截图备份软件的任务记录和恢复演练记录。如果客户能提供合理证据,报告可以写“备份机制不在SQL Server内记录,由第三方平台统一管理,判定为合规”。这样既客观也少跟客户吵架。反过来,如果客户连备份软件记录都拿不出来,那就该记问题了。
5. 等保测评SQL Server命令速查与现场常见问题
5.1 高频命令速查表
现场检查时不需要把上面所有命令都过一遍,按测评项挑重点跑就行。我把常用命令整理成速查表,进场照着刷:
| 检查项 | 核心命令 |
|---|---|
| 版本与环境 | SELECT @@VERSION;、SELECT SERVERPROPERTY('ProductVersion'); |
| 身份验证模式 | SELECT SERVERPROPERTY('IsIntegratedSecurityOnly'); |
| SQL登录密码策略 | SELECT name, is_policy_checked, is_expiration_checked FROM sys.sql_logins; |
| 空口令检测 | SELECT name, PWDCOMPARE('', password_hash) FROM sys.sql_logins; |
| 登录账号列表 | SELECT name, type_desc, is_disabled FROM sys.server_principals WHERE type IN ('S','U'); |
| sa账号状态 | SELECT name, is_disabled FROM sys.server_principals WHERE name = 'sa'; |
| 服务器角色成员 | SELECT p.name, r.name FROM sys.server_principals p JOIN sys.server_role_members rm ON p.principal_id = rm.member_principal_id JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id; |
| 数据库角色成员 | USE 库名; SELECT r.name, m.name FROM sys.database_role_members rm JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id; |
| 审计配置 | SELECT name, is_enabled FROM sys.server_audits; |
| 登录失败记录 | EXEC xp_readerrorlog 0, 1, N'%Login failed%', NULL, NULL, NULL, N'DESC'; |
| 高危配置 | SELECT name, value_in_use FROM sys.configurations WHERE name IN ('xp_cmdshell','Ole Automation Procedures','clr enabled'); |
| 数据库状态与加密 | SELECT name, state_desc, recovery_model_desc, is_encrypted FROM sys.databases; |
| 页面校验和 | USE 库名; SELECT name, page_verify_option_desc FROM sys.database_files; |
| 备份记录 | SELECT database_name, type, backup_start_date, backup_size FROM msdb.dbo.backupset ORDER BY backup_start_date DESC; |
| 维护作业 | USE msdb; SELECT name, enabled, date_created FROM sysjobs; |
每执行一条命令后,在 SSMS 里把结果切到“结果到文本”再另存为 txt 文件,比截图更清晰,后续写报告直接引用结果。
5.2 测评现场容易踩的几个坑
第一个坑是权限不足。有些客户只给一个 db_datareader 账号,跑 xp_readerrorlog 直接报“权限不足”。这种情况别硬刚,明确告诉客户测评需要 VIEW SERVER STATE 权限,一般走申请流程很快能拿到。如果客户确实不给,那就退一步,让 DBA 代为执行并输出结果,截图里必须保留执行账号。
第二个坑是命令在旧版本上的兼容问题。SQL Server 2008 不支持部分新视图字段,比如 sys.server_audits 在 2008 R2 里可能没有问题,但在 SQL Server 2005 里根本不存在。进场第一步先跑版本信息的目的就在这里,版本太老的话部分命令必须换写法,比如用 xp_logininfo 查登录信息而不是直接查视图。
第三个坑是 PWDCOMPARE 的结果受大小写和排序规则影响,SQL Server 的登录密码本身区分大小写,所以 PWDCOMPARE('', password_hash) 用来查空口令没问题,但拿它去验证“密码是否等于账号名”这种弱口令时要注意排序规则。别在现场反复试错,容易把账号锁住。遇到疑似弱口令,建议让客户 DBA 用内部策略检查工具确认,而不是在测评环境里暴力尝试。
第四个坑是备份历史记录和实际备份情况不一致。第三方备份软件对 SQL Server 做 VSS 快照时通常不会写 msdb 的 backupset 表,这类情况只按命令结果判不合规会冤枉客户。反过来,有些客户的备份作业每天都在跑,但备份文件没有做过恢复演练,报告里也要指出缺乏恢复验证。我的建议是备份检查分为两步:命令看记录,访谈问流程,两者对得上才算证据完整。
第五个坑是执行顺序。先查环境再查配置,先查 SELECT 再考虑是否需要执行 DBCC。测评人员不是挨骂的工具人,数据库是客户的命根子。所有可能产生性能影响的命令,必须在客户 DBA 确认的窗口内执行。
5.3 从命令结果到报告素材
测评报告的最后一步是把命令输出转成问题项和建议。这里有三个经验可以分享。
第一个经验是保存证据时带上下文。SSMS 里把查询结果输出到文件,文件名建议写成“实例名_检查项_日期.txt”,比如SQL01_identity_auth_20250220.txt。多个结果合并到一个文件里,后续写报告要找某一条记录时就特别省事。截图的时效性不如文本输出,文本结果还能做二次分析,比如统计登录失败次数。
第二个经验是判据要一致。同样的 is_policy_checked = 0,在一个库里判了不合规,在另一个库里也必须是相同结论,不能因为客户态度不同而改变判定标准。测评独立性靠的就是这个。如果想给客户追加建议,比如“该账号是运维脚本专用,可以申请例外”,那也应该在报告的整改建议里写明例外理由,而不是在原始判定上放水。
第三个经验是问题项要有整改闭环。命令发现问题后,给客户一条可落地的 ALTER 语句,比单纯写一句“建议修改口令复杂度”更有价值。比如:
ALTER LOGIN [ops_user] WITH CHECK_POLICY = ON, CHECK_EXPIRATION = ON;写成这样,客户拿到报告直接执行就行,落地成本低,配合意愿也高。
我个人在现场还有个习惯,最后把所有检查结果整理成一张对照表:每一项测评要求、对应命令、实际结果、判定结论、整改建议。这不是给客户看的,是给自己保存证据链用的。数据库测评最怕的就是几个月后客户问“当时那条记录是怎么查的”,你现在留好命令和原始输出,后面怎么问都能翻出来。
这套流程从头到尾跑一遍大概一两个小时,取决于库的数量和关联账号的多少。整体思路不复杂,难的是把每个判断背后的依据想清楚。命令只是抓手,真正值钱的是你能否在拿到结果后知道哪里有问题、为什么有问题、怎么改才不踩生产环境的坑。数据库不是拿来练手的沙盒,能用 SELECT 查到的就别乱改配置,动手之前一定先确认影响范围。