你的数据库密码还在用明文?SQL Server 凭据管理的 3 种现代方案

你的数据库密码还在用明文?SQL Server 凭据管理的 3 种现代方案


一、前言:明文密码是生产环境最大的"定时炸弹"

先讲一个真实场景------

凌晨 2 点,运维同学在排查问题,把连接字符串截图发到了技术群里。

截图里 Password=Prod@2023! 清晰可见。

群里有人顺手保存了图片。

三个月后,那个人离职了。

六个月后,数据库被拖库。

这不是电影情节,这是过去五年里 DBA 圈子里反复上演的剧本。

明文密码的危害远不止"被看见"这么简单:

风险场景 后果
代码仓库泄露(GitHub 误推送) 攻击者直接拿到数据库访问权
配置文件被运维/外包人员看到 内部威胁,无法追溯
连接字符串截图/日志外泄 社工攻击入口
密码硬编码在应用里 改密码 = 重新编译部署
合规审计(等保/SOX/GDPR) 直接不通过

好消息是:SQL Server 生态在凭据管理上已经非常成熟,你只是可能还没用上。

本文介绍 3 种现代方案,从"够用"到"企业级",总有一款适合你。


二、先搞清楚:你在"哪一层"管理凭据?

不同架构下,凭据的存储位置完全不同:

arduino 复制代码
┌─────────────────────────────────────────────────────────┐
│  应用层(代码/配置文件)  ← 大多数人停在这一层,用明文   │
├─────────────────────────────────────────────────────────┤
│  平台层(IIS/服务账号/容器)                             │
├─────────────────────────────────────────────────────────┤
│  凭据管理层(Key Vault / Credential Store)  ← 本文重点 │
├─────────────────────────────────────────────────────────┤
│  数据库层(SQL Server 自身认证)                         │
└─────────────────────────────────────────────────────────┘

核心原则 :凭据永远不应该出现在应用代码或配置文件中。它应该由专门的凭据管理层注入到运行时。


三、方案一:Azure Key Vault(云原生首选 ⭐⭐⭐⭐⭐)

3.1 它解决什么问题?

传统方式 Key Vault 方式
密码写在 web.config / appsettings.json 配置文件里只有 Key Vault 的地址
改密码要重新部署应用 在 Vault 里改,应用自动获取新密码
谁有服务器权限谁就能看到密码 密码永远不会离开 Vault
无法审计"谁在什么时候访问了密码" 每次访问都有完整日志

3.2 架构示意

arduino 复制代码
┌──────────┐      ① 请求凭据        ┌──────────────────┐
│          │  ──────────────────→   │                  │
│   应用   │                        │  Azure Key Vault │
│          │  ←──────────────────   │                  │
└──────────┘      ② 返回密码        └──────────────────┘
      │                                        │
      │ ③ 用凭据连接                            │ 审计日志
      ↓                                        ↓
┌──────────┐                          ┌──────────────┐
│ SQL Server│                          │  Azure Monitor│
└──────────┘                          └──────────────┘

3.3 快速上手

Step 1:在 Azure 中创建 Key Vault 并存储凭据

perl 复制代码
# 创建 Key Vault
az keyvault create --name my-sql-kv --resource-group my-rg --location eastus

# 存储 SQL 密码
az keyvault secret set --vault-name my-sql-kv --name "SqlPassword" --value "SuperSecret123!"

Step 2:给应用分配访问权限(Managed Identity,推荐)

csharp 复制代码
# 为应用服务启用系统分配托管标识
az webapp identity assign --name my-webapp --resource-group my-rg

# 授予 Key Vault 读取权限
az keyvault set-policy --name my-sql-kv \
  --object-id <managed-identity-object-id> \
  --secret-permissions get list

🔑 关键点 :应用不需要任何密码来访问 Key Vault------它用 Managed Identity(Azure AD 自动管理的身份),彻底消灭了"凭据的凭据"问题。

Step 3:代码中从 Key Vault 读取密码

csharp 复制代码
// .NET 6+ 方式(最简洁)
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

var client = new SecretClient(
    new Uri("https://my-sql-kv.vault.azure.net/"),
    new DefaultAzureCredential()  // 自动使用 Managed Identity
);

