自动客户端字段级加密
本页内容
自4.2版本起,MongoDB支持客户端字段级加密(CSFLE)。这是一个功能,允许您在将数据通过网络发送到MongoDB之前对应用中的数据进行加密。启用CSFLE后,没有任何MongoDB产品可以访问未加密的数据。
您可以使用以下机制设置CSFLE:
从版本9.0开始,Mongoid支持CSFLE的自动加密功能。本教程将指导您在Mongoid中设置和使用CSFLE的过程。
注意
本教程不涵盖所有CSLFE功能。您可以在服务器文档中找到有关MongoDB CSFLE的更多信息。
注意
如果您想使用显式FLE,请参考Ruby驱动程序文档。
安装
您可以在驱动程序文档中找到如何安装必需依赖项的详细说明。
请注意您应用程序中使用的Ruby驱动程序的版本,并选择下面的适当步骤。
安装libmongocrypt
这可以通过以下两种方式之一完成。
将 libmongocrypt-helper 钩子 添加到您的
Gemfile
或下载
libmongocrypt
的 发布存档,提取与您的操作系统匹配的版本,并相应地设置环境变量LIBMONGOCRYPT_PATH
。
安装自动加密共享库(Ruby 驱动 2.19+)
如果您使用的是 Ruby 驱动程序版本 2.19 及以上,应按照服务器手册中 自动加密共享库 页面上的说明进行安装。
所需步骤如下
导航到 MongoDB 下载中心
从版本下拉菜单中选择
x.y.z (当前)
(最新的当前版本)。在平台下拉菜单中选择您的平台。
在包下拉菜单中选择
crypt_shared
。点击下载。
解压后,确保在您的 mongoid.yml
中配置了库的完整路径,如下所示
development: clients: default: options: auto_encryption_options: extra_options: crypt_shared_lib_path: '/path/to/mongo_crypt_v1.so'
安装 mongocryptd
(Ruby驱动程序2.18或更早版本)
如果您使用的是较旧的Ruby驱动程序版本,则需要手动安装 mongocryptd
。 mongocryptd
随企业版MongoDB服务器(版本4.2及更高版本)预包装。有关安装说明,请参阅MongoDB手册。
将 ffi
添加到您的 Gemfile
MongoDB Ruby驱动程序使用ffi宝石 从 libmongocrypt
调用函数。由于此宝石不是驱动程序的依赖项,因此需要手动添加到您的 Gemfile
gem 'ffi'
创建客户主密钥
客户主密钥(CMK)用于加密数据加密密钥。最简单的方法是使用本地存储的96字节密钥。您可以使用以下Ruby代码生成此类密钥
require 'securerandom' SecureRandom.hex(48) # => "f54ab...."
在本教程的后续部分,我们假设客户主密钥可以从CUSTOMER_MASTER_KEY
环境变量中获取。
配置客户端
自动CSFLE需要对MongoDB客户端进行一些额外配置。假设您的应用程序只有一个default
客户端,您需要将以下内容添加到您的mongoid.yml
development: clients: default: uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority options: auto_encryption_options: # This key enables automatic encryption key_vault_namespace: 'encryption.__keyVault' # Database and collection to store data keys kms_providers: # Tells the driver where to obtain master keys local: # We use the local key in this tutorial key: "<%= ENV['CUSTOMER_MASTER_KEY'] %>" # Key that we generated earlier extra_options: crypt_shared_lib_path: '/path/to/mongo_crypt_v1.so' # Only if you use the library
创建数据加密密钥
数据加密密钥(DEK)是您用于加密MongoDB文档中字段的密钥。您使用CMK加密后将其存储在密钥保管库集合中。
在Mongoid中创建数据加密密钥(DEK)可以使用db:mongoid:encryption:create_data_key
Rake
任务
rake db:mongoid:encryption:create_data_key Created data key with id: 'KImyywsTQWi1+cFYIHdtlA==' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
如果需要,您可以创建多个DEK。
rake db:mongoid:encryption:create_data_key Created data key with id: 'Vxr5m+5cQISjDOruzZgE0w==' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
您还可以为DEK提供备选名称。这允许您在配置字段的加密时通过名称引用DEK。它还允许您在运行时动态地将DEK分配给字段。
rake db:mongoid:encryption:create_data_key -- --key-alt-name=my_data_key Created data key with id: 'yjF8hKmKQsqGeFGXlB9Sow==' with key alt name: 'my_data_key' for kms provider: 'local' in key vault: 'encryption.__keyVault'.
配置加密模式
现在我们可以告诉Mongoid应该加密什么
class Patient include Mongoid::Document include Mongoid::Timestamps # Tells Mongoid what DEK should be used to encrypt fields of the document # and its embedded documents. encrypt_with key_id: 'KImyywsTQWi1+cFYIHdtlA==' # This field is not encrypted. field :category, type: String # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm. field :passport_id, type: String, encrypt: { deterministic: false } # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic # algorithm. field :blood_type, type: String, encrypt: { deterministic: true } # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm and using a different data key. field :ssn, type: Integer, encrypt: { deterministic: false, key_id: 'Vxr5m+5cQISjDOruzZgE0w==' } embeds_one :insurance end class Insurance include Mongoid::Document include Mongoid::Timestamps field :insurer, type: String # This field is encrypted using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm using the key which alternate name is stored in the # policy_number_key field. field :policy_number, type: Integer, encrypt: { deterministic: false, key_name_field: :policy_number_key } embedded_in :patient end
注意
如果您正在开发Rails应用程序,建议在mongoid.yml
中将preload_models
设置为true
。这将确保在应用程序启动之前,Mongoid加载所有模型,并在读取或写入任何数据之前配置加密模式。
已知限制
MongoDB CSFLE有一些限制,这些限制在服务器手册的CSFLE限制页面中描述。这些限制也适用于Mongoid。
Mongoid不支持加密
embeds_many
关系。如果您使用
:key_name_field
选项,该字段必须使用非确定性算法进行加密。为了以确定性方式加密字段,您必须指定:key_id
选项。
处理数据
在许多情况下,自动使用CSFLE是透明的。
注意
以下代码示例中,我们假设存在一个名为 unencrypted_client
的变量,它是一个连接到同一数据库但没有加密的 MongoClient
。我们使用此客户端来演示实际上在数据库中持久化的是什么。
文档可以像往常一样创建,字段将根据配置进行加密和解密
Patient.create!( category: 'ER', passport_id: '123456', blood_type: 'AB+', ssn: 98765, insurance: Insurance.new(insurer: 'TK', policy_number: 123456, policy_number_key: 'my_data_key') ) # Fields are encrypted in the database unencrypted_client['patients'].find.first # => # {"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4292'), # "category"=>"ER", # "passport_id"=><BSON::Binary:0x404080 type=ciphertext data=0x012889b2cb0b1341...>, # "blood_type"=><BSON::Binary:0x404560 type=ciphertext data=0x022889b2cb0b1341...>, # "ssn"=><BSON::Binary:0x405040 type=ciphertext data=0x012889b2cb0b1341...>, # "insurance"=>{"_id"=>BSON::ObjectId('6446a1d046ebfd701f9f4293'), "insurer"=>"TK", "policy_number"=><BSON::Binary:0x405920 type=ciphertext data=0x012889b2cb0b1341...>}, "policy_number_key"=>"my_data_key"}
使用确定性算法加密的字段可以进行查询。仅支持精确匹配查询。有关更多详细信息,请参阅服务器文档。
# We can find documents by deterministically encrypted fields. Patient.where(blood_type: "AB+").to_a # => [#<Patient _id: 6447e34d46ebfd3debdd9c39, category: "ER", passport_id: "123456", blood_type: "AB+", ssn: 98765>]
加密密钥管理
客户主密钥
您的客户主密钥是您用于加密数据加密密钥的密钥。MongoDB在创建数据加密密钥时自动使用指定的CMK来加密数据加密密钥。
CMK是CSFLE中最敏感的密钥。如果您的CMK受到损害,所有加密数据都可以被解密。
- MongoDB CSFLE支持以下密钥管理系统(KMS)提供程序
任何KMIP兼容的密钥管理系统
本地密钥提供程序(仅用于测试)
数据加密密钥
可以使用db:mongoid:encryption:create_data_key
Rake
任务创建数据加密密钥。默认情况下,它们存储在与数据库相同的集群上。但是,将密钥分别存储可能是个好主意。这可以通过在mongoid.yml
中指定密钥库客户端来实现
development: clients: key_vault: uri: mongodb+srv://user:pass@anothercluster.mongodb.net/blog_development?retryWrites=true&w=majority default: uri: mongodb+srv://user:pass@yourcluster.mongodb.net/blog_development?retryWrites=true&w=majority options: auto_encryption_options: key_vault_client: :key_vault # Client to connect to key vault # ...
加密密钥轮换
可以使用Ruby驱动程序的rewrap_many_data_key
方法轮换加密密钥。此方法自动解密多个数据加密密钥,并使用指定的CMK重新加密它们。然后更新密钥库集合中的轮换密钥。此方法允许您根据两个可选参数轮换加密密钥
用于指定要轮换哪些密钥的过滤器。如果没有数据密钥与给定的过滤器匹配,则不会轮换任何密钥。省略过滤器以轮换密钥库集合中的所有密钥。
表示新CMK的对象。省略此对象以使用当前CMK轮换数据密钥。
以下是一个使用AWS KMS轮换密钥的示例
# Create a key vault client key_vault_client = Mongo::Client.new('mongodb+srv://user:pass@yourcluster.mongodb.net') # Or, if you declared the key value client in mongoid.yml, use it key_vault_client = Mongoid.client(:key_vault) # Create the encryption object encryption = Mongo::ClientEncryption.new( key_vault_client, key_vault_namespace: 'encryption.__keyVault', kms_providers: { aws: { "accessKeyId": "<IAM User Access Key ID>", "secretAccessKey": "<IAM User Secret Access Key>" } } ) encryption.rewrap_many_data_key( {}, # We want to rewrap all keys { provider: 'aws', master_key: { region: 'us-east-2', key: 'arn:aws:kms:us-east-2:...' } } )
将自动加密添加到现有项目
MongoDB自动CSFLE支持原地加密。您可以为现有数据库启用加密,同时仍然可以读取未加密的数据。所有写入数据库的数据都将被加密。然而,一旦启用加密,所有查询操作都将使用加密数据。
# We assume that there are two documents in the database, one created without # encryption enabled, and one with encryption. # We can still read both. Patient.all.to_a # => # [#<Patient _id: 644937ac46ebfd02468e58c8, category: "ER", passport_id: "DE-1257", blood_type: "AB+", ssn: 123456>, # #<Patient _id: 644937c946ebfd029309b912, category: "ER", passport_id: "AT-1545", blood_type: "AB+", ssn: 987654>] # But when we query, we can see only the latter one. Patient.where(blood_type: 'AB+').to_a # => [#<Patient _id: 644937c946ebfd029309b912, category: "ER", passport_id: "AT-1545", blood_type: "AB+", ssn: 987654>]
如果想要加密现有数据库,可以通过读取和回写所有数据来实现,即使没有任何更改。如果决定这样做,请记住以下几点。
验证现有数据的完整性以保证数据一致性。CSFLE是类型敏感的——例如,您不能在声明为
的字段中存储整数。 对于字符串,请确保空值始终为空字符串或未设置,而不是
nil
(CSFLE不支持原生的null
)。此操作需要应用程序停机。