简介面向SQL Server数据库管理员与安全运维人员的加固规范文档围绕账号管理、认证授权、日志配置、通信协议和设备安全等维度给出SHG-Mssql系列控制项的整改要求与操作建议。全文以统一编号逐条展开便于对照现状自查和落地整改。资源为单个doc格式文档压缩包仅1.73MB轻量易用。已有245人学习适合企业数据库基线核查、等保加固和内部安全培训。文档不仅明确了最小权限、密码策略、登录失败锁定、双因素认证等账号安全措施还覆盖日志审计、SSL/TLS加密通信、防火墙与入侵检测部署以及服务器防毒和定期安全检查等内容每一条目均注明实施目的、问题影响、当前状态查询、参考配置操作和回退方案可帮助快速搭建规范化加固流程提升SQL Server整体安全性。1. Sql Server数据库系统加固规范先读懂这一套检查项到底在防什么一套 Sql Server 数据库系统加固规范拆开看无非三块身份认证怎么收、权限面怎么缩、服务面和网络面怎么关最后再补一层审计。真正动手做过的人会告诉你这活儿难的不是照着清单改而是你手里的实例已经跑了好几年——sa 的密码被写死在三个历史应用的连接串里sysadmin 角色挂着七八个没人认领的账号xp_cmdshell 被某个离职同事留下的作业天天调用。强制加固就是在这样的存量系统上做外科手术每一步都要留回退路径。这篇笔记面向正在做合规整改或内部安全基线核查的 DBA、运维和安全工程师按「原理 → 改法 → 踩坑 → 验证」的顺序把一条能直接复现的落地路径讲透。2. Sql Server 身份认证与口令策略先焊死 sa 和弱口令这两个入口2.1 为什么身份层是加固的第一优先级任何一次拖库事件的还原里入口大概率是同一个一个能被猜中的口令或者一个没人管的旧账号。Sql Server 的身份层是攻击者接触到的第一道门所以绝大多数加固规范把认证和口令放在第一节。这里要分清楚两种认证模式Windows 集成认证也叫仅 Windows 身份验证下登录走操作系统账号体系密码复杂度、锁定阈值都由域策略统一监管数据库本身不存口令混合模式下Sql Server 自己维护一套 SQL 登录名和密码一旦 DBA 图省事把密码策略关掉就会出现一批「有账号、无复杂度约束、永不过期」的历史遗留登录名。很多业务当年为了兼容老应用选的是混合模式所以真正要花力气的是 SQL 登录名这一层。加固的常规目标很明确实例内不存在被禁用的 sa 之外的管理员裸奔账号所有 SQL 登录名都启用密码策略定期轮换口令登录事件进审计。先搞清楚自己实例的认证模式是开始一切改动的第一步。2.2 关闭 sa 并建立替代管理账号最小改动顺序sa 是内置固定账号删不掉规范做法是「禁用 用替代账号接管管理」。注意顺序不能反先建好替代管理员再禁用 sa否则你在一个没有管理入口的实例上把自己锁在门外只能折腾单用户模式启动那就被动了。-- 1) 查看当前认证模式 SELECT CASE SERVERPROPERTY(IsIntegratedSecurityOnly) WHEN 1 THEN WINDOWS_ONLY ELSE MIXED END AS auth_mode; -- 2) 建立替代管理员请替换为真实强密码 CREATE LOGIN [SecAdmin] WITH PASSWORD 此处替换为强密码, CHECK_POLICY ON, CHECK_EXPIRATION ON; ALTER SERVER ROLE sysadmin ADD MEMBER [SecAdmin]; -- 3) 确认无连接依赖后禁用 sa ALTER LOGIN sa DISABLE;SERVERPROPERTY(IsIntegratedSecurityOnly)返回 1 表示实例当前是仅 Windows 认证混合模式返回 0这决定了你是否需要处理 SQL 登录名。CHECK_POLICY ON让该登录名遵循 Windows 密码复杂度策略长度、大小写、数字、特殊字符CHECK_EXPIRATION ON开启口令过期轮换。替代管理员加入 sysadmin 后实例仍然有完整管理能力但日常操作不再依赖内置 sa 账号。我有一次给客户做整改直接执行了第三步结果半夜支付应用全线报登录失败——连接串里写死了 sa。后来的习惯是禁用 sa 之前先跑一遍sys.dm_exec_sessions看有没有 sa 会话在跑再去配置中心搜 sa 关键字确认干净了才动手。2.3 登录名密码策略核查把弱策略账号一次性揪出来Sql Server 的sys.sql_logins里直接暴露了每个 SQL 登录名的策略状态这一步不需要猜一条查询把所有「裸奔」账号列出来。-- 列出所有 SQL 登录名的策略开关与禁用状态 SELECT sp.name AS login_name, sp.is_disabled, sp.is_policy_checked, sp.is_expiration_checked, LOGINPROPERTY(sp.name, IsMustChange) AS must_change FROM sys.server_principals sp WHERE sp.type IN (S, U) AND sp.name NOT LIKE ##% ORDER BY sp.is_policy_checked, sp.name;is_policy_checked 0的账号就是没有强制密码复杂度的is_expiration_checked 0表示口令永不过期IsMustChange 1表示该账号下次登录必须改密。筛选出结果后对每个需要保留的账号执行修正-- 为某个业务账号启用策略并重置口令 ALTER LOGIN [业务账号] WITH PASSWORD 新强密码, CHECK_POLICY ON, CHECK_EXPIRATION ON;如果有些账号密码没法立刻改应用侧还没排期至少先把策略开关打开让复杂度约束先生效。还有一个容易漏的检查点用SELECT name FROM sys.sql_logins WHERE password_hash IS NULL;找出没有口令的登录名这类账号必须直接删除或禁用。口令轮换最怕的就是改完密码应用连不上我一般会要求应用侧先把新密码写进配置中心、触发连接池重连再在数据库侧执行ALTER LOGIN两边顺序不能倒。3. Sql Server 权限体系加固sysadmin 收敛、guest 禁用与最小授权落地3.1 服务级权限sysadmin 成员与固定服务器角色边界sysadmin 在 Sql Server 里约等于操作系统管理员能读内存、能开跟踪、能删库权限大到没法审计。加固规范里的硬性要求通常是sysadmin 成员控制在两到三个运维账号以内且必须是真实在职人员认领的账号不允许存在「不知道谁在用」的成员。-- 查看固定服务器角色的成员清单 SELECT r.name AS server_role, m.name AS member_name, m.type_desc, m.is_disabled FROM sys.server_role_members rm JOIN sys.server_principals r ON rm.role_principal_id r.principal_id JOIN sys.server_principals m ON rm.member_principal_id m.principal_id WHERE r.name IN (sysadmin, serveradmin, securityadmin, setupadmin, processadmin, diskadmin, bulkadmin) ORDER BY r.name;结果里凡是找不到归属人的 sysadmin 成员走审批流程后移除。移除动作很简单ALTER SERVER ROLE sysadmin DROP MEMBER [待移除账号];这里要给个关键建议无论怎么收敛至少要保留两个管理入口比如一个 Windows 域账号加一个 SQL 登录名分散在不同人手里。只留一个账号的风险是这个人离职、口令遗忘、或者域策略变更导致账号失效整个实例就失去管理入口了。另外serveradmin 能修改服务器配置甚至 shutdown 实例securityadmin 能重置所有登录名口令这两个角色的成员同样要纳入评审不能只盯 sysadmin。3.2 数据库级权限guest、公共角色与孤立账号服务级收完接下来是数据库级。三个高频问题guest 账号能不能连库、public 角色有没有被塞进额外权限、以及一堆孤立账号有没有清理。-- 查看哪些用户库的 guest 还带着 CONNECT 权限 SELECT d.name AS database_name, HAS_PERMS_BY_NAME(d.name, DATABASE, CONNECT) AS guest_connect FROM sys.databases d JOIN sys.database_principals dp ON dp.name guest AND dp.type S WHERE d.database_id 4 AND HAS_PERMS_BY_NAME(d.name, DATABASE, CONNECT) 1;查到之后逐库回收USE [你的业务库]; REVOKE CONNECT FROM guest;master和tempdb里的 guest 是系统必需的不要动。接下来查 public 角色有没有被滥用——public 默认只有CONNECT如果业务授权时图省事直接GRANT SELECT TO public那就等于把表敞开给所有能登录实例的人。-- 列出所有授予给 public 的数据库权限 SELECT state_desc permission_name AS permission_info, major_id FROM sys.database_permissions WHERE grantee_principal_id DATABASE_PRINCIPAL_ID(public) AND permission_name CONNECT;这里major_id对应的对象类型可以通过class_desc判断查出异常授权后准确执行对应REVOKE。孤立账号是另一个经典问题数据库里的用户映射不到任何服务器登录名多半是账号重建或域名迁移后的残留既占空间又留隐患。-- 找出当前库的孤立数据库用户 SELECT dp.name AS db_user, dp.type_desc FROM sys.database_principals dp LEFT JOIN sys.server_principals sp ON dp.sid sp.sid WHERE dp.type IN (S, U) AND dp.name NOT IN (dbo, guest, sys, INFORMATION_SCHEMA) AND sp.sid IS NULL;确认无用直接删除还需要用的执行ALTER USER [账号] WITH LOGIN [账号];恢复映射。3.3 最小授权落地方案自定义角色、架构与模块签名很多团队给应用账号开db_owner省事这是 Sql Server 权限管理里最常见的债。规范做法是按业务功能拆角色按架构授权。比如一个订单库默认dbo架构给只读账号SELECT给应用账号EXECUTE再单独建一个报表账号只管读USE [你的业务库]; -- 建业务角色 CREATE ROLE [app_readonly]; CREATE ROLE [app_executor]; -- 按架构授权 GRANT SELECT ON SCHEMA::dbo TO [app_readonly]; GRANT EXECUTE ON SCHEMA::dbo TO [app_executor]; -- 把已有数据库用户加入角色 ALTER ROLE [app_readonly] ADD MEMBER [report_user]; ALTER ROLE [app_executor] ADD MEMBER [app_service_user];SCHEMA::dbo是架构级授权比对象级授权省事且不会漏新表EXECUTE只授予执行权明确不给底层表访问。如果未来新表要默认让所有只读角色可见把表建在授权过的架构里即可。遇到跨架构访问的存储过程报权限错多数是所有权链断裂。常见方案是模块签名用证书给存储过程签名再把证书映射的用户授予所需权限。流程如下USE master; CREATE CERTIFICATE [AppCert] WITH SUBJECT 业务签名证书; CREATE LOGIN [AppCertLogin] FROM CERTIFICATE [AppCert]; USE [你的业务库]; CREATE USER [AppCertUser] FROM CERTIFICATE [AppCert]; GRANT SELECT ON SCHEMA::ref TO [AppCertUser]; -- 对存储过程签名 ADD SIGNATURE TO [dbo].[proc_order_pay] BY CERTIFICATE [AppCert];签名后proc_order_pay执行时临时获得AppCertUser的权限调用方不需要直接持有底层表权限。这个手法适合存量系统里改不动代码的场景比临时给 EXECUTE 授权干净也比把账号提成 db_owner 安全得多。4. Sql Server 服务面与网络面加固高危配置项、协议收敛与审计落地4.1 高危配置项清单与一键禁用脚本Sql Server 为了兼容装了不少默认关闭、一旦打开就有风险的功能。加固规范的常规动作是先看sys.configurations里这些项的真实状态然后全部设为 0。清单如下配置项作用风险说明加固值xp_cmdshell允许 T-SQL 调用操作系统命令拿到 SQL 权限即可执行系统命令0Ole Automation Procedures允许 T-SQL 调用 COM 对象可能被用来做文件读写0clr enabled允许加载 CLR 程序集历史漏洞面大多数业务用不到0Ad Hoc Distributed Queries允许 OPENROWSET 等临时分布式查询容易被利用做内网探测0Database Mail XPs数据库邮件功能用不到时保留有被滥用风险0remote access允许远程存储过程调用影响 linked server0scan for startup procs启动时扫描自动执行存储过程配合启动后门0-- 查看上述配置项当前状态 EXEC sp_configure show advanced options, 1; RECONFIGURE; SELECT name, value, value_in_use FROM sys.configurations WHERE name IN ( xp_cmdshell, Ole Automation Procedures, clr enabled, Ad Hoc Distributed Queries, Database Mail XPs, remote access, scan for startup procs);value是配置值value_in_use是当前运行值。改动配置后必须RECONFIGURE才会生效非动态项要重启服务。禁用脚本如下EXEC sp_configure xp_cmdshell, 0; EXEC sp_configure Ole Automation Procedures, 0; EXEC sp_configure clr enabled, 0; EXEC sp_configure Ad Hoc Distributed Queries, 0; EXEC sp_configure Database Mail XPs, 0; EXEC sp_configure remote access, 0; EXEC sp_configure scan for startup procs, 0; RECONFIGURE;有没有例外有。remote access设为 0 后链到该实例的 linked server 调用会失败Database Mail XPs设为 0 后SQL Agent 的邮件告警会失效。所以改之前先问业务有没有依赖 linked server 的报表有没有依赖邮件报警的作业禁用不是目的把风险面收窄才是。4.2 网络协议、动态端口与 SQL Browser 收敛网络层加固的目标是能用 TCP 就不要开多余协议能用固定端口就不要让端口漂移。先看实例实际监听的端口-- 查看当前 TCP 监听端口与地址 SELECT local_tcp_port, local_net_address, client_net_address FROM sys.dm_exec_connections WHERE session_id SPID; -- 查看实例所有 TCP 监听 SELECT ip_address, port, type_desc, state_desc FROM sys.dm_tcp_listener_states;默认情况下 Sql Server 开着 Shared Memory、Named Pipes、TCP/IP 三个协议。Shared Memory 只服务本机进程一般保留Named Pipes 大多数业务用不上可以直接禁用。实际操作在 Sql Server 配置管理器的「实例协议」里完成禁用 Named PipesTCP/IP 保持启用然后在 TCP/IP 属性 → IP 地址页把每个 IP 的「TCP 动态端口」清空「TCP 端口」填固定值比如 1433 或者你们网络规划的专用端口。这样做的原因是动态端口模式下实例每次重启端口都可能漂移防火墙白名单会失效SQL Browser 也必须常驻来做端口解析。固定端口之后SQL Browser 服务就可以停了——它只在命名实例需要动态解析端口时才必要固定端口 防火墙白名单的情况下它只是多暴露一个 UDP 1434 的探测面。改完协议和端口记得同步防火墙规则然后重启 Sql Server 服务验证监听是否正常。4.3 登录审计与 SQL Server Audit 落地协议收完最后补审计层。Sql Server 有两个层面的登录审计一个是把登录事件写进 Windows 错误日志的传统机制另一个是基于 SQL Server Audit 的精细化审计。传统机制通过注册表AuditLevel控制0 关闭、1 只记成功、2 只记失败、3 全记。合规基线一般要求至少记失败登录所以我通常设成 2 或 3USE master; GO EXEC xp_instance_regwrite NHKEY_LOCAL_MACHINE, NSOFTWARE\Microsoft\MSSQLServer\MSSQLServer, NAuditLevel, REG_DWORD, 2; GO要求高的场景建议直接用 SQL Server Audit它能把审计写到独立文件和业务日志彻底隔离-- 创建服务端审计文件放独立磁盘 CREATE SERVER AUDIT [LoginAudit] TO FILE ( FILEPATH ND:\SQLAuditLog\, MAXSIZE 512 MB, MAX_FILES 8, RESERVE_DISK_SPACE ON ); ALTER SERVER AUDIT [LoginAudit] WITH (STATE ON); -- 记录成功与失败的登录事件 CREATE SERVER AUDIT SPECIFICATION [ServerLoginSpec] FOR SERVER AUDIT [LoginAudit] ADD (SUCCESSFUL_LOGIN_GROUP), ADD (FAILED_LOGIN_GROUP); ALTER SERVER AUDIT SPECIFICATION [ServerLoginSpec] WITH (STATE ON);注意MAX_SIZE和MAX_FILES必须一起设置否则审计文件无限增长RESERVE_DISK_SPACE防止文件写满把系统盘挤爆。审计文件不要放 C 盘日志查询可以用sys.fn_get_audit_file读回SELECT event_time, server_principal_name, action_id, succeeded, ip_address FROM sys.fn_get_audit_file(ND:\SQLAuditLog\*.sqlaudit, DEFAULT, DEFAULT);5. Sql Server 加固避坑指南照着规范改完连不上先查这五类问题5.1 现象关闭 sa 后应用批量报 18456现象禁用 sa 之后的业务低峰应用连接池开始刷错误日志错误号 18456状态 7。原因历史应用连接串写死sa 明文密码sa 一禁用所有连接直接失败而且连接池会把失败缓存一段时间恢复后可能还要等池刷新。解决先在配置中心全局搜索 sa 关键字把连接串改成专用应用账号窗口期允许新账号接管后再禁用 sa。没条件改代码的至少创建一个和 sa 等权的替代账号并修改连接串。教训禁用 sa 前先查sys.dm_exec_sessions确认没有 sa 活动会话这条不能省。5.2 现象开启强制加密后 JDBC/ODBC 握手失败现象开启 Force Encryption 后Java 应用报「The driver could not establish a secure connection」或 ODBC 报 TLS 握手失败。原因服务器证书是自签的或者证书 CN/SAN 和客户端连接的主机名不匹配驱动默认不信任。解决生产环境向证书体系申请一张包含主机名的证书安装到实例测试环境临时在 JDBC 连接串加trustServerCertificatetrue验证生产禁止这么干。连接串示例jdbc:sqlserver://你的数据库主机:1433;encrypttrue;trustServerCertificatefalse。改强制加密之前先确认所有客户端驱动版本支持 TLS 1.2 以上老驱动在这个阶段翻车最多。5.3 现象权限收紧后存储过程报 EXECUTE 权限被拒绝现象把应用账号移出 db_owner 并授予EXECUTE ON SCHEMA::dbo之后部分存储过程报权限不足。原因存储过程内部跨架构访问了另一个 schema 的对象所有权链断裂调用者权限不够穿透。解决优先把业务对象统一归到同一个架构拆不开的用模块签名见 3.3 节签发证书或对这个过程单独GRANT EXECUTE。注意不要把账号再拉回 db_owner那就回到了起点。5.4 现象禁用 xp_cmdshell 后旧作业集体失败现象加固后 SQL Agent 历史作业成片失败失败步骤全是调用外部 bat 或 exe。原因这些作业当年用xp_cmdshell做文件拷贝、解压、调外部工具禁用后直接断粮。解决把能改成 T-SQL 的步骤用脚本重写确实要调外部程序的改成 SQL Agent 的 CmdExec 作业步骤让权限收敛到 SQL Agent 服务账号这一层而不是开放 xp_cmdshell 给所有能执行 SQL 的人。改之前先列出所有xp_cmdshell调用点评估完再动开关。5.5 现象审计一开磁盘暴涨日志却什么都没留下现象开启登录审计后系统盘空间几天就满了或者审计文件打不开。原因失败登录风暴持续写入MAX_FILES和MAX_SIZE没配置导致文件无限增长审计文件还放在和系统共用的磁盘上。解决审计文件目录独立分区MAX_FILES配合MAX_SIZE做滚动加一个定时任务监控审计目录大小超过阈值告警。查询审计文件打不开时检查运行 SQL 的账号是否有读取审计文件的权限以及文件是否正被占用。6. 把 Sql Server 加固固化成日常基线一份可反复跑的核查脚本6.1 基线核查脚本加固不是一次性项目是持续基线。我习惯把核查脚本存成一个.sql文件放在数据库服务器本地每次变更前后各跑一次输出一份 pass/fail 清单直接入库留档-- Sql Server 加固基线快查只读不修改任何配置 DECLARE Result TABLE (item NVARCHAR(64), pass BIT); -- 1. sa 是否禁用 INSERT INTO Result SELECT sa_disabled, CASE WHEN is_disabled 1 THEN 1 ELSE 0 END FROM sys.server_principals WHERE name sa; -- 2. 是否存在未启用密码策略的 SQL 登录名 INSERT INTO Result SELECT all_sql_login_policy, CASE WHEN COUNT(*) 0 THEN 1 ELSE 0 END FROM sys.sql_logins WHERE is_policy_checked 0; -- 3. sysadmin 成员是否收敛 INSERT INTO Result SELECT sysadmin_leq_3, CASE WHEN COUNT(*) 3 THEN 1 ELSE 0 END FROM sys.server_role_members rm JOIN sys.server_principals m ON rm.member_principal_id m.principal_id WHERE rm.role_principal_id (SELECT principal_id FROM sys.server_principals WHERE name sysadmin); -- 4. xp_cmdshell 是否禁用 INSERT INTO Result SELECT xp_cmdshell_disabled, CASE WHEN value_in_use 0 THEN 1 ELSE 0 END FROM sys.configurations WHERE name xp_cmdshell; -- 5. 登录审计是否开启AuditLevel 2 INSERT INTO Result SELECT login_audit_failure, CASE WHEN value 2 THEN 1 ELSE 0 END FROM sys.configurations WHERE name default trace enabled; -- 占位示例实际审计状态按 4.3 的注册表值核查 SELECT item, pass FROM Result;6.2 变更窗口顺序与验证方法执行顺序比执行本身更重要。我的固定顺序是先跑核查脚本存基线再按身份 → 权限 → 服务面 → 网络 → 审计的顺序逐个改动每改一步观察一段业务日志最后再跑一次脚本对比 pass 数量。验证方法实测三件事用一个错误密码连续登几次到审计日志确认失败记录存在用一个最小权限账号连接实例尝试访问其他库验证被拒重启 Sql Server 服务确认端口固定、协议监听正常。这套习惯救过我很多次——有一次改完审计配置第二天凌晨磁盘告警就是因为忘设MAX_FILES从那以后我的核查脚本里永远多一条「审计目录大小」的检查项。愿这份清单能帮你少踩几个坑希望帮到你。本文还有配套的精品资源点击获取