KeyVaultSecret secret = await client.GetSecretAsync("SqlPassword");
string connectionString = $"Server=mysqlserver.database.windows.net;Database=MyDB;User ID=myuser;Password={secret.Value};";

// 或者更优雅:直接用 Azure App Configuration 绑定

Step 4:本地开发怎么办?

bash 复制代码
# 本地开发用 Azure CLI 登录即可,DefaultAzureCredential 会自动识别
az login

本地开发者的 Azure AD 账号只需被加到 Key Vault 的访问策略中,就能读取同一个 Secret------不需要把密码告诉开发者

3.4 自动轮换(Rotation)

这是 Key Vault 最强大的功能之一:

python 复制代码
# 创建轮换策略(每 90 天自动轮换 SQL 密码)
az keyvault secret set-attributes \
  --vault-name my-sql-kv \
  --name SqlPassword \
  --content-type "application/json"

# 通过 Azure Automation / Logic App 实现完整轮换流程:
# 1. 在 SQL Server 中 ALTER LOGIN 改密码
# 2. 更新 Key Vault 中的 Secret
# 3. 应用通过重试逻辑自动获取新密码重连

3.5 优缺点总结

✅ 优点 ❌ 缺点
密码永不出现在代码/配置中 需要 Azure 环境(云或 Hybrid)
自动轮换,减少人为操作 增加架构复杂度(多一个依赖)
完整审计日志 需要学习 Azure IAM 权限模型
Managed Identity 免密访问 有少量费用($0.03/万次操作)
合规友好(等保/SOX/GDPR) 网络不通时应用无法启动

四、方案二:Windows Credential Manager + DPAPI(本地部署首选 ⭐⭐⭐⭐)

适用场景:SQL Server 部署在本地机房 / 私有云,没有 Azure Key Vault 可用,但想彻底摆脱明文密码。

4.1 核心思路

markdown 复制代码
明文密码  →  DPAPI 加密(绑定到 Windows 用户/机器)  →  存储在 Credential Manager
                                                              ↓
                                                    应用运行时解密读取

DPAPI(Data Protection API) ​ 是 Windows 内置的加密机制:

  • 加密密钥由 Windows 自动管理(基于用户登录凭据或机器密钥)
  • 不需要你管理任何密钥------这是它最大的优势
  • 加密数据只能在同一台机器 + 同一个用户下解密

4.2 存储凭据到 Windows Credential Manager

方式一:PowerShell(推荐用于部署脚本)

bash 复制代码
# 将 SQL 密码存入 Windows Credential Manager
$password = Read-Host "Enter SQL Password" -AsSecureString
$credential = New-Object System.Management.Automation.PSCredential("SQL_PROD_USER", $password)

# 使用 CredentialManager 模块
Install-Module -Name CredentialManager -Force
Set-StoredCredential -Target "SQL_Prod_Connection" -Credential $credential

方式二:.NET 代码直接调用 DPAPI

csharp 复制代码
using System.Security.Cryptography;
using System.Security;

// 加密密码
public static string EncryptPassword(string plainPassword)
{
    byte[] data = System.Text.Encoding.UTF8.GetBytes(plainPassword);
    byte[] encrypted = ProtectedData.Protect(
        data,
        null,  // 可选熵值(额外随机种子)
        DataProtectionScope.LocalMachine  // 或 CurrentUser
    );
    return Convert.ToBase64String(encrypted);
}

// 解密密码
public static string DecryptPassword(string encryptedBase64)
{
    byte[] data = Convert.FromBase64String(encryptedBase64);
    byte[] decrypted = ProtectedData.Unprotect(
        data,
        null,
        DataProtectionScope.LocalMachine
    );
    return System.Text.Encoding.UTF8.GetString(decrypted);
}

方式三:存储到配置文件(加密后的)

json 复制代码
{
  "ConnectionStrings": {
    "SqlEncrypted": "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA...",
    "SqlUser": "myapp_user"
  }
}

应用启动时解密:

python 复制代码
string encryptedPwd = config["ConnectionStrings:SqlEncrypted"];
string password = DecryptPassword(encryptedPwd);
string connStr = $"Server=sqlprod;Database=MyDB;User ID={config["ConnectionStrings:SqlUser"]};Password={password};";

4.3 更优雅的方案:Windows 服务账号 + SSPI(零密码)

如果你的 SQL Server 和应用在 同一个 AD 域​ 中,其实连密码都不需要:

