对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

配置 Queryable Encryption

在本指南中,您可以了解如何使用 EF Core 提供商通过 Queryable Encryption (QE) 加密特定文档字段。

Queryable Encryption 在将数据写入 MongoDB 之前,在应用程序层对敏感文档字段进行加密,同时仍允许应用程序查询这些字段。只有有权访问加密密钥的应用程序才能读取明文数据。如果攻击者获取对数据库的访问权,他们只能看到密文,因为他们无法访问加密密钥。

例如,您可以使用 Queryable Encryption 加密包含以下内容的字段:

  • 社会安全号

  • 信用卡号码

  • 健康或医疗信息

  • 财务信息

  • 任何其他敏感信息或个人可识别信息

EF Core 提供商通过流畅模型 API 支持 Queryable Encryption。您可以使用 OnModelCreating() 方法配置要加密的实体属性,提供商在读取和写入数据时会自动处理加密。

在使用 EF Core 提供商配置 Queryable Encryption 之前,请确保具备以下条件:

  • 运行 MongoDB 7.0 或更高版本的 MongoDB Enterprise 或 MongoDB Atlas 集群。

  • 访问 Key Management Service (KMS)。支持的 KMS 提供商包括 Amazon Web Services、Azure、Google Cloud Platform、密钥管理互操作协议 (KMIP)和本地密钥提供商。

  • 环境中安装的自动加密共享库 (CRYPT_SHARED) 或 mongocryptd。要了解如何安装共享库,请参阅 MongoDB Server 文档中的 安装查询分析组件

以下部分介绍如何在上下文中配置加密选项,以及如何在模型中标记实体属性以进行加密:

在配置使用 Queryable Encryption 的任何上下文之前,必须通过运行以下代码为应用程序注册一次自动加密提供商:

MongoClientSettings.Extensions.AddAutoEncryption();

然后,创建 MongoOptionsExtension 实例,并在将实例传递给 UseMongoDB() 方法之前链接以下方法:

  • WithKmsProviders() — 指定您的 KMS 提供商和凭证。

  • WithKeyVaultNamespace() — 指定用于存储数据加密密钥的集合。

  • WithCryptProvider() — 指定要使用的加密库及其路径。

以下示例配置本地 KMS 提供商以供开发使用:

var kmsProviders = new Dictionary<
string, IReadOnlyDictionary<string, object>>
{
{
"local",
new Dictionary<string, object>
{
{ "key", localMasterKey }
}
}
};
var keyVaultNamespace = CollectionNamespace.FromFullName(
"encryption.__keyVault");
var mongoOptions = new MongoOptionsExtension()
.WithConnectionString(connectionString)
.WithDatabaseName("myDatabase")
.WithKmsProviders(kmsProviders)
.WithKeyVaultNamespace(keyVaultNamespace)
.WithCryptProvider(
CryptProvider.AutoEncryptSharedLibrary,
Environment.GetEnvironmentVariable("CRYPT_SHARED_LIB_PATH"));
var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>()
.UseMongoDB(mongoOptions);

要使用 mongocryptd 而不是共享库,请将 CryptProvider.Mongocryptdmongocryptd 二进制文件的路径传递给 WithCryptProvider() 方法。

警告

不要在生产环境中使用本地 KMS 提供商。如果没有远程 KMS,则存在未经授权访问主密钥或永久丢失解密数据所需密钥的风险。

OnModelCreating() 方法中,对要加密的每个属性调用加密方法。您选择的方法控制该字段上可用的查询类型:

方法
查询支持
注意

IsEncrypted(dataKeyId)

在没有查询支持的情况下加密字段。用于存储但从不直接过滤的字段。

IsEncryptedForEquality(dataKeyId)

相等性 (==)

不适用于 Decimal128DoubleDocumentArray BSON 存储类型。

IsEncryptedForRange(min, max, dataKeyId)

范围 (>, <, >=, <=)

仅支持 DateTimeDecimal128DoubleInt32Int64 BSON 存储类型。

要加密所有的实体而不是标量属性,请在 OwnsOne()OwnsMany() 返回的 OwnedNavigationBuilderOwnershipBuilder 上调用 IsEncrypted(dataKeyId)

有关使用上述方法的完整示例,请参阅“示例:加密患者数据”部分。

以下 Patient 实体定义 document model,其中 SSNDateOfBirth 字段用于加密:

public class Patient
{
public ObjectId Id { get; set; }
public string Name { get; set; } = null!;
public string SSN { get; set; } = null!;
public DateTime DateOfBirth { get; set; }
}

