简介:这份Sql Server数据库系统加固规范文档源自中国移动管理信息系统部,面向数据库管理员、系统运维与安全合规人员,针对数据库日常运维中常见的账号共享、弱口令、权限越界、日志缺失、明文传输等安全隐患,提供了一套可落地的安全加固基线。资源为doc格式,压缩包内含1个文档,体积约1.73MB,内容按账号管理认证授权、日志配置、通信协议、设备其他安全要求等章节组织,结构清晰,便于按条目对照检视。已有245人学习下载,适合需要提升生产环境数据库安全等级、应对内外部安全检查或准备加固整改方案的读者参考。文档中每项规范均包含编号、实施目的、问题影响、当前状态查询语句、具体配置操作及回退方案,例如密码长度不少于12字符且有效期不超过90天、登录失败次数限制在5次以内、启用双因素认证、使用SSL/TLS加密通信等,可直接作为SQL Server安全加固的对照清单与操作手册使用。
1. 数据库加固不是跑一遍脚本就完事:Mssql 加固规范能救什么火
做数据库运维的人大概率都遇到过这类尴尬:业务账号有 sysadmin 权限、sa 密码还是 10 年前设的弱口令、xp_cmdshell 敞开着,等被扫出高危漏洞才回头补课。Sql Server 数据库系统加固规范这份材料,本质是一份把账号管理、认证授权、日志配置、通信协议、设备安全要求拆成可执行条目的安全配置基线,编号从 SHG-Mssql-01-01-01 到 SHG-Mssql-04-01-02,每个条目都带实施目的、系统当前状态查询语句、具体操作步骤、回退方案和风险等级。适合两类人:一是刚接手数据库安全整改、不知道从哪下手的运维,照着条目逐条过就能交差;二是要做等保或内部合规检查的 DBA,拿它当基线清单对照现状,省去自己梳理检查项的功夫。说白了,这份规范的价值不在于原理多深,而在于把「安全要求」翻译成了「可执行的 SQL 和配置路径」,照着做,就能把 SQL Server 的暴露面收住一大半。
2. 账号管理与认证授权:把权限和口令理清,先过这六关
账号管理是 SQL Server 加固里最琐碎但也最见效的部分。那份规范文件里这一章占了六个条目,从分账号、清无效账号、限启动权限到最小权限、数据库角色、空密码检查,基本覆盖了生产环境最常见的账号乱象。下面按执行顺序逐个拆解,每个都给出可直接落地的语句和操作路径。
2.1 不同管理员分配不同账号,别让所有人共用一个 sa
规范条目 SHG-Mssql-01-01-01 的要求很朴素:为不同的管理员分配不同的账号,避免共享账号导致权限分不清、出事了没法追溯。现实中很多团队偷懒,几个人共用一个 sa 或者一个高权限账号,平时看似省事,等出问题查审计日志时,根本分不清是哪个人干的,背锅都找不到人。
先看当前系统有哪些登录账号,用这条语句:
USE master SELECT name, password FROM syslogins ORDER BY name说明:syslogins 是 master 库里的系统视图,保存了服务器级别的登录账号信息。password 字段在 SQL Server 2000 里还能看到哈希值,2005 以后这个字段就返回 NULL 了,但查询语句本身不会报错,主要用它看账号列表和排序情况。如果是在 2005 以上的版本,想查登录账号建议改用SELECT name FROM syslogins WHERE islogin = 1或者查 sys.server_principals。
< 创建新账号给不同的管理员分别使用:
-- 创建两个独立的登录账号,密码务必用强口令 sp_addlogin 'user_name_1', 'password1' sp_addlogin 'user_name_2', 'password2'参数说明:user_name_1 和 user_name_2 是两个不同的账号名称,按实际管理员的名字或工号来取,不要叫 admin01、admin02 这种没区分度的名字。password1、password2 只是占位符,实际设置时至少要满足大写字母、小写字母、数字、特殊字符四类中三类以上,长度不低于 10 位。
创建完账号只是第一步,第二步行权分配。规范里的做法是先建立角色并授权,再把角色赋给用户。这样后续有新管理员入职,只要把他加进对应角色就能获得既定权限,离职时从角色里移除即可,不用逐条改权限:
-- 在目标数据库里创建角色 USE your_db EXEC sp_addrole 'db_operator_role' -- 给角色授予需要的权限 GRANT SELECT, INSERT, UPDATE, DELETE ON dbo.orders TO db_operator_role -- 把登录账号映射到数据库用户,并加入角色 USE your_db EXEC sp_grantdbaccess 'user_name_1', 'user_name_1' EXEC sp_addrolemember 'db_operator_role', 'user_name_1'逻辑说明:先把业务库里的对象权限集中授予角色,再把人加入角色。这样做权限的增删都集中在角色上,账号本身只是个「身份标识」,不会出现某个人的账号权限越滚越大、最后谁也说不清他有多少权限的情况。
2.2 删除或锁定无效账号,减少默认入口
规范条目 SHG-Mssql-01-01-02 要求删除或锁定无效账号。所谓无效账号,包括离职员工的账号、长期不用的测试账号、以及安装 SQL Server 时自动创建但从未使用过的默认账号。留着这些账号,等于给攻击者多留了几扇不知道什么时候会打开的门。
查看当前登录账号,方法和 2.1 一样用 syslogins 查列表,然后逐个找管理员确认哪些已经没人用了。确认后按下面方式删除:
-- 删除登录账号(SQL Server 2005+) DROP LOGIN [test_user] GO -- SQL Server 2000 方式 EXEC sp_droplogin 'test_user'补充说明:如果账号在数据库里还映射了用户,直接删登录账号会报错,提示「The database principal owns a schema in the database, cannot be dropped」。这时候需要先删掉或转移该账号在业务库里的用户映射,再回来删登录。规范里的「回退方案」是增加删除的账户,也就是删除前最好把账号名字和权限记录下来,万一误删了还能加回来。
判断依据很简单:询问管理员哪些账号是无效账号,确认后再动手。风险等级标的是高,因为删账号是不可逆操作,删完就找不回来了。
2.3 限制 SQL Server 服务启动账号的权限
这一条(SHG-Mssql-01-01-03)容易被忽略,但它属于典型的「隐形风险」。SQL Server 服务本身跑在一个 Windows 账号下,如果这个账号是 Administrators 组成员,或者拥有本机过高的权限,那 SQL Server 的每一个子进程、每一个扩展存储过程调用,都继承了这份权限。一旦 SQL Server 被注入或攻破,攻击者拿到的就是高权限令牌,而不是单纯的数据库权限。
规范给的思路是:新建一个专门的 SQL Server 服务账号,不要把它加入 User 组,更不能加入 Administrators 组。具体在 Windows 上操作,路径是「服务管理器 → SQL Server (MSSQLSERVER) → 属性 → 登录」,把「此账户」改为专门创建的域账号或本地账号。之后授予这个账号以下最小权限:
- 作为操作系统的一部分(SeTcbPrivilege)——SQL Server 启动时需要
- 调整进程的内存配额(SeIncreaseQuotaPrivilege)
- 替换进程级别令牌(SeAssignPrimaryTokenPrivilege)
- 以服务方式登录(SeServiceLogonRight)——服务账号的基本要求
- 对 SQL Server 数据目录、备份目录、错误日志目录有完全控制权,但只限于这些目录,不能扩大到整个磁盘或系统目录
注意:如果用本地系统账号(Local System)跑 SQL Server,权限是最大的,方便但风险也最大。规范建议的做法是单独建账号并刻意压低权限。回退方案是替换回原来的启动账号,改完之后记得重启 SQL Server 服务才会生效。
2.4 权限最小化:取消业务账号用不上的服务器角色
规范条目 SHG-Mssql-01-01-04 强调权限最小化。生产中常见的翻车现场是:开发为了省事直接把自己的账号加进 sysadmin 固定服务器角色,时间一长就没有人再去收敛,整个数据库等于对开发完全透明。
先查当前哪些账号拥有过高的服务器角色权限:
-- 查看所有登录账号及其服务器角色 SELECT s.name AS login_name, r.name AS server_role FROM sys.server_principals s LEFT JOIN sys.server_role_members m ON s.principal_id = m.member_principal_id LEFT JOIN sys.server_principals r ON m.role_principal_id = r.principal_id WHERE s.type IN ('U', 'S', 'G') ORDER BY s.name这条语句把每个登录账号对应的服务器角色列出来,重点看哪些业务账号挂了 sysadmin、securityadmin、serveradmin 这类高风险角色。逻辑说明:sys.server_role_members 是服务器角色成员关系的系统视图,把它和 sys.server_principals 做两次关联,一次拿账号信息,一次拿角色名字。type 字段里 U 是 Windows 登录、S 是 SQL 登录、G 是 Windows 组。
查出来之后,通过数据库属性界面操作:右键点击实例 → 属性 → 安全性 → 点击对应登录名 → 服务器角色页,把不需要的角色勾选去掉,只保留 public(所有登录默认都有的基础角色)。然后还要检查数据库级别的权限:右键点击业务数据库 → 属性 → 权限 → 查看该账号的「数据库访问许可」和「数据库角色中允许」两个区域,把用不到的数据库访问权限和数据库角色取消。
回退方案是还原添加或删除的权限,所以操作之前建议把账号原有的角色权限截个图存到变更记录里。判断依据是业务测试正常,也就是说权限改完要让业务方做一轮冒烟测试,确认没改坏。
2.5 用数据库角色管权限,别直接给用户授权
规范条目 SHG-Mssql-01-01-05 强调用数据库角色(ROLE)来管理对象权限。道理和 2.1 里说的类似:直接给用户授权,权限分散在每个用户身上,后续梳理和调整都很痛苦;用角色做中转,权限调整只需要改角色,所有成员自动生效。
在 SQL Server 里完整操作是:
-- 第一步:在对应数据库中创建新角色 USE your_db EXEC sp_addrole 'app_read_write_role' -- 第二步:调整角色属性,赋予对象对应的 DML 权限 GRANT SELECT, INSERT, UPDATE, DELETE ON dbo.orders TO app_read_write_role GRANT EXEC ON dbo.sp_get_order_detail TO app_read_write_role -- 第三步:把业务账号加入角色 EXEC sp_addrolemember 'app_read_write_role', 'app_user'参数说明:app_read_write_role 是自定义角色名,按业务含义起;dbo.orders 是业务表,sp_get_order_detail 是存储过程。权限粒度上,只给角色需要的最小指令集,原则是「用不到的不给,不确定的先问业务」。规范里还提到了 DRI 权限,也就是 REFERENCES 权限,主要在表间有外键约束时才需要,平时不用主动授。
回退方案是删除相应的角色:EXEC sp_droprole 'app_read_write_role',但注意角色里有成员时删不掉,需要先把成员移除。
2.6 空密码和弱口令清剿:sa 至少 10 位强口令
规范条目 SHG-Mssql-01-01-06 是针对空密码和弱口令的专项检查。这里有一句关键要求:sa 账号需要设置至少 10 位的强口令。在 SQL Server 2000 时代,sa 空口令就能本地登录,2005 以后安装时会强制设密码,但很多从老版本升级上来的实例或者开发环境,sa 密码依然是弱口令,比如 123456、sa123 这种。
先查当前有没有空口令账号:
-- 查看口令为空的用户(SQL Server 2000 适用) SELECT name, password FROM syslogins WHERE password IS NULL ORDER BY name说明:这条语句在 SQL Server 2000 的 syslogins 视图里,password 字段还能反映口令是否存在。2005 以后 password 字段不再暴露,但依然会返回 NULL,这条语句就失效了,需要换成下面的验证方式:
-- 2005+ 查看密码策略是否启用、密码最后修改时间 SELECT name, is_policy_checked, is_expiration_checked, LOGINPROPERTY(name, 'DaysUntilExpiration') AS days_left FROM sys.sql_logins WHERE is_disabled = 0 ORDER BY name修改指定账号的密码:
USE master EXEC sp_password '旧口令', '新口令', '用户名'回退方案是恢复用户密码到原来状态,所以执行前务必把旧密码先登记到密码管理平台或交给安全负责人保管。规范给的判断依据是再查一遍SELECT name, password FROM syslogins WHERE password IS NULL,确认已经不存在空口令账号。
3. 日志配置与审计:把谁在什么时候干了什么记下来
日志这块在整份规范里篇幅不大,只有一个条目 SHG-Mssql-02-01-01,但恰恰是出事之后唯一能拿来追溯的东西。等保测评里「应配置日志功能,对用户登录进行记录,记录内容包括用户登录使用的账号、登录是否成功、登录时间以及远程登录时用户使用的 IP 地址」这条要求,对应的就是这里的审计级别配置和登录日志排查。
3.1 把审计级别从「无」调到「全部」,别等出了事才后悔
SQL Server 默认的审计级别是「无」,这意味着失败登录和成功登录都不会被记录。现场最常见的场景是:某天发现业务数据被动过,想查是谁做的,打开日志一看什么也没有,只能干瞪眼。
正确配置路径如下:
-- 推荐用 SQL 语句开启审计,效果等同于 SSMS 界面操作 USE master GO EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'AuditLevel', REG_DWORD, 3 GO说明:AuditLevel 的取值含义是 0 = 无审计,1 = 仅记录成功登录,2 = 仅记录失败登录,3 = 记录全部登录。规范要求调整为「全部」,也就是 3,这样无论攻击者用密码猜解尝试登录还是正常用户登录,都会留下痕迹。xp_instance_regwrite 是 SQL Server 提供的扩展存储过程,用于写实例级别的注册表配置,修改后需要重启 SQL Server 服务才生效。
SSMS 图形界面路径是:右键点击实例 → 属性 → 安全性 → 在「审核」区域把「无」改为「全部」,同时把服务器身份验证改为「SQL Server 和 Windows 模式」。注意,只有改成混合验证模式,SQL 账号的登录审计才有意义,纯 Windows 验证模式下 SQL 账号根本不存在。
开启审计后,日志看得见摸得着:
-- 查看 SQL Server 错误日志,重点看登录相关记录 EXEC xp_readerrorlog 0, 1, N'Login', NULL, NULL, N'DESC', N'DESC'这条语句会按时间倒序把错误日志里带 Login 关键字的记录列出来,每条日志会显示「登录日期、是否成功、账号名、来源 IP」。逻辑说明:xp_readerrorlog 的前两个参数是日志文件编号和日志类型(1 = 当前错误日志),第三个参数是过滤关键字,后两个 NULL 是过滤字段边界,DESC 表示倒序。实际排查时,搜关键字Login failed可以快速定位密码猜解攻击。
3.2 登录日志怎么看:从语句输出里区分攻击和正常访问
审计开启之后,日志量会明显变大,尤其是数据库每天被各类监控工具、定时任务反复连的情况下。这里讲一下我自己的排查习惯。
搜索失败登录记录:
EXEC xp_readerrorlog 0, 1, N'Login failed', NULL, NULL, N'DESC', N'DESC'搜索某个账号的所有登录:
EXEC xp_readerrorlog 0, 1, N'user_name_1', NULL, NULL, N'DESC', N'DESC'排错经验:如果在错误日志里看到大量来自同一个 IP 的 Login failed,每几秒一条,说明有人在跑密码字典,要立刻在 Windows 防火墙层封掉这个 IP。如果看到 Login failed for user 'sa' 且来源 IP 是内网段,可能是某个老系统的连接串里 sa 密码已经改掉了但应用没更新,这时要反过来查那个 IP 对应的服务器上跑了什么服务。
推荐的做法是把审计级别调到 3 之后留一周,观察日志量是否大到影响性能。对大多数业务系统来说,登录日志的量级很小,根本不会拖慢性能,但日志文件增长是有的,要注意错误日志轮转策略。
4. 通信协议与网络安全:收紧网络面,别让数据库裸奔
通信协议这一章包含三个条目:只保留 TCP/IP 并禁用其余协议、加固 TCP/IP 协议栈的注册表键值、强制协议加密。这一章的操作和其他章节不太一样,除了 SQL Server 自身的配置,还涉及到 Windows 注册表和底层网络参数,操作时要格外小心。
4.1 网络协议最小化:只留 TCP/IP,禁用 Named Pipes 和 Shared Memory
SQL Server 默认会启用 Shared Memory、Named Pipes、TCP/IP 等多种协议。Shared Memory 只用于本机连接,Named Pipes 在跨网段场景下效率低且历史上出过安全问题,规范明确建议只使用 TCP/IP 协议,禁用其他协议。
查看当前启用了哪些协议,用 SQL Server 配置管理器:
# 打开 SQL Server Configuration Manager # 路径:SQL Server 配置管理器 -> SQL Server 网络配置 -> 实例名 -> 协议在配置管理器里,右键 TCP/IP 选择启用,右键 Named Pipes 和 Shared Memory 选择禁用。操作完成后,重启 SQL Server 服务生效。
判断依据:重新打开服务网络实用工具,查看协议列表,确认只有 TCP/IP 处于启用状态。这里有个需要注意的细节:如果你本机的一些管理工具(比如某些版本的 SSMS 第一次连本机)依赖 Shared Memory,把 Shared Memory 禁用后可能会出现「无法连接,请检查网络或实例名」的报错,遇到这种情况不用慌,连接串里显式指定tcp:前缀或改用127.0.0.1即可。
回退方案:在配置管理器里把禁用的协议重新启用即可,不需要动任何数据文件。
4.2 加固 TCP/IP 协议栈:三个注册表键值堵住路由器级攻击
这一节(SHG-Mssql-03-01-02)是整份规范里技术含量最高但也最容易出错的部分。它加固的不是 SQL Server 本身,而是承载 SQL Server 的 Windows 操作系统底层的 TCP/IP 协议栈。要检查三个注册表键值:
| 注册表键 | 推荐值 | 防什么攻击 |
|---|---|---|
HKLM\System\CurrentControlSet\Services\Tcpip\Parameters\DisableIPSourceRouting | 2 | 源路由欺骗攻击 |
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\EnableICMPRedirect | 0 | ICMP 重定向攻击 |
HKLM\System\CurrentControlSet\Services\Tcpip\Parameters\SynAttackProtect | 2 | SYN Flood 攻击 |
用命令检查当前值:
reg query "HKLM\System\CurrentControlSet\Services\Tcpip\Parameters" /v DisableIPSourceRouting reg query "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v EnableICMPRedirect reg query "HKLM\System\CurrentControlSet\Services\Tcpip\Parameters" /v SynAttackProtect如果查出来不是推荐值,用下面的命令修改:
reg add "HKLM\System\CurrentControlSet\Services\Tcpip\Parameters" /v DisableIPSourceRouting /t REG_DWORD /d 2 /f reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v EnableICMPRedirect /t REG_DWORD /d 0 /f reg add "HKLM\System\CurrentControlSet\Services\Tcpip\Parameters" /v SynAttackProtect /t REG_DWORD /d 2 /f参数说明:DisableIPSourceRouting 设置为 2 表示完全禁止 IP 源路由,这样攻击者就无法通过伪造源路由头部来绕过访问控制;EnableICMPRedirect 设置为 0 表示禁用 ICMP 重定向,防止攻击者通过伪造 ICMP 重定向报文改变路由表;SynAttackProtect 设置为 2 表示启用 SYN 攻击保护,当 SYN 半连接数达到阈值时系统会触发 synattack 保护机制,降低被 SYN Flood 打挂的概率。
这里几个坑要讲清楚:一是注册表修改需要重启 Windows 才生效,不仅仅是重启 SQL Server;二是这三个键值改完后,某些依赖多网卡路由的服务器可能出现路由行为变化,生产环境务必在变更窗口操作并提前做网络连通性测试;三是 SynAttackProtect 在 Windows Server 2012 以后系统里默认就有保护机制,键值可能查不到,如果查不到不用强行添加,说明系统已经内置了同类保护。
回退方案:把键值改回原来的数(一般 DisableIPSourceRouting 原值是 0、EnableICMPRedirect 原值是 1、SynAttackProtect 可能不存在),重启网络或重启系统即可。判断依据:重新reg query读取三个键值确认。
4.3 强制协议加密,堵住明文抓包风险
规范条目 SHG-Mssql-03-01-04 要求设置「强制协议加密」。SQL Server 默认情况下客户端和服务端之间的 TDS 数据包是明文传输的,只要有人在网络上抓包,就能直接看到 SQL 语句里的表名、字段名、甚至是 WHERE 条件里的业务数据。虽然密码字段是哈希过的,但 SQL 语句里的数据内容会裸露出来,对安全性要求高的系统这不能接受。
老版本 SQL Server 用服务器网络实用工具来配置,路径是:开始菜单 → Microsoft SQL Server 程序组 → 服务器网络实用工具 → 常规 → 勾选「强制协议加密」。在 SQL Server 2005 以后版本里对应的功能在 SQL Server 配置管理器里:
# SQL Server 配置管理器 -> SQL Server 网络配置 -> 协议 -> 右键 TCP/IP -> 属性 -> 标志页 # 找到 "Force Encryption",设为 "Yes"设置成 Yes 之后,要求所有客户端连接都使用 SSL 加密,否则拒绝连接。但这个操作有一个连带要求:服务器上需要安装证书。如果没有安装由受信任 CA 签发的证书,SQL Server 会使用自签名证书,客户端的 SSMS 或应用连接时会弹出证书不受信任的警告,某些老版本 JDBC 驱动甚至可能直接连不上。
更好的做法是把「强制加密」打开的同时,确保服务器已申请并安装了合法 CA 证书。如果因为证书问题导致现有应用连不上,规范里的回退方案是恢复「强制协议加密」到原状态,也就是设回 No,等证书准备好再切。实际执行时我的建议是:先让业务方用测试环境验证 JDBC/ODBC 连接串是否兼容加密连接,再在生产上切换,切换后立刻用一个独立账号做一轮完整登录测试。
5. 常见问题排查:执行规范时最容易翻车的五个场景
把 01、02、03、04 四章的操作真正落到生产环境时,会有一些文档里没写透的隐蔽问题。这一章每条都是实际执行过才踩到的坑,按「现象 → 原因 → 解决」的方式记录,给你当现场排除手册用。
5.1 删了 xp_cmdshell 之后,业务系统报错「SQL Server 阻止了对组件 xp_cmdshell 的访问」
现象:执行完规范里 4.1.1 的存储过程清理脚本后,原本跑得好好的某个报表任务突然全部失败,应用日志里报SQL Server blocked access to procedure 'xp_cmdshell'。
原因:业务系统里有功能实际依赖 xp_cmdshell 调用外部程序,比如某些老系统用 xp_cmdshell 调 bat 脚本做数据导出。当时只按规范删了存储过程,没有先做依赖排查。
解决:临时恢复生产上的 xp_cmdshell,同时联系业务方确认具体用途,推动业务改造。恢复命令:
USE master EXEC sp_addextendedproc 'xp_cmdshell'如果 SQL Server 2005 以上版本,推荐不恢复扩展存储过程,而是改用 SQL Server Agent 的作业步骤去调用外部程序,或者用 CLR 存储过程替代,从根本上降低风险。从那以后我每次做危险存储过程删除前,都会先跑一遍下面的依赖排查:
-- 查找哪些存储过程/作业/视图引用了 xp_cmdshell SELECT OBJECT_NAME(object_id) AS obj_name, definition FROM sys.sql_modules WHERE definition LIKE '%xp_cmdshell%'5.2 在部署了 SQL Server 集群(Cluster)的机器上改 TCP/IP 协议栈注册表,心跳断了
现象:在一套 SQL Server 故障转移集群上执行 4.2 的注册表加固后,集群节点的私有心跳网络出现频繁抖动,甚至触发了一次故障转移。
原因:DisableIPSourceRouting 设为 2 后,某些依赖源路由选项的旧版多播或心跳网络会有异常,集群心跳使用的是节点间专用网络,某些老环境的网卡驱动对源路由协议有依赖。
解决:集群环境的网络协议栈加固不能一刀切。把 DisableIPSourceRouting 从 2 回退到 1(表示丢弃来自非本机接口的源路由包,但允许本机处理的包),然后重新做集群健康检查。回退命令:
reg add "HKLM\System\CurrentControlSet\Services\Tcpip\Parameters" /v DisableIPSourceRouting /t REG_DWORD /d 1 /f另一条经验:集群服务器上做注册表修改前,先检查 Windows 故障转移集群的cluster.log确认心跳网络没有依赖项,再执行变更。
5.3 审计级别改成「全部」后,错误日志文件暴涨,一晚上写了 5GB
现象:把审计级别调到 3 之后,发现 SQL Server 错误日志目录下的 ERRORLOG 文件在业务高峰期快速膨胀,磁盘空间告警。
原因:审计级别为「全部」时,任何登录成功或失败都会写日志。如果业务系统使用连接池,每一条新建连接的 Login 成功记录都会写入,高并发系统一晚上产生几十万条登录记录是常事。
解决:不要直接调回 0,正确姿势是配合日志轮转策略。SQL Server 错误日志默认每 6 次重启或手动轮转才会生成新文件。手动轮转命令:
EXEC sp_cycle_errorlog把轮转频率提高并设置日志文件上限,然后在 SQL Server Agent 里建一个每小时的作业调用sp_cycle_errorlog,再配合 Windows 的计划任务清理超过 7 天的日志文件。这样既保留审计能力,又控制磁盘占用。
5.4 sa 密码改强口令之后,好几个老业务连不上了
现象:按 2.6 的规范把 sa 密码改成 15 位强口令后,当天夜里批量作业开始报登录失败。
原因:老业务系统的连接串里 sa 密码是明文写死的,改密码后应用不会自动读取新密码。常见于供应商已失联的老系统,或者交接不完整、没人知道哪个文件里写死了连接串。
解决:生产环境改 sa 密码前,先抓取系统里所有包含Password=或pwd=的配置文件。我自己常用的排查方式是在数据库服务器上搜索:
findstr /s /i /m "password=" C:\app\*.config C:\app\*.xml C:\app\*.properties把搜索到的文件列表发给业务方逐项确认。如果实在有系统没法当天改连接串,可以先把旧密码记录到变更记录里,给业务方留 24 小时缓冲期,第二天窗口期统一切换。
5.5 强制协议加密打开后,JDBC 连接报 SSL 证书错误
现象:执行 4.3 强制协议加密之后,Java 应用连数据库时报The server selected protocol version TLS10 is not accepted by client preferences [TLS12]或者证书信任错误。
原因:老版本 SQL Server(2008/2008 R2)默认仅支持 TLS 1.0,而新版本 JDBC 驱动默认要求 TLS 1.2;同时服务器用自签名证书,JVM 的信任库不认。
解决:分两步走。给服务器安装受信任的 CA 证书(从正规证书服务商申请或内部 CA 签发),然后在 JDBC 连接串里显式指定加密参数和 TLS 版本。连接串参考:
jdbc:sqlserver://10.1.2.3:1433;databaseName=erp;encrypt=true;trustServerCertificate=true;sslProtocol=TLSv1.2参数说明:encrypt=true开启加密;trustServerCertificate=true表示暂时信任服务器证书(测试环境用,生产环境这句要删掉);sslProtocol=TLSv1.2指定协议版本。如果 SQL Server 版本太老不支持 TLS 1.2,优先考虑升级 SQL Server,不要为了兼容降低 JDK 的 TLS 版本要求。
6. 把整份规范变成自动化核验脚本:半小时扫完所有实例
规范的条目再多,人工逐条核对终究费时费力,而且容易漏项。这里分享一个我常用的做法:把规范里的「系统当前状态」的查询语句收集起来,拼成一个批量检查脚本,用 SQLCMD 一次跑完所有实例,输出一份 Markdown 格式的核验报告。这套方法在几十个实例的巡检场景下很实用。
先创建一个存储过程或 SQL 脚本文件sqlserver_hardening_check.sql,内容覆盖账号、权限、审计、协议、危险存储过程五个维度:
SET NOCOUNT ON; PRINT '===== 1. 账号与权限检查 ====='; -- 检查 sysadmin 角色的成员,确认没有多余账号 SELECT 'sysadmin_members' AS check_item, s.name AS login_name FROM sys.server_role_members m JOIN sys.server_principals r ON m.role_principal_id = r.principal_id JOIN sys.server_principals s ON m.member_principal_id = s.principal_id WHERE r.name = 'sysadmin'; -- 检查是否存在空密码 SQL 登录(密码策略未启用且密码为空) SELECT 'blank_password' AS check_item, name FROM sys.sql_logins WHERE password_hash IS NULL AND is_disabled = 0; PRINT '===== 2. 审计级别检查 ====='; -- 读取审计级别注册表值,应为 3 EXEC xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'AuditLevel'; PRINT '===== 3. 危险扩展存储过程检查 ====='; -- 检查 xp_cmdshell 等扩展存储过程是否还存在 SELECT 'dangerous_proc' AS check_item, o.name FROM sys.objects o WHERE o.name IN ('xp_cmdshell', 'xp_dirtree', 'xp_fixeddrives', 'xp_regread', 'sp_OACreate') AND o.type = 'X';脚本逻辑分三段:第一段查 sysadmin 角色成员和空密码登录,这是账号管理检查的核心;第二段用 xp_instance_regread 读审计级别注册表值,正常应为 3;第三段查危险扩展存储过程是否还在,列出所有没删干净的。运行这段脚本的方式是在命令行里对每个实例执行:
sqlcmd -S 10.1.2.3 -U sa -P "密码" -i sqlserver_hardening_check.sql -o check_result.txt如果怕密码泄露在命令行里,可以把连接信息写到脚本文件里,用-i直接调用,或者使用-E走 Windows 身份验证:
sqlcmd -S 10.1.2.3 -E -i sqlserver_hardening_check.sql -o check_result.txt跑完之后打开 check_result.txt,重点关注三类输出:sysadmin_members 列表里有没有不应该出现的账号;blank_password 有没有返回记录;dangerous_proc 有没有输出。只要这三项全空、AuditLevel 读出来是 3,这台实例的核心加固项基本就到位了。
文件输出之后,建议把脚本一并保存到运维文档库里,每个月做一次月度巡检时重新跑一遍,把结果和上个月的对比,新增加的账号和权限变化一目了然。这个做法比任何监控工具都直接,因为查的就是规范条目本身,不存在「监控漏配」的问题。从那以后我每次接手新的数据库实例,第一件事就是跑一遍这个脚本,三十秒内就知道这台机器在安全上处于什么状态,不用再靠感觉判断。希望帮到你。
本文还有配套的精品资源,点击获取