json 复制代码
{
  "ConnectionStrings": {
    "Sql": "Server=sqlprod;Database=MyDB;Integrated Security=true;"
  }
}

原理 :应用以 Windows 服务账号运行 → SQL Server 用 Windows 身份验证 → 完全不需要用户名密码

方案 凭据存储位置 安全性
明文密码 配置文件 ❌ 极低
DPAPI 加密 配置文件(密文) ✅ 高(绑定机器)
Windows 集成认证 无(Kerberos 票据) ✅✅ 最高

🏆 如果条件允许,Windows 集成认证(Integrated Security)永远是最佳方案------因为根本不存在"密码"这个东西。

4.4 优缺点总结

✅ 优点 ❌ 缺点
无需引入第三方服务 仅限 Windows 环境
DPAPI 密钥由 OS 管理,不用自己管 加密数据绑定机器,迁移需重新加密
实现简单,.NET 原生支持 没有集中式审计日志
可与 AD 集成实现零密码 无法跨平台(Linux 不行)
无额外费用 密码轮换仍需手动操作

五、方案三:SQL Server 自带凭据机制(DBA 视角 ⭐⭐⭐⭐)

适用场景 :你需要在 SQL Server 内部管理外部资源的凭据(备份到 Azure、链接服务器、Agent Job 代理等),而不是应用连接数据库的密码。

这是很多 DBA 容易忽略的一层------SQL Server 自己也需要"密码"来访问外部资源

5.1 Credential(凭据对象)

SQL Server 的 Credential 是一个数据库引擎级别的凭据存储,用来保存访问外部资源的账号密码。

ini 复制代码
-- 创建凭据(密码在内存中加密存储,不会以明文出现在系统表中)
CREATE CREDENTIAL AzureBackupCred
WITH IDENTITY = 'mystorageaccount',
     SECRET = 'N4m8K7pQ2xR9vW3z...';  -- Azure Storage Key
GO

-- 将凭据绑定到 SQL Server Agent Proxy
EXEC msdb.dbo.sp_add_proxy
    @proxy_name = 'AzureBackupProxy',
    @credential_name = 'AzureBackupCred',
    @enabled = 1;
GO

-- 授权 Proxy 执行特定步骤
EXEC msdb.dbo.sp_grant_proxy_to_subsystem
    @proxy_name = 'AzureBackupProxy',
    @subsystem_name = 'CmdExec';
GO

5.2 使用凭据进行 Azure 备份(TDE + 备份加密)

ini 复制代码
-- 创建数据库主密钥(如果还没有)
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'StrongMasterKeyPassword!';
GO

-- 创建备份加密凭据
CREATE CREDENTIAL BackupCertCred
WITH IDENTITY = 'BackupEncryption',
     SECRET = 'BackupCertPassword123!';
GO

-- 创建证书用于备份加密
CREATE CERTIFICATE BackupCert
WITH SUBJECT = 'Database Backup Encryption Certificate';
GO

-- 备份数据库(加密)
BACKUP DATABASE MyDB
TO DISK = 'D:\Backups\MyDB.bak'
WITH COMPRESSION,
     ENCRYPTION (
         ALGORITHM = AES_256,
         SERVER CERTIFICATE = BackupCert
     );
GO

5.3 链接服务器 + 凭据

ini 复制代码
-- 创建访问远程 Oracle 的凭据
CREATE CREDENTIAL OracleRemoteCred
WITH IDENTITY = 'oracle_user',
     SECRET = 'oracle_password';
GO

-- 创建链接服务器
EXEC sp_addlinkedserver
    @server = 'ORACLE_REMOTE',
    @srvproduct = 'Oracle',
    @provider = 'OraOLEDB.Oracle',
    @datasrc = 'OracleServer';
GO

-- 将凭据映射到登录
EXEC sp_addlinkedsrvlogin
    @rmtsrvname = 'ORACLE_REMOTE',
    @useself = 'FALSE',
    @locallogin = 'sql_login',
    @rmtuser = 'oracle_user',
    @rmtpassword = 'oracle_password';
GO

5.4 查看凭据(不显示密码)

vbnet 复制代码
-- 列出所有凭据(SECRET 列不会显示实际密码)
SELECT name, credential_identity, create_date, modify_date
FROM sys.credentials;
GO