以下 HospitalContext 标记了 OnModelCreating() 方法中用于加密的字段。SSN 在没有查询支持的情况下加密,DateOfBirth 在具有范围查询支持的情况下加密。构造函数接受两个数据加密密钥的 ID,您在初始化上下文之前在密钥保管库中创建这些 ID:

public class HospitalContext : DbContext
{
public DbSet<Patient> Patients { get; set; } = null!;
private readonly Guid _ssnDataKeyId;
private readonly Guid _dobDataKeyId;
public HospitalContext(
DbContextOptions options,
Guid ssnDataKeyId,
Guid dobDataKeyId)
: base(options)
{
_ssnDataKeyId = ssnDataKeyId;
_dobDataKeyId = dobDataKeyId;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Patient>(entity =>
{
entity.ToCollection("patients");
entity.Property(p => p.SSN)
.IsEncrypted(_ssnDataKeyId);
entity.Property(p => p.DateOfBirth)
.IsEncryptedForRange(
new DateTime(1900, 1, 1),
new DateTime(2100, 12, 31),
_dobDataKeyId);
});
}
}

以下代码配置加密选项,使用数据密钥 ID 实例化 HospitalContext,并插入和查询 Patient 文档:

var mongoOptions = new MongoOptionsExtension()
.WithConnectionString("<connection string URI>")
.WithDatabaseName("hospitalDb")
.WithKmsProviders(kmsProviders)
.WithKeyVaultNamespace(keyVaultNamespace)
.WithCryptProvider(
CryptProvider.AutoEncryptSharedLibrary,
Environment.GetEnvironmentVariable("CRYPT_SHARED_LIB_PATH"));
using var context = new HospitalContext(
new DbContextOptionsBuilder<HospitalContext>()
.UseMongoDB(mongoOptions)
.Options,
ssnDataKeyId,
dobDataKeyId);
context.Database.EnsureCreated();
context.Patients.Add(new Patient
{
Name = "John Doe",
SSN = "123-45-6789",
DateOfBirth = new DateTime(1985, 6, 15)
});
context.SaveChanges();
var results = context.Patients
.Where(p => p.DateOfBirth > new DateTime(1980, 1, 1))
.ToList();

默认情况下,EF Core 提供商仅将字段加密配置应用于客户端。单客户端加密适用于开发过程,因为加密模式可能仍在演进。

对于生产部署,在创建集合时在服务器上注册加密模式。服务器持有模式后,它会独立于客户端配置实施加密,从而防止因配置错误的客户端导致意外的明文写入。在服务器上注册模式后,您无法更改加密字段,除非重新创建集合。

要创建具有服务器端模式的集合,请将上下文的 Model 传递给 QueryableEncryptionSchemaGenerator.GenerateSchemas()。然后,将结果传递给 CreateCollection() 方法,如以下示例所示:

var encryptedSchemas =
QueryableEncryptionSchemaGenerator.GenerateSchemas(
context.Model);
using var client = new MongoClient(
"<connection string URI>");
var database = client.GetDatabase("hospitalDb");
foreach (var entityType in context.Model
.GetEntityTypes()
.Where(e => e.IsDocumentRoot()))
{
var collectionName = entityType.GetCollectionName();
if (encryptedSchemas.TryGetValue(
collectionName, out var schema))
{
database.CreateCollection(
collectionName,
new CreateCollectionOptions
{
EncryptedFields = schema
});
}
}
context.Database.EnsureCreated();

在服务器上存在加密集合后,您可以配置新的上下文实例以仅使用服务器模式。在 MongoOptionsExtension 上设置 QueryableEncryptionSchemaMode.Ignore,如下例所示:

var mongoOptions = new MongoOptionsExtension()
.WithConnectionString("<connection string URI>")
.WithDatabaseName("hospitalDb")
.WithKmsProviders(kmsProviders)
.WithKeyVaultNamespace(keyVaultNamespace)
.WithCryptProvider(
CryptProvider.AutoEncryptSharedLibrary,
Environment.GetEnvironmentVariable("CRYPT_SHARED_LIB_PATH"))
.WithQueryableEncryptionSchemaMode(
QueryableEncryptionSchemaMode.Ignore);

Ignore 模式下,OnModelCreating() 方法中的任何 IsEncrypted 配置对加密都没有影响。服务器模式控制哪些字段可以加密。

要了解有关 Queryable Encryption 的更多信息,请参阅 MongoDB Server 手册中的 Queryable EncryptionQueryable Encryption 使用案例 章节。

For complete 实现 details, see the EF Core 提供商 API 文档。