简介:面向Sql Server数据库运维与安全管理人员的系统加固参考文档,围绕账号权限、登录安全、日志审计、通信加密及服务器防护等核心问题提供具体可执行规范,适合在等保合规整改、数据库安全检查、日常运维加固等场景使用。文档以中国移动管理信息系统部2022年发布的条目式规范为框架,分为账号管理、认证授权、日志配置、通信协议、设备其他安全要求四大部分,共12条加固项,从最小权限分配、双因素认证、密码复杂度与有效期、登录失败锁定,到完整操作日志、SSL/TLS加密通信、防火墙与入侵检测部署、防毒软件安装与定期更新均有明确规定。包体为单个doc格式文件,大小1.73MB,内容结构清晰便于按编号逐项核查。目前已有245人学习下载。每个加固条目都列出了编号、名称、实施目的、问题影响、系统当前状态、实施步骤、回退方案和判断依据,可直接转化为安全基线检查表或整改方案,帮助团队快速定位Sql Server数据库存在的安全风险并逐项加固。
1. SQL Server加固规范:一份能直接当整改清单用的安全基线
拿到这份Sql Server加固规范时,我第一反应是翻它的编号和校验项结构——SHG-Mssql-01-01-01这种命名一看就是企业内部合规基线,不是网上拼凑的教程。整套规范把账号管理、日志配置、通信协议、设备安全拆成了二十多个可执行条目,每个条目都带实施目的、问题影响、操作步骤、回退方案和风险等级,这比市面上绝大多数"安全加固指南"更贴近一线整改场景。适合什么人用:刚接手数据库安全巡检的DBA、要做等保整改的运维、以及需要给开发团队定数据库操作红线的人。需要注意一个背景:规范里的命令停留在SQL Server 2000时代的写法,但安全思想放到2022年依然成立,照着做之前要先把代际差异换算清楚。
2. 账号管理与认证授权:六个条目的T-SQL落地写法
2.1 账号盘点:先搞清楚系统里有多少个登录名
规范的第一个动作是盘点当前登录名。原文给出的查询是:
use master Select name, password from syslogins order by name逻辑说明:这条语句在master库下扫描syslogins视图,列出所有登录名和口令哈希。SQL Server 2000下password列直接可见,2005之后该列为varbinary哈希值,且普通账号查询会受限,所以我在2005以上版本习惯用:
SELECT name, createdate, updatedate, is_disabled FROM sys.sql_logins ORDER BY name;参数说明:is_disabled字段用来判断登录是否被禁用,createdate和updatedate可以顺带看出密码的设立与更新时间,这对后续"空密码和长期未改密"两项检查很有用。拿到清单之后,找管理员逐个人头确认归属,把离职、过期、用途不明的账号单独列一个待处理组。这一步不要跳,后面所有账号操作都以这份盘点结果为依据。
2.2 独立账号与角色授权:让每个管理员有自己的登录名
规范的SHG-Mssql-01-01-01条目要求"为不同的管理员分配不同的账号",核心是禁止共享账号。原文的操作是:
sp_addlogin 'user_name_1','password1' sp_addlogin 'user_name_2','password2'逻辑说明:sp_addlogin是SQL Server 2000时代创建SQL登录名的存储过程。两个不同的管理员各自持有独立账号,密码各自负责,出错时能从日志里定位到具体的人。2005之后官方推荐用CREATE LOGIN替代,写法为:
CREATE LOGIN user_name_1 WITH PASSWORD = 'password1', CHECK_POLICY = ON, CHECK_EXPIRATION = ON;参数说明:CHECK_POLICY和CHECK_EXPIRATION分别启用Windows密码策略和密码过期策略。这两项在2000时代不存在,但现代Windows域环境下应该尽量打开,否则新建的弱口令账号很容易成为突破口。
账号建完后要给它配角色。原文建议"建立角色,并给角色授权,把角色赋给不同的用户"。对应的落地脚本:
USE [业务库名]; CREATE ROLE db_audit_writer; GRANT SELECT, INSERT, UPDATE, DELETE ON dbo.订单表 TO db_audit_writer; EXEC sp_addrolemember 'db_audit_writer', 'user_name_1';逻辑说明:先建角色,再给角色授对象级权限,最后把用户加进角色。用户不直接持有对象权限,后续调整权限时只改角色,不用逐个用户改。这套做法在SQL Server 2000和现代版本都能用,只是2005之后推荐用CREATE ROLE替代sp_addrole。
提示:这里的user_name_1和user_name_2只是示例名。实际命名建议带业务标识,比如dba_zhangsan、app_report_reader,避免出现一堆无法辨认用途的登录名。
2.3 删除无效账号:权限越少入口越小
SHG-Mssql-01-01-02要求删除或锁定无效账号。原文操作路径是"企业管理器->安全性->登录,在用户上点右键选择删除"。用命令替代的话:
-- 删除登录名(会连带删除该登录名下的用户映射) DROP LOGIN old_user_name;逻辑说明:DROP LOGIN把登录名从实例级别移除。但要注意,如果该登录名在某个业务库里有用户映射,直接DROP可能报错或留下孤立用户。更稳妥的做法是先在对应库里执行DROP USER,再回到master执行DROP LOGIN。
如果暂时不确定某个账号是否还有用,我一般先禁不禁用、不删除:
ALTER LOGIN old_user_name DISABLE;参数说明:DISABLE是软处理,账号保留但无法登录,出了问题时可以快速恢复。等确认无影响后再DROP,相当于给自己留了后悔药。规范里回退方案写的是"增加删除的账户",实际操作里先禁用后删除,回退成本低得多。
2.4 限制启动账号权限与权限最小化
SHG-Mssql-01-01-03针对的是SQL Server服务启动账号。原文的关键约束是:启动账号不要是Administrators组成员,也不要让它拥有超出需要的用户权限。这条执行起来不在数据库里,而在Windows服务管理器里。操作路径:services.msc -> SQL Server服务 -> 属性 -> 登录,把启动账号改为专用账号,并确保该账号只属于User组。SQL Server服务进程会以启动账号的权限运行,账号权限过高,等于给所有通过SQL Server能触达的子进程开了同等权限。
SHG-Mssql-01-01-04权限最小化要处理两块:服务器角色和数据库角色。服务器角色检查语句:
SELECT s.name, r.name AS server_role FROM sys.server_role_members rm JOIN sys.sql_logins s ON rm.member_principal_id = s.principal_id JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id WHERE s.name = '某个业务登录名';数据库角色检查:
USE 业务库名; SELECT m.name AS username, r.name AS db_role FROM sys.database_role_members rm JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id;逻辑说明:第一段查该登录名被赋予了哪些服务器级角色,重点关注sysadmin、securityadmin、serveradmin这种高危角色。第二段查它在每个库里的角色归属,重点看db_owner。业务账号只需要读某几张表,却顶着sysadmin或db_owner,这就是典型的权限过大。最小化原则的执行动作就是取消多余角色:
ALTER SERVER ROLE sysadmin DROP MEMBER 某个登录名; ALTER ROLE db_owner DROP MEMBER 某个用户名;2.5 空密码与强口令:sa账号至少10位
SHG-Mssql-01-01-06对空密码的检查脚本是:
Select name, Password from syslogins where password is null order by name逻辑说明:password为null代表该登录名没有设置密码。任何数据库都不能允许空密码登录,尤其是sa。2005以上版本里可以用:
SELECT name FROM sys.sql_logins WHERE password_hash IS NULL OR LEN(password_hash) = 0;密码修改的原文命令是:
exec sp_password '旧口令','新口令',用户名参数说明:sp_password是2000时代的改密存储过程,2005之后虽然仍兼容,但推荐用ALTER LOGIN。
ALTER LOGIN sa WITH PASSWORD = '新强口令';强口令策略要直接写进规范:至少10位,包含大写、小写、数字、特殊字符四类里至少三类,且不能是键盘连续序列(如admin123、P@ssw0rd这种被字典收录的)。sa口令的更新要慎之又慎,改之前确认所有依赖sa连接的连接字符串都同步更新,否则改完密码整个业务连不上库,那就是典型的加固引发生产事故。
3. 日志配置:审计级别"全部"与登录记录的核查路径
3.1 审计级别为什么要调到"全部"
SHG-Mssql-02-01-01要求启用日志记录功能,记录内容包括登录账号、登录是否成功、登录时间、远程登录IP。原文的配置路径是"数据库属性->安全性->审计级别调整为全部"。这个设置在SQL Server 2000里是数据库属性页的选项,到了2005之后被挪到服务器属性下的"安全性"页,叫"登录审核",选项有:无、仅失败登录、仅成功登录、全部。
这里有个执行细节容易被忽略:审计级别"全部"会同时记录成功和失败的登录尝试。只会记录失败,排查密码暴力破解够用;但合规审计要求看到谁在什么时间成功登录过,必须选"全部"。命令层面可以用注册表方式直接写入:
EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'AuditLevel', REG_DWORD, 3;参数说明:AuditLevel的值,0为无、1为仅成功、2为仅失败、3为全部。xp_instance_regwrite是系统扩展存储过程,专门用来读写SQL Server实例相关的注册表项,比手动开注册表编辑器更安全,因为实例名大小写和路径差异会被自动处理。执行完需要重启SQL Server服务生效。
3.2 身份验证模式:混合模式是审计前提
同一章节还要求把身份验证调整为"SQL Server和Windows"。这个模式在2000里叫"混合验证模式",2005之后叫"SQL Server和Windows身份验证模式"。设置它是因为:Windows身份验证模式下,SQL登录名(包括sa)无法登录,审计功能对SQL账号的登录记录形同虚设;只有混合模式才能同时审计两种来源的登录。
检查当前模式:
EXEC xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode';返回的REG_DWORD值为1表示仅Windows身份验证,2表示混合模式。需要改成混合模式时:
EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2;注意:修改LoginMode后同样要重启服务。而且从仅Windows模式切到混合模式后,所有SQL登录名重新获得登录资格,如果之前有些账号密码是空的,这一步会瞬间打开多个入口。所以在切模式之前,务必先按上一章的检查脚本把空密码账号清干净。
3.3 登录日志去哪儿看
开启审计后,登录事件会写入Windows应用程序日志,来源为MSSQLSERVER。查看方式:
- 事件查看器 -> Windows日志 -> 应用程序,筛选来源为MSSQLSERVER。
- 用PowerShell查询近24小时的登录失败记录:
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='MSSQLSERVER'; StartTime=(Get-Date).AddDays(-1)} | Where-Object { $_.Id -in 18454,18455,18456 } | Select-Object TimeCreated, Id, Message参数说明:事件ID 18456代表登录失败,18454代表登录成功(SQL登录名),18455代表登录成功(Windows登录名)。生产环境如果发现18456频繁出现且来源IP分散,基本可以判定有人在扫密码。规范里"远程登录时用户使用的IP地址"这一项,就藏在这些日志的消息体里,排查时需要把Message完整展开。
日志量是另一个要提前评估的点。把审计开到"全部"后,一个每天有几千次应用连接的库,日志文件增长会很明显。建议配套启用日志循环,控制单个日志文件大小:
EXEC sp_cycle_errorlog;逻辑说明:sp_cycle_errorlog是SQL Server错误日志的轮换命令,把当前日志归档为旧日志并开启新日志。对Windows应用程序日志,则靠系统级的日志大小策略约束。审计与日志量是一对矛盾体,规范只要求"记录",没告诉你日志爆了会拖慢系统,这个平衡点要靠自己把握。
4. 通信协议加固:TCP/IP协议栈与三个注册表键值
4.1 协议裁剪:只留TCP/IP的取舍
SHG-Mssql-03-01-01要求"只使用TCP/IP协议,禁用其他协议"。在SQL Server 2000里通过"服务器网络实用工具"配置,现代版本在"SQL Server配置管理器->SQL Server网络配置->协议"里操作。每个网络协议启用状态如下:
| 协议 | 默认状态 | 建议状态 | 说明 |
|---|---|---|---|
| Shared Memory | 启用 | 视情况保留 | 本机回环连接需要,禁了会影响本机工具连接 |
| Named Pipes | 启用 | 禁用 | 局域网文件共享风格连接,现代环境基本不用 |
| TCP/IP | 启用 | 启用 | 保留为标准连接方式 |
这里有坑:规范说"禁用其他协议"没错,但Shared Memory禁掉之后,本机SSMS、SQLCMD连接都会受到影响。我一般处理为:Named Pipes直接禁用,Shared Memory保留。严格合规要求全部关闭的话,必须提前确认所有监控脚本和备份任务的连接方式,否则禁用瞬间一堆任务失败。
通过PowerShell查看协议状态:
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\实例名\MSSQLServer\SuperSocketNetLib' | Select-Object -ExpandProperty ProtocolList更直接的做法是在配置管理器里逐项看,因为协议状态变更后还要重启SQL Server服务才会生效。
4.2 TCP/IP协议栈加固:三个注册表键值的含义
SHG-Mssql-03-01-02的核心是三个注册表键值,防御对象分别是源路由欺骗、ICMP重定向和SYN Flood。整理如下:
| 键名 | 路径 | 期望值 | 防御目标 |
|---|---|---|---|
| DisableIPSourceRouting | HKLM\System\CurrentControlSet\Services\Tcpip\Parameters | 2 | 禁止IP源路由,防止攻击者用源路由选项绕过访问控制 |
| EnableICMPRedirect | HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters | 0 | 关闭ICMP重定向,防止路由表被恶意篡改 |
| SynAttackProtect | HKLM\System\CurrentControlSet\Services\Tcpip\Parameters | 2 | 开启SYN攻击保护,减缓SYN Flood冲击 |
这三项的意义要对应到数据库场景:SQL Server通常承载核心业务数据,攻击者拿不到应用层入口时,会尝试从网络层下手。源路由欺骗可以构造一条绕过防火墙的路径访问数据库端口;ICMP重定向可以把数据包导向攻击者的机器;SYN Flood则是经典拒绝服务手段,直接打满数据库的监听队列。这三个键值设好,是把网络层的攻击面收窄。
修改用管理员PowerShell:
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\Tcpip\Parameters' ` -Name 'DisableIPSourceRouting' -Value 2 -Type DWord Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' ` -Name 'EnableICMPRedirect' -Value 0 -Type DWord Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\Tcpip\Parameters' ` -Name 'SynAttackProtect' -Value 2 -Type DWord参数说明:-Value后面的数字就是最终写入的键值,-Type DWord保证32位整型存储。执行后验证读取:
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\Tcpip\Parameters' | Select-Object DisableIPSourceRouting, EnableICMPRedirect, SynAttackProtect这三个键值是Windows系统级参数,不是SQL Server私有配置。修改后重启操作系统才能完全生效,很多运维改完不重启,过了几周发现值没变,以为是写入失败,其实是系统没重新加载网络栈。另外,64位系统上如果键不存在,先确认路径里有没有WOW6432Node节点,32位应用视角和64位系统视角的注册表路径可能不一致。
4.3 强制协议加密:连接字符串要同步改
SHG-Mssql-03-01-04要求把协议加密设为强制,"服务器网络配置工具->常规->强制协议加密"。开启后SQL Server所有客户端连接都必须走SSL/TLS加密,这能防止数据库账号密码在网络上明文传输。
但这一步坑非常大。开启强制加密后,原有连接字符串如果没配证书信任,应用会直接报连接失败或证书错误。比如JDBC连接串需要加:
jdbc:sqlserver://10.0.0.5:1433;databaseName=业务库;encrypt=true;trustServerCertificate=true;参数说明:encrypt=true要求连接走加密,trustServerCertificate=true表示信任服务器自签名证书。如果SQL Server只装了自签名证书,客户端必须信任它,否则握手失败;如果生产环境已有CA签发的证书,则可以去掉trustServerCertificate并配置正常的信任链验证。
开启强制加密前还要确认一件事:SQL Server服务账号对证书私钥有读取权限。没有权限时服务启动正常,但加密握手必然失败,现象是客户端连上即断开。用证书管理单元给服务账号授予私钥读取权限,这个操作很容易被漏掉。
5. 危险存储过程与补丁:处置顺序与避坑实录
5.1 xp_cmdshell家族:为什么它们是头号清理对象
SHG-Mssql-04-01-01要求停用不必要的存储过程。规范里列出的清单很长,核心危险组件分几组:
| 存储过程组 | 代表成员 | 风险 |
|---|---|---|
| 命令执行 | xp_cmdshell | 执行系统命令,等于给了数据库账号一个shell |
| OLE自动化 | Sp_OACreate、Sp_OAMethod、Sp_OASetProperty | 创建COM对象并调用,可写文件、可执行外部程序 |
| 注册表读写 | Xp_regread、Xp_regwrite、Xp_regdeletekey | 读写Windows注册表,破坏系统配置 |
| 文件系统探测 | xp_dirtree、xp_fixeddrives、xp_subdirs | 枚举目录与磁盘,配合其他漏洞可窃取文件 |
在SQL Server 2000上,这些扩展存储过程默认存在且可直接调用。2005之后,xp_cmdshell默认关闭但可通过sp_configure重新开启。规范原文给的删除命令逐个sp_dropextendedproc,实际在2005以上版本里,很多系统扩展存储过程用sp_dropextendedproc已删不掉,更安全的做法是保持禁用状态:
EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;逻辑说明:先开show advanced options才能看到xp_cmdshell配置项,再显式设为0确保禁用。RECONFIGURE使配置立即生效,不需要重启服务。OLE Automation系列在现代版本里的控制项是'Ole Automation Procedures':
EXEC sp_configure 'Ole Automation Procedures', 0; RECONFIGURE;参数说明:sp_configure的第二个参数只有0和1两个合法值,0为禁用、1为启用。生产环境如果确认业务不需要这些组件,直接保持默认禁用即可,不删存储过程本身,避免删了之后系统组件间引用报错。
5.2 补丁版本基线:2000时代对照表现在怎么用
SHG-Mssql-04-01-02给出的版本对照表是SQL Server 2000的补丁号:
| 版本号 | 补丁级别 |
|---|---|
| 8.00.194 | RTM |
| 8.00.384 | SP1 |
| 8.00.534 | SP2 |
| 8.00.760 | SP3 |
| 8.00.2039 | SP4 |
查询当前版本用:
SELECT @@VERSION;逻辑说明:@@VERSION返回完整的版本字符串,包含版本号、编译时间、处理器架构等信息。SQL Server 2019的版本号是15.x,2022是16.x,规范里那张2000对照表对现代实例已经没有直接参考价值,但思想仍然适用——实例必须处于官方支持的补丁级别。现代检查补丁的方式:
SELECT serverproperty('ProductVersion') AS 版本, serverproperty('Edition') AS 版本类型, serverproperty('ProductLevel') AS 补丁级别;参数说明:ProductLevel返回RTM、SPx或CUx之类的补丁级别标识。拿到结果后去微软官方支持生命周期页面核对,不在支持周期的版本要尽快升级。
5.3 避坑实录:五条真实踩坑记录
现象一:执行规范里的sp_dropextendedproc 'xp_cmdshell'后,SQL Server 2005实例报"找不到存储过程"。原因:2005之后xp_cmdshell不再作为扩展存储过程暴露,而是由配置项管理,sp_dropextendedproc自然找不到对象。解决:改用sp_configure方式关闭,不要强行用DROP命令。
现象二:某应用在强制协议加密开启后,所有连接报"SSL Provider: The certificate chain was issued by an authority that is not trusted"。原因:客户端默认校验服务器证书链,而SQL Server用的是自签名证书。解决:连接串加trustServerCertificate=true;有条件的部门改用CA签发证书,彻底消除客户端告警。
现象三:禁用Named Pipes后,本机某个老备份工具连不上数据库。原因:工具写死了np:localhost连接字符串,走的是Named Pipes通道。解决:把连接字符串改成tcp:localhost或ip地址形式;确认工具不支持TCP时恢复Named Pipes。
现象四:修改SynAttackProtect等注册表项后重启数据库服务,键值仍然为0。原因:TCP/IP协议栈参数是系统级配置,SQL Server服务重启不触发网络栈重新加载,必须重启操作系统或至少重启TCP/IP服务。解决:规划维护窗口重启服务器,重启后复查键值。
现象五:把审计级别设为"全部"后,磁盘剩余空间在一天内从40%掉到10%。原因:成功登录日志量远超预期,Windows事件日志达到上限后开始覆盖,但覆盖前的写放大已经占满数据盘。解决:先估算实例日均登录连接数,审计开启前配置日志大小上限与轮转策略,并把日志目录移到独立的数据盘。
6. 加固前的快照与回滚:我的固定操作习惯
任何一条加固规范落地前,先拍快照再动手,这是我被坑过之后养成的习惯。SQL Server层面的加固动作,快照要做四件事:备份master库、导出登录名清单、导出注册表键值、记录当前服务账号。
备份master库是为了防止账号、角色、配置类的操作不可逆:
BACKUP DATABASE master TO DISK = N'D:\backup\master_加固前.bak' WITH INIT;导出登录名与权限,用一条SQL生成所有登录名的CREATE LOGIN脚本:
SELECT 'IF NOT EXISTS (SELECT * FROM sys.sql_logins WHERE name = ''' + name + ''') CREATE LOGIN [' + name + '] WITH PASSWORD = ' + CONVERT(NVARCHAR(MAX), password_hash, 1) + ' HASHED, CHECK_POLICY = OFF;' FROM sys.sql_logins;逻辑说明:password_hash以varbinary存储,用CONVERT转成十六进制字符串后拼进CREATE LOGIN语句,加HASHED关键字表示直接使用哈希值恢复登录名密码。这套脚本在回退时可以重建所有登录名。
注册表键值的导出,用regedit的"导出"功能或命令:
reg export "HKLM\System\CurrentControlSet\Services\Tcpip\Parameters" D:\backup\tcpip_加固前.reg /y服务启动账号记录下来,直接截图或抄到工单里就行,千万别省。回退SQL Server服务账号比回退登录名麻烦得多,换错账号可能导致服务起不来。
每完成一个编号条目,我强制自己走一遍"变更前留痕、变更后验证、失败即回退"三步。比如改完sa密码,立刻用新密码建立一条测试连接,确认成功后把密码记录放进密码保险箱;改完注册表键值,立刻用Get-ItemProperty读一遍确认写入成功。验证不过就执行回退方案,而不是带着问题往下做。从那以后我每次做数据库加固都强制走一遍快照、变更、验证、留档这个流程,回滚的次数比我预想的多得多,但造成的故障时长从小时级降到了分钟级。希望帮到你。
本文还有配套的精品资源,点击获取