-- 检查凭据是否被使用
SELECT
    c.name AS credential_name,
    c.credential_identity,
    p.name AS proxy_name,
    p.enabled
FROM sys.credentials c
LEFT JOIN msdb.dbo.sysproxies p ON c.credential_id = p.credential_id
ORDER BY c.name;

5.5 SQL Server 2019+ 的 Secure Enclaves(进阶)

如果你用的是 SQL Server 2019+ 且启用了 Always Encrypted with Secure Enclaves,还可以将列主密钥(CMK)存储在 Azure Key Vault 或 HSM 中,实现数据库管理员也看不到明文数据的终极安全模型。

ini 复制代码
-- 创建列主密钥,引用 Azure Key Vault
CREATE COLUMN MASTER KEY MyCMK
WITH (
    KEY_STORE_PROVIDER_NAME = 'AZURE_KEY_VAULT',
    KEY_PATH = 'https://my-kv.vault.azure.net/keys/MyCMK'
);
GO

-- 创建列加密密钥
CREATE COLUMN ENCRYPTION KEY MyCEK
WITH VALUES (
    COLUMN_MASTER_KEY = MyCMK,
    ALGORITHM = 'RSA_OAEP',
    ENCRYPTED_VALUE = 0x01A3...
);
GO

5.6 优缺点总结

✅ 优点 ❌ 缺点
原生支持,无需额外组件 仅限 SQL Server 内部使用
密码在系统表中加密存储 不能用于应用层连接字符串
支持 Agent Proxy、备份加密、链接服务器 密码轮换仍需手动 ALTER
可与 Azure Key Vault 集成(2019+) 查看/管理需要高权限
审计友好(DDL 触发器可追踪变更) 不适用于跨平台场景

六、三种方案对比与选型指南

维度 Azure Key Vault DPAPI + Credential Manager SQL Server Credential
适用场景 云原生 / 混合云 本地部署 Windows 环境 SQL Server 内部管理
应用层密码管理 ✅ 最佳 ✅ 可用 ❌ 不适用
零密码(无凭据) ✅ Managed Identity ✅ Windows 集成认证
自动轮换 ✅ 原生支持 ❌ 需自行实现 ❌ 需自行实现
审计日志 ✅ 完整 ⚠️ 有限 ⚠️ DDL 日志
跨机器/跨平台 ✅ 任何平台 ❌ 仅 Windows ⚠️ 仅 SQL Server
学习成本 中高
额外费用 少量
等保/合规 ✅ 最优 ✅ 可用 ✅ 可用

选型决策树

sql 复制代码
你的 SQL Server 在云上(Azure)?
  ├── 是 → Azure Key Vault + Managed Identity ✅
  └── 否 → 应用在 AD 域中?
              ├── 是 → Windows 集成认证(Integrated Security)✅✅
              └── 否 → Windows 服务器?
                          ├── 是 → DPAPI 加密 + Credential Manager ✅
                          └── 否(Linux)→ 环境变量 + 文件权限 或 HashiCorp Vault

七、额外加分项:那些容易被忽略的最佳实践

7.1 连接字符串中不要出现密码(即使加密了)

xml 复制代码
<!-- ❌ 不推荐 -->
<connectionStrings>
  <add name="Sql" connectionString="Server=sql;Database=db;User=sa;Password=xxx" />
</connectionStrings>

<!-- ✅ 推荐:用占位符,运行时注入 -->
<connectionStrings>
  <add name="Sql" connectionString="{SQL_CONNECTION_STRING}" />
</connectionStrings>

7.2 使用 SqlConnectionStringBuilder 防止注入

ini 复制代码
// ❌ 字符串拼接有注入风险
var connStr = $"Server={server};User ID={user};Password={password}";

// ✅ 使用 Builder
var builder = new SqlConnectionStringBuilder
{
    DataSource = server,
    InitialCatalog = "MyDB",
    UserID = user,
    Password = password,
    Encrypt = true,           // 强制加密连接
    TrustServerCertificate = false  // 生产环境不要跳过证书验证
};

7.3 强制加密连接(防中间人)

ini 复制代码
-- 在 SQL Server 端强制加密
-- SQL Server Configuration Manager → 网络配置 → 协议 → 属性 → Force Encryption = Yes

