Overview
在本指南中,您可以学习;了解如何使用客户端字段级加密(CSFLE) 来加密数据。CSFLE 允许您先对应用应用程序中的数据进行加密,然后再通过网络将其发送到MongoDB。 这意味着任何MongoDB产品都无法访问权限您的未加密形式的数据。
您可以使用以下机制之一设立CSFLE:
自动加密:允许您执行加密的读写写入,而无需指定如何加密字段
显式加密:允许您在整个应用程序中使用指定的加密逻辑执行加密的读取和写入操作。
本指南介绍如何设立具有自动加密的 CSFLE。
安装依赖项
要将 CSFLE 与 Mongoid 结合使用,必须首先安装以下依赖项:
libmongocrypt
自动加密共享库(如果您使用的是Ruby驾驶员v2.19 或更高版本)。 或者
mongocryptd
(如果您使用的是Ruby驾驶员v2.18 或更早版本)。ffi
以下部分详细介绍了如何安装这些依赖项。
libmongocrypt
您可以通过将libmongocrypt
libmongocrypt-helper gem 添加到Gemfile
或手动下载该库来安装 。
要通过添加 gem文件来安装 libmongocrypt
,请导航到应用程序所在的文件夹,然后在Shell中运行以下命令:
gem install libmongocrypt-helper --pre
注意
由于 libmongocrypt-helper
的版本号可能包含字母,这在Ruby中表示预发布版本,因此需要 --pre
标志。
共享库(驱动程序v2.19 或更高版本)
以下步骤详细说明了如何安装自动加密共享库:
从
Version
下拉列表中选择最新的当前版本,用(current)
标签表示。在
Platform
下拉列表中选择您的平台。在
Package
下拉列表中选择crypt_shared
。单击
Download
按钮下载共享库。
下载文件后,解压缩内容并将文件保存在应用程序可以访问权限的位置。 然后,在应用程序中配置 mongoid.yml
文件以点该库,如以下示例所示:
development: clients: default: options: auto_encryption_options: extra_options: crypt_shared_lib_path: '<Path to Shared Library>'
mongocryptd(驱动程序v2.18 或更早版本)
如果您使用的是Ruby驾驶员版本 2.18 或更早版本,则必须使用 mongocryptd
而不是自动加密共享库。 mongocryptd
预打包在MongoDB Server的企业版中。 有关如何安装和配置mongocryptd
的说明,请参阅MongoDB Server手册中的安装 mongocryptd指南。
ffi
Mongoid 使用 ffi gem 调用libmongocrypt
中的函数。通过在Shell中运行以下命令,将 gem 添加到Gemfile
:
gem 'ffi'
创建客户主密钥
要加密和解密数据,您必须首先创建客户主密钥(集合扫描)。 集合扫描是用于加密数据加密密钥(DEK)的密钥。如果无法访问权限集合扫描,您的客户端应用程序将无法解密与加密数据关联的 DEK。
您可以通过运行以下Ruby代码来创建本地存储的密钥以用作本地集合扫描以进行测试:
require 'securerandom' SecureRandom.hex(48)
配置客户端
您必须配置MongoDB客户端才能实现CSFLE。 要为 CSFLE 配置客户端,请将以下代码添加到您的 mongoid.yml
文件中:
development: clients: default: uri: "<connection string>" options: auto_encryption_options: # This key enables automatic encryption key_vault_namespace: 'encryption.__keyVault' # Database and collection in which to store data keys kms_providers: # Tells the driver where to obtain master keys local: # Specifies that the key is local key: "<Path to your CMK>" extra_options: crypt_shared_lib_path: '<Path to Shared Library>' # Only required for Ruby versions 2.19 or later
注意
确保替换前面代码示例中用方括号 (<>
) 括起来的占位符。
创建数据加密密钥
数据加密密钥(DEK)是用于加密文档中字段的密钥。 MongoDB将使用集合扫描加密的DEK 作为BSON文档存储在 Key Vault集合中。 MongoDB永远无法解密 DEK,因为密钥管理由客户端和客户控制。
要在 Mongoid 中创建 DEK,可以使用 db:mongoid:encryption:create_data_key
rake任务,如以下示例所示:
rake db:mongoid:encryption:create_data_key
您可以通过重复前面的命令以创建多个 DEK,生成的密钥数量与您要生成的密钥数量相同。
您还可以为 DEK 提供备用名称。 这样,您就可以在为字段配置加密时按名称引用 DEK,并在运行时将 DEK 动态分配给字段。
以下示例在生成新的 DEK 时创建一个备用名称:
rake db:mongoid:encryption:create_data_key -- --key-alt-name=<custom DEK name>
配置加密模式
您可以通过将 encrypt
选项添加到模型的字段定义中并指定 deterministic
和 key_id
选项来指定要加密的字段,如以下示例所示:
class Patient include Mongoid::Document include Mongoid::Timestamps encrypt_with key_id: '<data encryption key>' # This field is not encrypted field :category, type: String # This field is encrypted by using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm field :passport_id, type: String, encrypt: { deterministic: false } # This field is encrypted by using AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic # algorithm field :blood_type, type: String, encrypt: { deterministic: true } # This field is encrypted by using AEAD_AES_256_CBC_HMAC_SHA_512-Random # algorithm and a different data key field :ssn, type: Integer, encrypt: { deterministic: false, key_id: '<New key ID' } 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 a key with an alternate name 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 在读取或写入任何数据之前加载所有模型并配置加密模式。
限制
将 CSFLE 与 Mongoid 结合使用时,应用以下限制:
Mongoid 不支持对
embeds_many
关联加密。如果使用
:key_name_field
选项,则必须使用非确定性算法对该字段进行加密。 要确定性地加密字段,必须改为指定:key_id
选项。MongoDB Server手册中的 CSFLE 限制页面上列出的限制也应用于 Mongoid。
处理数据
通常,自动 CSFLE 在应用程序中透明地运行。 在为 CSFLE 配置应用应用程序后,您可以照常创建文档,Mongoid 会根据您的配置自动对这些文档进行加密和解密。
以下示例在为 CSFLE 配置的应用程序中创建了一个新的 Patient
文档。 然后,它使用一个名为 unencrypted_client
的客户端来读取该文档,该客户端已连接到数据库,但未针对 CSFLE 进行配置。
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"}
在前面的示例中,unencrypted_client
客户端无法读取加密字段。 但是,如果您使用配置了 CSFLE 的客户端查询文档,Mongoid 会自动解密字段。
轮换加密密钥
您可以使用 rewrap_many_data_key
Ruby驾驶员程序方法轮换加密密钥。 此方法会自动解密多个数据加密密钥,并使用指定的集合扫描重新加密这些密钥。 然后,它会更新密钥保管库集合中轮换的密钥。
rewrap_many_data_key
方法采用以下参数:
筛选器,用于指定要轮换的字段。 如果没有数据密钥与给定的过滤匹配,则不会轮换任何密钥。 省略过滤可轮换密钥保管库集合中的所有密钥。
表示新集合扫描 的对象,使用该 CMK 重新加密 DEK。 省略此对象可使用当前的 CMK 轮换数据密钥。
以下示例使用Amazon Web Services KMS轮换加密密钥:
# Create a key vault client key_vault_client = Mongo::Client.new('<connection string>') # 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( {}, # Empty filter to rewrap all keys { provider: 'aws', master_key: { region: 'us-east-2', key: 'arn:aws:kms:us-east-2:...' } } )
为现有项目添加自动加密
使用 Mongoid 的自动 CSFLE 支持就地加密。 您可以对现有数据库启用加密,但仍可读取未加密的数据。 但是,一旦启用加密,所有新数据都会加密,并且任何查询操作都仅使用加密的文档。 这意味着,如果在启用加密之前保存了某些文档,则查询可能不会返回所有文档。
以下示例查询一个包含一份加密文档和一份未加密文档的集合:
# Print all documents in the collection. The first document is unencrypted, and # the second is encrypted. 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>] # Querying for documents with a CSFLE-enabled client returns only the encrypted document Patient.where(blood_type: 'AB+').to_a # => [#<Patient _id: 644937c946ebfd029309b912, category: "ER", passport_id: "AT-1545", blood_type: "AB+", ssn: 987654>]
您可以通过使用启用 CSFLE 的客户端读取然后写回所有数据,对集合中的现有数据进行加密。 执行此操作时,请确保所有现有数据均为预期类型,并且空值未设立为 nil
。