Join us Sept 17 at .local NYC! Use code WEB50 to save 50% on tickets. Learn more >
MongoDB Event
Docs 菜单
Docs 主页
/ / /
Mongoid
/

客户端字段级加密 (Client-Side Field Level Encryption)

在本指南中,您可以学习;了解如何使用客户端字段级加密(CSFLE) 来加密数据。CSFLE 允许您先对应用应用程序中的数据进行加密,然后再通过网络将其发送到MongoDB。 这意味着任何MongoDB产品都无法访问权限您的未加密形式的数据。

您可以使用以下机制之一设立CSFLE:

  • 自动加密:允许您执行加密的读写写入,而无需指定如何加密字段

  • 显式加密:允许您在整个应用程序中使用指定的加密逻辑执行加密的读取和写入操作。

本指南介绍如何设立具有自动加密的 CSFLE。

要将 CSFLE 与 Mongoid 结合使用,必须首先安装以下依赖项:

  • libmongocrypt

  • 自动加密共享库(如果您使用的是Ruby驾驶员v2.19 或更高版本)。 或者 mongocryptd(如果您使用的是Ruby驾驶员v2.18 或更早版本)。

  • ffi

以下部分详细介绍了如何安装这些依赖项。

您可以通过将libmongocrypt libmongocrypt-helper gem 添加到Gemfile 或手动下载该库来安装 。

要通过添加 gem文件来安装 libmongocrypt,请导航到应用程序所在的文件夹,然后在Shell中运行以下命令:

gem install libmongocrypt-helper --pre

注意

由于 libmongocrypt-helper 的版本号可能包含字母,这在Ruby中表示预发布版本,因此需要 --pre 标志。

以下步骤详细说明了如何安装自动加密共享库:

  1. 在浏览器中,导航至MongoDB下载中心。

  2. Version 下拉列表中选择最新的当前版本,用 (current)标签表示。

  3. Platform 下拉列表中选择您的平台。

  4. Package 下拉列表中选择 crypt_shared

  5. 单击 Download 按钮下载共享库。

下载文件后,解压缩内容并将文件保存在应用程序可以访问权限的位置。 然后,在应用程序中配置 mongoid.yml文件以点该库,如以下示例所示:

development:
clients:
default:
options:
auto_encryption_options:
extra_options:
crypt_shared_lib_path: '<Path to Shared Library>'

如果您使用的是Ruby驾驶员版本 2.18 或更早版本,则必须使用 mongocryptd 而不是自动加密共享库。 mongocryptd 预打包在MongoDB Server的企业版中。 有关如何安装和配置mongocryptd 的说明,请参阅MongoDB Server手册中的安装 mongocryptd指南。

Mongoid 使用 ffi gem 调用libmongocrypt 中的函数。通过在Shell中运行以下命令,将 gem 添加到Gemfile

gem 'ffi'

要加密和解密数据,您必须首先创建客户主密钥(集合扫描)。 集合扫描是用于加密数据加密密钥(DEK)的密钥。如果无法访问权限集合扫描,您的客户端应用程序将无法解密与加密数据关联的 DEK。

您可以通过运行以下Ruby代码来创建本地存储的密钥以用作本地集合扫描以进行测试:

require 'securerandom'
SecureRandom.hex(48)

警告

在生产环境中使用本地集合扫描不安全。 对于生产环境,请使用远程KMS创建和存储您的集合扫描。

要学习;了解有关KMS提供程序的更多信息,请参阅MongoDB Server手册中的KMS提供程序指南。

您必须配置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 选项添加到模型的字段定义中并指定 deterministickey_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

要学习;了解有关 CSFLE 的更多信息,请参阅MongoDB Server手册中的客户端字段级加密指南。

要学习;了解有关在应用程序中配置 Mongoid 的更多信息,请参阅 应用程序配置指南。

后退

保护您的数据

在此页面上