SQL Server 管理系列(第二期):安全加固——保护你的数据资产
SQL Server 管理系列(第二期):安全加固——保护你的数据资产
上一期我们完成了 SQL Server 的安装配置。这一期,我们进入安全加固——这是 DBA 最重要的职责之一。你是否遇到过:开发人员直接使用 sa 账户连接应用?离职员工的账号还在数据库里?敏感数据(如身份证、手机号)在查询中明文显示?合规审计要求数据加密却不知从何入手?这一期,我们从最小权限、数据保护、审计追踪三个维度,构建完整的安全体系。
一、安全架构概览
SQL Server 安全采用分层防御模型:
外层:网络防火墙 + Windows 防火墙
↓
第1层:SQL Server 认证(Windows / SQL 登录)
↓
第2层:服务器角色权限(sysadmin, securityadmin, serveradmin...)
↓
第3层:数据库用户映射
↓
第4层:数据库角色权限(db_owner, db_datareader, db_datawriter...)
↓
第5层:对象级权限(表、视图、存储过程、列)
↓
第6层:行级安全 + 数据脱敏
↓
内层:透明数据加密(TDE)+ 备份加密
核心原则:
- 最小权限:只授予完成任务所需的最小权限
- 分层防御:即使一层被突破,其他层仍能保护数据
- 默认拒绝:未明确授权的操作一律禁止
- 审计合规:所有敏感操作必须可追溯
二、认证与授权基础
2.1 认证模式选择
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Windows 认证 | 集成域安全、密码策略、Kerberos | 需要域环境 | 企业内部应用 |
| SQL Server 认证 | 跨域、应用连接字符串简单 | 密码存储在应用配置 | 第三方应用、云环境 |
| 混合模式 | 兼容两者 | 管理两个密码体系 | 大多数生产环境 |
-- 查看当前认证模式
SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS IsWindowsOnly
-- 0 = 混合模式,1 = Windows 认证模式
-- 修改认证模式(需重启服务)
-- 使用 SQL Server 配置管理器 → SQL Server 属性 → 安全性
2.2 登录(Server Level)vs 用户(Database Level)
-- 创建登录(服务器级别)
CREATE LOGIN AppUser WITH
PASSWORD = 'Str0ngP@ssw0rd!',
CHECK_POLICY = ON, -- 强制 Windows 密码策略
CHECK_EXPIRATION = ON, -- 密码过期
DEFAULT_DATABASE = YourDB
-- 创建用户(数据库级别)并映射登录
USE YourDB
CREATE USER AppUser FOR LOGIN AppUser
-- 查看所有登录
SELECT name, type_desc, is_disabled, create_date
FROM sys.sql_logins
WHERE type = 'S' -- SQL 登录
-- 查看所有用户
SELECT name, type_desc, create_date
FROM sys.database_principals
WHERE type IN ('S', 'U') -- SQL 用户、Windows 用户
三、权限管理(最小权限原则)
3.1 服务器角色
| 角色 | 权限 | 建议 |
|---|---|---|
| sysadmin | 完全控制 | 仅限 2-3 个 DBA |
| securityadmin | 管理登录/权限 | 限制使用 |
| serveradmin | 服务器级配置 | 限制使用 |
| setupadmin | 管理链接服务器 | 限制使用 |
| processadmin | 终止进程 | 按需授予 |
| diskadmin | 管理磁盘文件 | 按需授予 |
| dbcreator | 创建/修改数据库 | 开发环境可用 |
| bulkadmin | 执行 BULK INSERT | 按需授予 |
-- 查看服务器角色成员
EXEC sp_helpsrvrolemember 'sysadmin'
-- 添加/删除成员
EXEC sp_addsrvrolemember 'DOMAIN\DBA_User', 'sysadmin'
EXEC sp_dropsrvrolemember 'DOMAIN\OldDBA', 'sysadmin'
-- 创建自定义服务器角色(SQL Server 2012+)
CREATE SERVER ROLE DBA_ReadOnly
GRANT VIEW SERVER STATE TO DBA_ReadOnly
GRANT VIEW ANY DEFINITION TO DBA_ReadOnly
3.2 数据库角色
| 角色 | 权限 | 建议 |
|---|---|---|
| db_owner | 完全控制数据库 | 仅限 DBA |
| db_securityadmin | 管理数据库权限 | 限制使用 |
| db_accessadmin | 管理数据库用户 | 限制使用 |
| db_backupoperator | 备份数据库 | 备份账户 |
| db_ddladmin | 执行 DDL | 开发/迁移账户 |
| db_datawriter | INSERT/UPDATE/DELETE | 应用账户 |
| db_datareader | SELECT | 报表账户 |
| db_denydatawriter | 禁止写入 | 特殊限制 |
| db_denydatareader | 禁止读取 | 特殊限制 |
-- 查看数据库角色成员
EXEC sp_helprolemember 'db_datareader'
-- 添加用户到角色
EXEC sp_addrolemember 'db_datareader', 'AppUser'
EXEC sp_addrolemember 'db_datawriter', 'AppUser'
-- 创建自定义数据库角色
CREATE ROLE ReportReader
GRANT SELECT ON Orders TO ReportReader
GRANT SELECT ON Customers TO ReportReader
EXEC sp_addrolemember 'ReportReader', 'ReportingUser'
3.3 对象级权限(细粒度控制)
-- 表级别
GRANT SELECT ON Orders TO SalesReader
GRANT INSERT, UPDATE ON Orders TO SalesWriter
DENY DELETE ON Orders TO SalesWriter -- 禁止删除
-- 列级别(敏感数据)
GRANT SELECT ON Customers(FirstName, LastName, Email) TO SupportTeam
DENY SELECT ON Customers(SSN, CreditCard) TO SupportTeam
-- 存储过程级别(推荐:通过存储过程封装所有数据访问)
GRANT EXECUTE ON usp_GetCustomerData TO AppUser
-- 不授予表直接访问权限
DENY SELECT ON Customers TO AppUser
-- 架构级别
GRANT SELECT ON SCHEMA::Sales TO SalesReader
GRANT EXECUTE ON SCHEMA::dbo TO AppUser
-- 查看权限
SELECT
pr.name AS PrincipalName,
pr.type_desc,
pe.permission_name,
pe.state_desc,
OBJECT_NAME(pe.major_id) AS ObjectName
FROM sys.database_permissions pe
JOIN sys.database_principals pr ON pe.grantee_principal_id = pr.principal_id
WHERE pe.class = 1 -- 对象级权限
ORDER BY pr.name, pe.permission_name
四、高级安全特性
4.1 行级安全(Row-Level Security, RLS)
场景:销售人员只能看自己的客户数据
-- 创建过滤谓词函数
CREATE FUNCTION dbo.fn_SalespersonFilter(@SalespersonID INT)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT 1 AS IsAccessible
WHERE @SalespersonID = CAST(SESSION_CONTEXT(N'SalespersonID') AS INT)
OR IS_ROLEMEMBER('db_owner') = 1
)
-- 创建安全策略
CREATE SECURITY POLICY SalespersonPolicy
ADD FILTER PREDICATE dbo.fn_SalespersonFilter(SalespersonID) ON dbo.Customers
WITH (STATE = ON)
-- 应用设置会话上下文
EXEC sp_set_session_context N'SalespersonID', 123
SELECT * FROM Customers -- 只看到 SalespersonID=123 的客户
-- 查看策略
SELECT * FROM sys.security_policies
SELECT * FROM sys.security_predicates
4.2 动态数据脱敏(Dynamic Data Masking)
场景:客服人员查询时,敏感字段自动脱敏
-- 创建表时定义脱敏
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY,
Name NVARCHAR(100) MASKED WITH (FUNCTION = 'partial(2, "***", 2)'),
Email NVARCHAR(100) MASKED WITH (FUNCTION = 'email()'),
Phone VARCHAR(20) MASKED WITH (FUNCTION = 'default()'),
CreditCard CHAR(16) MASKED WITH (FUNCTION = 'partial(4, "****-****-****-", 4)')
)
-- 事后添加脱敏
ALTER TABLE Customers ALTER COLUMN Phone
ADD MASKED WITH (FUNCTION = 'partial(0, "*******", 4)')
-- 测试脱敏效果
-- 普通用户查询
SELECT * FROM Customers
-- 结果:王**,a***@***.com,*******,****-****-****-1234
-- 授权查看真实数据
GRANT UNMASK TO DataAnalyst
REVOKE UNMASK FROM SupportTeam
-- 查看脱敏配置
SELECT
SCHEMA_NAME(t.schema_id) AS SchemaName,
t.name AS TableName,
c.name AS ColumnName,
c.is_masked,
c.masking_function
FROM sys.masked_columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.is_masked = 1
4.3 透明数据加密(Transparent Data Encryption, TDE)
场景:防止磁盘丢失导致数据泄露
-- 1. 创建数据库主密钥
USE master
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'Str0ngM@sterKey!'
-- 2. 创建证书
CREATE CERTIFICATE TDECert WITH SUBJECT = 'TDE Certificate for YourDB'
-- 3. 备份证书(重要!恢复数据库时需要)
BACKUP CERTIFICATE TDECert TO FILE = 'G:\Backup\TDECert.cer'
WITH PRIVATE KEY (
FILE = 'G:\Backup\TDECert_PrivateKey.pvk',
ENCRYPTION BY PASSWORD = 'CertPrivateKey!@#',
DECRYPTION BY PASSWORD = 'CertPrivateKey!@#'
)
-- 4. 启用数据库加密
USE YourDB
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER CERTIFICATE TDECert
ALTER DATABASE YourDB SET ENCRYPTION ON
-- 5. 查看加密进度
SELECT
db.name,
dek.encryption_state,
CASE dek.encryption_state
WHEN 0 THEN 'No encryption'
WHEN 1 THEN 'Encryption in progress'
WHEN 2 THEN 'Encrypted'
WHEN 3 THEN 'Decryption in progress'
END AS EncryptionState
FROM sys.dm_database_encryption_keys dek
JOIN sys.databases db ON dek.database_id = db.database_id
WHERE db.name = 'YourDB'
4.4 备份加密
-- 使用证书加密备份
BACKUP DATABASE YourDB
TO DISK = 'G:\Backup\YourDB_Encrypted.bak'
WITH
COMPRESSION,
ENCRYPTION (
ALGORITHM = AES_256,
SERVER CERTIFICATE = TDECert
)
-- 使用密码加密备份(简单但安全性较低)
BACKUP DATABASE YourDB
TO DISK = 'G:\Backup\YourDB_Password.bak'
WITH PASSWORD = 'BackupP@ssw0rd!'
-- 恢复加密备份需要证书
CREATE CERTIFICATE TDECert FROM FILE = 'G:\Backup\TDECert.cer'
WITH PRIVATE KEY (
FILE = 'G:\Backup\TDECert_PrivateKey.pvk',
DECRYPTION BY PASSWORD = 'CertPrivateKey!@#'
)
五、审计与合规
5.1 SQL Server Audit(企业版)
-- 创建审计对象
USE master
CREATE SERVER AUDIT ServerAudit
TO FILE (FILEPATH = 'G:\Audit\')
WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE)
-- 启用审计
ALTER SERVER AUDIT ServerAudit WITH (STATE = ON)
-- 创建服务器级审计规范(监控登录失败)
CREATE SERVER AUDIT SPECIFICATION LoginAudit
FOR SERVER AUDIT ServerAudit
ADD (FAILED_LOGIN_GROUP)
WITH (STATE = ON)
-- 创建数据库级审计规范(监控 DDL 操作)
USE YourDB
CREATE DATABASE AUDIT SPECIFICATION DDLAudit
FOR SERVER AUDIT ServerAudit
ADD (SCHEMA_OBJECT_CHANGE_GROUP),
ADD (DATABASE_OBJECT_CHANGE_GROUP)
WITH (STATE = ON)
-- 查看审计日志
SELECT
event_time,
action_id,
session_server_principal_name,
object_name,
statement
FROM sys.fn_get_audit_file('G:\Audit\*.sqlaudit', DEFAULT, DEFAULT)
ORDER BY event_time DESC
5.2 通用审计脚本(非企业版)
-- 创建审计表
CREATE TABLE DBA_AuditLog (
AuditID BIGINT IDENTITY PRIMARY KEY,
EventTime DATETIME2 DEFAULT GETDATE(),
LoginName NVARCHAR(128),
HostName NVARCHAR(128),
AppName NVARCHAR(128),
EventType NVARCHAR(50),
ObjectName NVARCHAR(255),
SQLText NVARCHAR(MAX)
)
-- 创建触发器记录 DDL 操作
CREATE TRIGGER trg_AuditDDL
ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS
AS
BEGIN
DECLARE @data XML = EVENTDATA()
INSERT INTO DBA_AuditLog (
LoginName,
HostName,
AppName,
EventType,
ObjectName,
SQLText
)
VALUES (
@data.value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(128)'),
@data.value('(/EVENT_INSTANCE/HostName)[1]', 'NVARCHAR(128)'),
@data.value('(/EVENT_INSTANCE/ApplicationName)[1]', 'NVARCHAR(128)'),
@data.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(50)'),
@data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'),
@data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)')
)
END
六、SQL 注入防护(数据库层面)
-- 1. 使用参数化查询(存储过程强制参数化)
CREATE PROC usp_GetUser @UserID INT
AS
BEGIN
SELECT * FROM Users WHERE UserID = @UserID
END
-- 2. 强制参数化(防止字符串拼接)
ALTER DATABASE YourDB SET PARAMETERIZATION FORCED
-- 3. 使用 QUOTENAME 处理动态表名/列名
DECLARE @TableName NVARCHAR(128) = QUOTENAME(@UserInput)
SET @sql = 'SELECT * FROM ' + @TableName
-- 4. 最小权限:应用账户只有 EXECUTE 权限,没有直接表 SELECT
DENY SELECT ON SCHEMA::dbo TO AppUser
GRANT EXECUTE ON SCHEMA::dbo TO AppUser
-- 5. 启用 CLR 安全(如不使用)
EXEC sp_configure 'clr enabled', 0
RECONFIGURE
-- 6. 禁用 OLE Automation(如不使用)
EXEC sp_configure 'Ole Automation Procedures', 0
RECONFIGURE
-- 7. 禁用 xp_cmdshell(强烈建议)
EXEC sp_configure 'xp_cmdshell', 0
RECONFIGURE
七、安全配置检查清单
账户管理
权限管理
数据保护
审计追踪
攻击面最小化
八、核心总结
| 安全层 | 核心技术 | 关键配置 |
|---|---|---|
| 认证 | Windows + SQL 混合 | 禁用 sa,使用域账户 |
| 授权 | 最小权限原则 | 应用用 db_datareader + db_datawriter |
| 行级 | RLS | 安全策略 + 谓词函数 |
| 列级 | 动态脱敏 | MASKED 列 + UNMASK 权限 |
| 加密 | TDE | 数据库加密 + 证书备份 |
| 审计 | SQL Audit / 触发器 | 记录登录、DDL、权限变更 |
| 防注入 | 参数化 | 存储过程 + 最小权限 |
一句话记住本期内容:
SQL Server 安全是分层防御——禁用 sa 和 xp_cmdshell,应用账户最小权限,敏感数据脱敏加密,所有操作有审计。
动手练习
-- 场景:电商系统安全配置
-- 1. 创建应用账户,只能通过存储过程访问 Orders 表(不能直接 SELECT/UPDATE)
-- 2. 对 Customers 表的 Phone、Email 列添加动态脱敏
-- 3. 创建 RLS 策略,让销售人员只能看到自己的客户
-- 4. 配置审计,记录谁在周末删除了数据
点击查看参考答案
1. 应用账户配置:
CREATE LOGIN AppUser WITH PASSWORD = 'AppP@ssw0rd'
USE YourDB
CREATE USER AppUser FOR LOGIN AppUser
DENY SELECT, INSERT, UPDATE, DELETE ON Orders TO AppUser
GRANT EXECUTE ON usp_GetOrders TO AppUser
GRANT EXECUTE ON usp_UpdateOrder TO AppUser
2. 动态脱敏:
ALTER TABLE Customers ALTER COLUMN Phone
ADD MASKED WITH (FUNCTION = 'partial(0, "*******", 4)')
ALTER TABLE Customers ALTER COLUMN Email
ADD MASKED WITH (FUNCTION = 'email()')
3. RLS:参考 4.1 节代码
4. 审计:
CREATE TABLE WeekendDeleteAudit (...)
CREATE TRIGGER trg_WeekendDelete ON Orders AFTER DELETE
AS
IF DATEPART(WEEKDAY, GETDATE()) IN (1,7)
INSERT INTO WeekendDeleteAudit SELECT *, GETDATE() FROM deleted
下一期预告
备份与恢复——数据安全的最后防线
- 完整备份、差异备份、日志备份的深度解析
- 备份策略设计:RPO/RTO 权衡
- 恢复演练:从备份还原数据库
- 备份加密与压缩
- 灾难恢复:异地备份、日志传送、Always On
📌 本文配置适用于 SQL Server 2016-2022。生产环境安全策略需结合企业合规要求(GDPR、等保等)调整。
管理系列持续更新中,点击关注不错过第三期。
浙公网安备 33010602011771号