最小可见与字段级加密:EF Core 拦截器 + Always Encrypted 🔐
📚 目录
- 最小可见与字段级加密:EF Core 拦截器 + Always Encrypted 🔐
0)TL;DR 🧾
-
Always Encrypted (AE) 将加/解密前移到客户端驱动(如
Microsoft.Data.SqlClient),数据库只持久化密文;启用 Secure Enclave 可获得原地(in-place)重加密与更丰富的机密查询能力。 -
用 EF Core
DbCommandInterceptor在发送前:- 统一设置
SqlCommand.ColumnEncryptionSetting(默认Enabled,部分只读场景可降到ResultSetOnly); - 强制参数化(命中敏感列的非参数化文本 SQL 直接阻断);
- 埋点审计(租户、用户、模式、列清单、TraceId)。
- 统一设置
-
密钥治理:CMK/CEK 由 SSMS/PowerShell 管理;CEK 支持在线轮换,Enclave 下可 in-place;同一列仅能绑定一个 CEK(要“按租户密钥”必须分库/分架构/分表或分片)。
1)问题与边界 🧭
-
目标:敏感列加密存储、透明解密、全链路可审计。
-
限制:
- 无 Enclave:确定性加密仅支持等值类操作(可索引);随机化更安全但基本不可查询。
- Enclave:扩展机密查询并可 in-place 重加密,但仍受函数/运算限制与环境前提约束。
-
与 DDM/RLS 的关系(互补):
- AE:存储/传输阶段的机密性;
- DDM(动态数据掩码):结果显示层遮蔽(“看得到但看不全”);
- RLS(行级安全):按主体控制行可见性(“看不见就不返回”)。
2)关键概念速读 💡
- CMK/CEK:列主密钥(CMK)包裹列加密密钥(CEK),CEK 对真实列加密。一个 CEK 可服务多列,但“一列只绑定一个 CEK”(同一时刻)。
- 确定性 vs 随机化:确定性支持等值/索引(存在枚举风险);随机化保密性强但不支持筛选。
- Secure Enclave:受信执行环境(SQL Server 2019+ / Azure SQL),支持 in-place 加/重/解密与更丰富的机密查询。
- 驱动:务必使用
Microsoft.Data.SqlClient(新特性前进方向);System.Data.SqlClient已停止功能性演进,应迁移。
3)目标架构(ABP/EF Core 集成)🏗️
要点:
- 加/解密发生在客户端;数据库只存密文。
- Enclave 可与驱动建立证明会话,启用更多机密查询与 in-place 重加密。
- 列级绑定:同一物理列 只能绑定一把 CEK。
4)环境准备与密钥配置(可复现)🛠️
4.1 SSMS 向导(最快)⚡
用 Always Encrypted 向导:选择列 → 选择/创建 CMK/CEK → 选择加密类型(确定性/随机化)→ 执行(可生成脚本留存)。
4.2 PowerShell(自动化/可回滚)
# 需要:Install-Module SqlServer -Scope CurrentUser
$server="localhost"; $instance="DEFAULT"; $db="ContosoHR"
$path="SQLSERVER:\SQL\$server\$instance\Databases\$db"
# 1) 创建 CMK(示例:Windows 证书存储)
New-SqlColumnMasterKey -Path $path -Name "CMK1" `
-KeyStoreProviderName "MSSQL_CERTIFICATE_STORE" `
-KeyPath "CurrentUser/My/<THUMBPRINT>"
# 2) 生成 CEK(由 CMK 包裹)
New-SqlColumnEncryptionKey -Path $path -Name "CEK1" -ColumnMasterKey "CMK1"
# 3) 加密列(示例:确定性)
$ssn = New-SqlColumnEncryptionSettings -ColumnName "dbo.Employees.SSN" `
-EncryptionType Deterministic -ColumnEncryptionKey "CEK1"
Set-SqlColumnEncryption -Path $path -ColumnEncryptionSettings $ssn
# == 轮换 ==
# 新 CEK:CEK2
New-SqlColumnEncryptionKey -Path $path -Name "CEK2" -ColumnMasterKey "CMK1"
# 重加密(可批量;启用 Enclave 时可 in-place 降低时窗)
$ssn2 = New-SqlColumnEncryptionSettings -ColumnName "dbo.Employees.SSN" `
-EncryptionType Deterministic -ColumnEncryptionKey "CEK2"
Set-SqlColumnEncryption -Path $path -ColumnEncryptionSettings $ssn2
5)EF Core 拦截器:策略化开关 + 强制参数化 + 审计 🧩
🎯 目标:
- 默认
Enabled;纯读且无敏感参数的查询可降级为ResultSetOnly;- 命中敏感列且非参数化的文本 SQL 一律阻断;
- 放行驱动内部的参数加密元数据探测(
sys.sp_describe_parameter_encryption)。- 执行后统一采集列清单(
ReaderExecuted/Async),避免遗漏。
5.1 注册(推荐:由 DI 注入到 DbContextOptions)
// Program.cs / Startup.cs
services.AddSingleton<AeCommandInterceptor>(); // 无状态或仅 Logger/AsyncLocal
services.AddDbContext<MyDbContext>((sp, opts) =>
{
opts.AddInterceptors(sp.GetRequiredService<AeCommandInterceptor>());
// ... 其他 EF 配置
});
5.2 拦截器实现(覆盖 Reader/NonQuery/Scalar 的同步/异步 + Executed 采集列清单)
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
public class AeCommandInterceptor : DbCommandInterceptor
{
private readonly IAePolicy _policy;
private readonly ICommandAuditor _auditor;
public AeCommandInterceptor(IAePolicy policy, ICommandAuditor auditor)
{
_policy = policy; _auditor = auditor; }
// ---- 同步 Executing ----
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command, CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
Apply(command, eventData); return base.ReaderExecuting(command, eventData, result); }
public override InterceptionResult<int
EF Core拦截器与Always Encrypted实战

最低0.47元/天 解锁文章
420

被折叠的 条评论
为什么被折叠?