-- 或使用 T-SQL 检查
SELECT session_id, encrypt_option
FROM sys.dm_exec_connections
WHERE session_id = @@SPID;
-- encrypt_option = TRUE 表示连接已加密

7.4 定期轮换 SQL 登录密码

less 复制代码
-- 创建密码轮换存储过程
CREATE PROCEDURE dbo.RotateSqlLoginPassword
    @LoginName SYSNAME,
    @NewPassword NVARCHAR(128)
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    SET @sql = 'ALTER LOGIN ' + QUOTENAME(@LoginName) + ' WITH PASSWORD = ' + QUOTENAME(@NewPassword, '''');
    EXEC sp_executesql @sql;

    -- 记录轮换事件
    INSERT INTO DBA_Monitor.dbo.PasswordRotationLog (login_name, rotated_at, rotated_by)
    VALUES (@LoginName, GETDATE(), SYSTEM_USER);
END;
GO

7.5 最小权限原则:应用账号只给需要的权限

sql 复制代码
-- ❌ 不要用 sa 或 db_owner
-- ✅ 创建专用应用账号,只给必要的权限
CREATE USER [AppUser] FOR LOGIN [AppLogin];
ALTER ROLE [db_datareader] ADD MEMBER [AppUser];
ALTER ROLE [db_datawriter] ADD MEMBER [AppUser];
-- 只授予需要的存储过程执行权限
GRANT EXECUTE ON dbo.usp_GetOrders TO [AppUser];
-- 拒绝直接表访问
DENY SELECT ON dbo.SensitiveTable TO [AppUser];

八、迁移路线图:从明文到现代凭据管理

别试图一步到位------按这个节奏来:

vbnet 复制代码
Phase 1(第 1 周):消灭代码中的硬编码密码
  → 移到配置文件(加密),或移到环境变量
  → 确保代码仓库中没有明文密码

Phase 2(第 2-3 周):引入平台级凭据管理
  → 云环境:接入 Azure Key Vault
  → 本地环境:DPAPI 加密 + Windows 集成认证

Phase 3(第 4-6 周):实现自动轮换
  → Key Vault 自动轮换 SQL 密码
  → 应用增加重试 + 重连逻辑

Phase 4(持续):审计与合规
  → 定期审查谁有权访问凭据
  → 监控凭据访问日志
  → 密码轮换纳入 SOP

九、总结

你现在的做法 风险等级 建议行动
密码硬编码在代码里 🔴 极高 立即重构,移到配置/Key Vault
密码写在 appsettings.json 🔴 高 至少用 DPAPI 加密,或移到 Key Vault
密码在环境变量中 🟡 中 可接受过渡方案,尽快升级到 Key Vault
用 Key Vault + Managed Identity 🟢 低 保持,完善审计和轮换
Windows 集成认证 🟢 最低 这是终极方案,能用的地方尽量用

一句话总结 :明文密码不是"技术债",是安全漏洞。Azure Key Vault 是云上最优解,Windows 集成认证是本地最优解,SQL Server 自带凭据机制是 DBA 的内部防线------三者各司其职,组合使用才能构建完整的凭据安全体系。

相关推荐
凤山老林1 小时前
Spring Boot 集成 ShardingSphere-Encrypt 实现字段级实时脱敏
java·spring boot·后端·数据脱敏
vipxieliang1 小时前
ValidX v1.2.0 更新日志
java·后端
Zane19941 小时前
只改一个方向的引用,循环引用就能立刻被回收?一文讲透 weakref 弱引用
后端·python
长大19881 小时前
窗口函数用不好反而更慢?SQL Server中OVER子句的4个性能陷阱
后端
大黄评测1 小时前
MERGE语句有Bug?SQL Server官方不推荐的背后真相与替代方案
后端
长大19881 小时前
TempDB 爆满导致系统卡死?SQL Server TempDB 瓶颈诊断与根治方案
后端
lichenyang4531 小时前
从「房间」到「实时通知」:用 NestJS + Socket.IO 实现团队邀请的完整工程实践
前端·后端
大黄评测1 小时前
为什么你的SQL查询慢?这7个执行计划陷阱90%的人都踩过
后端
沙盘客1 小时前
AFSIM 示例解读(09)· 传感器全家桶 sensor_demos(下):ESM / SAR / 被动测向
c++·经验分享·后端