SQL Server安全加固规范落地:账号、日志、协议与危险存储过程治理
简介面向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、Pssw0rd这种被字典收录的。sa口令的更新要慎之又慎改之前确认所有依赖sa连接的连接字符串都同步更新否则改完密码整个业务连不上库那就是典型的加固引发生产事故。3. 日志配置审计级别全部与登录记录的核查路径3.1 审计级别为什么要调到全部SHG-Mssql-02-01-01要求启用日志记录功能记录内容包括登录账号、登录是否成功、登录时间、远程登录IP。原文的配置路径是数据库属性-安全性-审计级别调整为全部。这个设置在SQL Server 2000里是数据库属性页的选项到了2005之后被挪到服务器属性下的安全性页叫登录审核选项有无、仅失败登录、仅成功登录、全部。这里有个执行细节容易被忽略审计级别全部会同时记录成功和失败的登录尝试。只会记录失败排查密码暴力破解够用但合规审计要求看到谁在什么时间成功登录过必须选全部。命令层面可以用注册表方式直接写入EXEC xp_instance_regwrite NHKEY_LOCAL_MACHINE, NSoftware\Microsoft\MSSQLServer\MSSQLServer, NAuditLevel, 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 NHKEY_LOCAL_MACHINE, NSoftware\Microsoft\MSSQLServer\MSSQLServer, NLoginMode;返回的REG_DWORD值为1表示仅Windows身份验证2表示混合模式。需要改成混合模式时EXEC xp_instance_regwrite NHKEY_LOCAL_MACHINE, NSoftware\Microsoft\MSSQLServer\MSSQLServer, NLoginMode, REG_DWORD, 2;注意修改LoginMode后同样要重启服务。而且从仅Windows模式切到混合模式后所有SQL登录名重新获得登录资格如果之前有些账号密码是空的这一步会瞬间打开多个入口。所以在切模式之前务必先按上一章的检查脚本把空密码账号清干净。3.3 登录日志去哪儿看开启审计后登录事件会写入Windows应用程序日志来源为MSSQLSERVER。查看方式事件查看器 - Windows日志 - 应用程序筛选来源为MSSQLSERVER。用PowerShell查询近24小时的登录失败记录Get-WinEvent -FilterHashtable {LogNameApplication; ProviderNameMSSQLSERVER; 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。整理如下键名路径期望值防御目标DisableIPSourceRoutingHKLM\System\CurrentControlSet\Services\Tcpip\Parameters2禁止IP源路由防止攻击者用源路由选项绕过访问控制EnableICMPRedirectHKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters0关闭ICMP重定向防止路由表被恶意篡改SynAttackProtectHKLM\System\CurrentControlSet\Services\Tcpip\Parameters2开启SYN攻击保护减缓SYN Flood冲击这三项的意义要对应到数据库场景SQL Server通常承载核心业务数据攻击者拿不到应用层入口时会尝试从网络层下手。源路由欺骗可以构造一条绕过防火墙的路径访问数据库端口ICMP重定向可以把数据包导向攻击者的机器SYN Flood则是经典拒绝服务手段直接打满数据库的监听队列。这三个键值设好是把网络层的攻击面收窄。修改用管理员PowerShellSet-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业务库;encrypttrue;trustServerCertificatetrue;参数说明encrypttrue要求连接走加密trustServerCertificatetrue表示信任服务器自签名证书。如果SQL Server只装了自签名证书客户端必须信任它否则握手失败如果生产环境已有CA签发的证书则可以去掉trustServerCertificate并配置正常的信任链验证。开启强制加密前还要确认一件事SQL Server服务账号对证书私钥有读取权限。没有权限时服务启动正常但加密握手必然失败现象是客户端连上即断开。用证书管理单元给服务账号授予私钥读取权限这个操作很容易被漏掉。5. 危险存储过程与补丁处置顺序与避坑实录5.1 xp_cmdshell家族为什么它们是头号清理对象SHG-Mssql-04-01-01要求停用不必要的存储过程。规范里列出的清单很长核心危险组件分几组存储过程组代表成员风险命令执行xp_cmdshell执行系统命令等于给了数据库账号一个shellOLE自动化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 ProceduresEXEC 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.194RTM8.00.384SP18.00.534SP28.00.760SP38.00.2039SP4查询当前版本用SELECT VERSION;逻辑说明VERSION返回完整的版本字符串包含版本号、编译时间、处理器架构等信息。SQL Server 2019的版本号是15.x2022是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用的是自签名证书。解决连接串加trustServerCertificatetrue有条件的部门改用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 ND:\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读一遍确认写入成功。验证不过就执行回退方案而不是带着问题往下做。从那以后我每次做数据库加固都强制走一遍快照、变更、验证、留档这个流程回滚的次数比我预想的多得多但造成的故障时长从小时级降到了分钟级。希望帮到你。本文还有配套的精品资源点击获取