自动客户端字段级加密
在此页面上
自版本4.2 MongoDB 支持客户端字段级加密 (CSFLE) 。 通过此功能,您可以先对应用程序中的数据进行加密,然后再通过网络将其发送到 MongoDB。 启用 CSFLE 后,任何 MongoDB 产品都无法以未加密的形式访问您的数据。
您可以使用以下机制设置 CSFLE:
自动加密:使您能够执行加密的读取和写入操作,而无需编写代码来指定如何加密字段。
显式加密:使您能够通过 MongoDB 驱动程序的加密库执行加密的读取和写入操作。 您必须在整个应用程序中指定使用此库进行加密的逻辑。
从版本9.0开始, Mongoid 支持 CSFLE 的自动加密功能。 本教程将引导您完成在 Mongoid 中设置和使用 CSFLE 的过程。
注意
本教程并未涵盖所有 CSLFE 功能。 您可以在服务器文档中找到有关 MongoDB CSFLE 的更多信息。
注意
如果要使用显式 FLE,请遵循Ruby 驱动程序文档。
安装
您可以在驱动程序文档中找到有关如何安装必要依赖项的详细说明。
请注意应用程序中使用的 Ruby 驱动程序的版本,并选择以下相应的步骤。
安装 libmongocrypt
这可以通过以下两种方式之一完成。
添加 libmongocrypt-helper gem 到您的
Gemfile
或libmongocrypt
下载 版本存档 ,提取与操作系统匹配的版本,并相应地设置LIBMONGOCRYPT_PATH
环境变量。
安装自动加密共享库(Ruby 驱动程序2.19 +)
如果您使用Ruby 驱动程序版本2.19 及更高版本,则应按照 手册中“可查询加密的Queryable Encryption 加密共享库”页面上的说明安装自动加密共享库MongoDB Server 。
所需步骤如下:
导航至MongoDB 下载中心
从版本下拉列表中,选择
x.y.z (current)
(当前最新版本)。在平台下拉列表中,选择您的平台。
在 Package(包)下拉列表中,选择
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 Server 的企业版( 4.2及更高版本)中。 有关安装说明,请参阅MongoDB 手册。
将 添加到您的ffi
Gemfile
MongoDB Ruby 驱动程序使用 ffi gem 从libmongocrypt
调用函数。由于此 gem 不是驱动程序的依赖项,因此需要手动将其添加到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
注意
如果您正在开发 Railspreload_models
应用程序,建议在true
中将 设置为mongoid.yml
。这将确保 Mongoid 在应用程序启动之前加载所有模型,并在读取或写入任何数据之前配置加密模式。
已知限制
处理数据
在许多情况下,自动使用 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 轮换数据密钥。
以下是使用Amazon Web Services 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 对类型敏感,例如,您不能在声明为
String
的字段中存储整数。对于字符串,确保空值始终为空字符串或只是未设置,但不是
nil
(CSFLE 不支持原生null
)。此操作需要应用程序停机。