在本指南中,您可以学习;了解如何使用 EF Core 提供程序通过Queryable Encryption(QE) 来加密特定文档字段。
Overview
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)和本地密钥提供商。
启用可查询加密
以下部分介绍如何在上下文中配置加密选项,并在模型中标记要加密的实体属性:
配置加密选项
在配置任何使用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.Mongocryptd 和 mongocryptd 二进制文件的路径传递给 WithCryptProvider() 方法。
警告
请勿在生产中使用本地KMS提供商。如果没有远程KMS,您可能会冒未经授权访问权限主密钥的风险,或者永久丢失解密数据所需的密钥的风险。
标记要加密的字段
在 OnModelCreating() 方法中,对要加密的每个属性调用加密方法。您选择的方法控制着该字段可以使用哪些查询类型:
方法 | 查询支持 | 注意 |
|---|---|---|
| 无 | 在不支持查询的情况下加密字段。用于您存储但从不直接过滤的字段。 |
| 相等 ( | 不适用于 |
| 范围 ( | 仅支持 |
要加密自有实体而不是标量属性,请对从 OwnsOne() 或 OwnsMany() 返回的 OwnedNavigationBuilder 或 OwnershipBuilder 调用 IsEncrypted(dataKeyId)。
示例:加密患者数据
以下 Patient 实体定义了文档模型,其中包含要加密的 SSN 和 DateOfBirth 字段:
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,这两个密钥是在初始化上下文之前在密钥保管库中创建的:
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文档。