对于 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 群集。

  • 访问密钥管理服务 (KMS)。支持的KMS提供商包括Amazon Web Services、 Azure、 Google Cloud Platform、 Key Management Interoperability Protocol (KMIP)和本地密钥提供商。

  • 环境中已安装自动加密共享库 ()CRYPT_SHAREDmongocryptd 。要学习;了解如何安装共享库,请参阅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 实体定义了文档模型,其中包含要加密的 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; }
}

以下 HospitalContextOnModelCreating() 方法中标记要加密的这些字段。 SSN 在不支持查询的情况下加密,DateOfBirth 在支持范围查询的情况下加密。构造函数接受两个数据加密密钥的 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 Encryption和Queryable Encryption使用案例部分。

有关完整的实施详情,请参阅 EF Core 提供程序API文档。