客户端加密
客户端加密是 MongoDB 4.2 中的新增功能,允许管理员和开发者在将 MongoDB 文档插入数据库之前对其中的特定字段进行加密。
通过客户端加密,开发者可以加密客户端字段,而无需任何服务器端配置或指令。 客户端加密支持的工作负载中,应用程序必须保证未经授权的各方(包括服务器管理员)无法读取加密数据。
警告
启用客户端加密会减小最大写入批处理大小,可能会对性能产生负面影响。
安装
客户端加密需要安装额外的包。
libmongocrypt
Libmongocrypt 是驱动程序用于客户端加密的 C 库。 要使用客户端加密,必须在运行 Ruby 程序的机器上安装 libmongocrypt 库。
安装此库的最简单方法是安装 libmongocrypt-helper 如下所示:
gem install libmongocrypt-helper --pre
libmongocrypt-helper 的版本号是包含的 libmongocrypt 的版本,后跟版本号,例如 1.3.2.r1。 由于 Ruby 认为版本号中的任何字母都表示预发布版本,因此需要 --pre
标志。
驱动程序将自动加载 libmongocrypt-helper — 无需进一步配置。
注意
libmongocrypt-helper 目前仅支持 Linux 操作系统。
或者,您可以下载 libmongocrypt 的预构建二进制发行版,并在计算机上手动放置所需的共享对象,如下所示:
提取下载的文件。 您将看到一个目录列表,每个目录对应一个操作系统。 找到与您的操作系统匹配的目录并将其打开。
在该文件夹中,打开名为“nocrypto”的文件夹。 在 lib 或 lb64 文件夹中,您将找到 libmongocrypt.so 或 libmongocrypt.dylib 或 libmongocrypt.dll 文件,具体取决于您的操作系统。
将该文件移动到计算机上要保留的位置。 您可以删除 tarball 中包含的其他文件。
要从源代码构建二进制文件:
按照 libmongocrypt 存储库Github 中自述文件中的说明进行操作 。
在计算机上安装 libmongocrypt 二进制文件后,使用 LIBMongOCRYPT_PATH 环境变量指定二进制文件的路径。 建议将此变量添加到 rc 文件中。 例如:
export LIBMONGOCRYPT_PATH=/path/to/your/libmongocrypt.so
注意
本节中引用的二进制文件可以是 libmongocrypt 的预发布版本,不建议在生产环境中使用。
自动加密共享库
自动加密共享库是一个动态库,使客户端应用程序能够执行自动加密。 仅在自动加密时需要,这是一项仅限企业使用的功能。 如果您只想使用显式加密,则可以跳过此步骤。自动加密共享库提供与 mongocryptd 相同的功能(见下文),但不要求您生成另一个进程来执行自动加密。
有关安装说明,请参阅MongoDB 手册。
启用自动加密后,libmongocrypt 将在系统库路径中查找共享库,或者如果创建客户端时提供了:crypt_shared_lib_path
选项,则尝试从特定位置加载库。 如果可以加载该库,则驱动程序将不会尝试生成 mongocryptd 守护进程。 如果找不到共享库,仍会生成守护进程。
也可以在创建客户端时通过传递crypt_shared_lib_required: true
选项来要求使用共享库。 在这种情况下,如果无法加载共享库,则会引发错误。
注意
同一进程中的所有Mongo::Client
对象应使用相同的设置:crypt_shared_lib_path
,因为在单个操作系统进程中同时加载多个 crypt_shared 动态库是一种错误。
mongocryptd
Mongocryptd 是自动加密共享库的替代方案。 Mongocryptd 是一个守护进程,它告诉驱动程序在给定操作中要加密哪些字段。仅在自动加密时需要,这是一项仅限企业使用的功能。 如果您只想使用显式加密,则可以跳过此步骤。
Mongocryptd 预打包有 MongoDB 服务器的企业版( 4.2及更高版本)。 有关安装说明,请参阅MongoDB 手册。
为了配置 mongocryptd(例如,侦听哪个端口或用于生成守护进程的路径),有必要向执行自动加密的Mongo::Client
传递不同的选项。 有关更多信息,请参阅本教程的:extra_options部分。
自动加密
自动加密功能允许用户将Mongo::Client
实例配置为在执行数据库操作时始终加密特定文档字段。 配置Mongo::Client
后,它会在将任何需要加密的字段写入数据库之前自动对其进行加密,并在读取这些字段时自动解密这些字段。
客户端加密实现了信封加密,即使用数据密钥对数据进行加密,然后使用主密钥对数据进行加密。因此,在 MongoDB 中使用客户端加密涉及三个主要步骤:
创建主密钥
创建数据密钥(并使用主密钥对其进行加密)
使用数据密钥加密数据
以下示例演示了如何使用本地主密钥按照这些步骤执行自动加密。
注意
注意
自动加密要求经过身份验证的用户具有 listCollections 权限操作。
注意
使用自动加密时,如果配置了:auto_encryption_options
的Mongo::Client
实例的连接池大小有限(即非零:max_pool_size
,这是默认设置),则会创建一个单独的内部Mongo::Client
实例如果以下任一条件为 true:
auto_encryption_options[:key_vault_client]
未通过。auto_encryption_options[:bypass_automatic_encryption]
未通过或为 false。
Mongo::Client
如果创建了内部实例,则其配置选项与父客户端相同,不同之处在于:min_pool_size
设置为 0 并且省略了:auto_encryption_options
。
require 'mongo' ##################################### # Step 1: Create a local master key # ##################################### # A local master key is a 96-byte binary blob. local_master_key = SecureRandom.random_bytes(96) # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." ############################# # Step 2: Create a data key # ############################# kms_providers = { local: { key: local_master_key } } # The key vault client is a Mongo::Client instance connected to the collection # that will store your data keys. key_vault_client = Mongo::Client.new(['localhost:27017']) # Use an instance of Mongo::ClientEncryption to create a new data key client_encryption = Mongo::ClientEncryption.new( key_vault_client, key_vault_namespace: 'encryption.__keyVault', kms_providers: kms_providers ) data_key_id = client_encryption.create_data_key('local') # => <BSON::Binary... type=ciphertext...> ####################################################### # Step 3: Configure Mongo::Client for auto-encryption # ####################################################### # Create a schema map, which tells the Mongo::Client which fields to encrypt schema_map = { 'encryption_db.encryption_coll': { properties: { encrypted_field: { encrypt: { keyId: [data_key_id], bsonType: "string", algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } }, bsonType: "object" } } # Configure the client for automatic encryption client = Mongo::Client.new( ['localhost:27017'], auto_encryption_options: { key_vault_namespace: 'encryption.__keyVault', kms_providers: kms_providers, schema_map: schema_map }, database: 'encryption_db', ) collection = client['encryption_coll'] collection.drop # Make sure there is no data in the collection # The string "sensitive data" will be encrypted and stored in the database # as ciphertext collection.insert_one(encrypted_field: 'sensitive data') # The data is decrypted before being returned to the user collection.find(encrypted_field: 'sensitive data').first['encrypted_field'] # => "sensitive data" # A client with no auto_encryption_options is unable to decrypt the data client_no_encryption = Mongo::Client.new( ['localhost:27017'], database: 'encryption_db', ) client_no_encryption['encryption_coll'].find.first['encrypted_field'] # => <BSON::Binary... type=ciphertext...>
上面的示例演示了如何使用本地主密钥进行自动加密。 有关使用其他密钥管理服务创建主密钥和数据密钥的更多信息,请参阅本教程的以下部分:
显式加密
显式加密功能允许用户加密和解密单个数据段,例如字符串、整数或符号。 显式加密是一项社区功能,不需要 MongoDB Server 的企业版即可使用。要执行所有显式加密和解密操作,请使用 ClientEncryption 类的实例。
客户端加密实现了信封加密,即使用数据密钥对数据进行加密,然后使用主密钥对数据进行加密。因此,在 MongoDB 中使用客户端加密涉及三个主要步骤:
创建主密钥
创建数据密钥(并使用主密钥对其进行加密)
使用数据密钥加密数据
以下示例演示了如何使用本地主密钥按照这些步骤执行显式加密。
require 'mongo' ##################################### # Step 1: Create a local master key # ##################################### # A local master key is a 96-byte binary blob. local_master_key = SecureRandom.random_bytes(96) # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." ############################# # Step 2: Create a data key # ############################# kms_providers = { local: { key: local_master_key } } # The key vault client is a Mongo::Client instance connected to the collection # that will store your data keys. key_vault_client = Mongo::Client.new(['localhost:27017']) # Use an instance of Mongo::ClientEncryption to create a new data key client_encryption = Mongo::ClientEncryption.new( key_vault_client, key_vault_namespace: 'encryption.__keyVault', kms_providers: kms_providers ) data_key_id = client_encryption.create_data_key('local') # => <BSON::Binary... type=ciphertext...> ##################################################### # Step 3: Encrypt a string with explicit encryption # ##################################################### # The value to encrypt value = 'sensitive data' # Encrypt the value encrypted_value = client_encryption.encrypt( 'sensitive data', { key_id: data_key_id, algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } ) # Create the client you will use to read and write the data to MongoDB client = Mongo::Client.new( ['localhost:27017'], database: 'encryption_db', ) collection = client['encryption_coll'] collection.drop # Make sure there is no data in the collection # Insert the encrypted value into the collection collection.insert_one(encrypted_field: encrypted_value) # Use the client to read the encrypted value from the database, then # use the ClientEncryption object to decrypt it find_result = collection.find(encrypted_field: encrypted_value).first['encrypted_field'] # => <BSON::Binary...> (the find result is encrypted) unencrypted_result = client_encryption.decrypt(find_result) # => "sensitive data"
上面的示例演示了如何使用带有本地主密钥的显式加密。 有关使用其他密钥管理服务创建主密钥和数据密钥的更多信息,请参阅本教程的以下部分:
创建主密钥
自动加密和显式加密都需要加密主密钥。 该主密钥用于加密数据密钥,而数据密钥又用于加密用户数据。 主密钥可以通过以下两种方式之一生成:创建本地密钥,或在KMS中创建密钥。目前,Ruby 驱动程序支持 Amazon Web Services KMS、Azure Key Vault 和 Google Cloud Platform KMS (GCP KMS)。
本地主密钥
本地主密钥是一个 96 字节的二进制字符串。 它应作为环境变量或文本文件保留在您的计算机上。
警告
使用本地主密钥不安全,如果计划在生产中使用客户端加密,则不建议使用。
运行以下代码,使用 Ruby 生成本地主密钥:
local_master_key = SecureRandom.random_bytes(96) # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." (a binary blob)
远程主密钥
建议使用远程 KMS 来创建和存储主密钥。 为此,请按照 MongoDB 客户端加密文档中“设置远程主密钥”的步骤操作。
有关创建主密钥的更多信息,请参阅 MongoDB 手册的创建主密钥部分。
创建数据密钥
创建主密钥后,通过在Mongo::ClientEncryption
类的实例上调用#create_data_key
方法来创建数据密钥。 此方法生成一个新的数据密钥并将其插入密钥保管库集合,即您选择在其中存储数据密钥的 MongoDB collection。#create_data_key
方法以 BSON::Binary 对象的形式返回新创建的数据键的 ID。
使用本地主密钥创建数据密钥
如果您已创建本地主密钥,则可以使用它通过以下代码片段生成新的数据密钥:
警告
使用本地主密钥不安全,如果计划在生产中使用客户端加密,则不建议使用。
# A Mongo::Client instance that will be used to connect to the key vault # collection. Replace the server address with the address of the MongoDB # server where you would like to store your key vault collection. key_vault_client = Mongo::Client.new(['localhost:27017']) client_encryption = Mongo::ClientEncryption.new( key_vault_client, # Replace with the database and collection names for your key vault collection key_vault_namespace: 'encryption.__keyVault', kms_providers: { local: { key: local_master_key } } ) data_key_id = client_encryption.create_data_key('local') # => <BSON::Binary... type=ciphertext...>
有关生成新的本地主密钥的更多信息,请参阅“本地主密钥”部分。
使用远程主密钥创建数据密钥
如果您已创建 Amazon Web Services KMS 主密钥,请记下有权使用该密钥的 IAM 用户的访问密钥 ID 和秘密访问密钥。此外,请记下主密钥的 Amazon Web Services 地区和 Amazon 资源编号 (ARN)。您将使用该信息生成数据密钥。
如果已创建 Azure 主密钥,请记下租户 ID、客户端 ID 以及有权使用该密钥的应用程序的客户端密钥。 此外,请记下主密钥的密钥名称、密钥版本(如有)和密钥保管库端点。 您将使用该信息生成数据密钥。
如果您已创建 GCP KMS 主密钥,请记下电子邮件和私钥,以及有权使用该密钥的应用程序的客户端密钥。 此外,请记下主密钥的项目 ID、位置、密钥环、密钥名称和密钥版本(如有)。 您将使用该信息生成数据密钥。
请注意,GCP 私钥可以采用不同的格式。 Ruby 驱动程序支持将 DER 编码的 RSA 私钥作为 base64 编码的字符串。 对于 MRI Ruby,驱动程序还支持 PEM 编码的 RSA 私钥。
如果使用Key Management Interoperability Protocol (KMIP)兼容的服务器创建了主密钥,请记下服务器主机和端口以及ID。您将使用该信息生成数据密钥。 您可能还需要证书颁发机构证书以及客户端证书和私钥来向 KMIP 服务器进行身份验证。
# A Mongo::Client instance that will be used to connect to the key vault # collection. Replace the server address with the address of the MongoDB # server where you would like to store your key vault collection. key_vault_client = Mongo::Client.new(['localhost:27017']) client_encryption = Mongo::ClientEncryption.new( key_vault_client, # Replace with the database and collection names for your key vault collection key_vault_namespace: 'encryption.__keyVault', kms_providers: { aws: { access_key_id: 'IAM-ACCESS-KEY-ID', secret_access_key: 'IAM-SECRET-ACCESS-KEY' }, azure: { tenant_id: 'AZURE-TENANT-ID', client_id: 'AZURE-CLIENT-ID', client_secret: 'AZURE-CLIENT-SECRET' }, gcp: { email: 'GCP-EMAIL', # :private_key value should be GCP private key as base64 encoded # DER RSA private key, or PEM RSA private key, if you are using MRI Ruby. private_key: 'GCP-PRIVATE-KEY', }, kmip: { # KMIP server endpoint may include port. endpoint: 'KMIP-SERVER-HOST' }, # TLS options to connect to KMIP server. kms_tls_options: { kmip: { ssl_ca_cert: 'PATH-TO-CA-FILE', ssl_cert: 'PATH-TO-CLIENT-CERT-FILE', ssl_key: 'PATH-TO-CLIENT-KEY-FILE' } } } ) aws_data_key_id = client_encryption.create_data_key( 'aws', { master_key: { region: 'REGION-OF-YOUR-MASTER-KEY', key: 'ARN-OF-YOUR-MASTER-KEY' } } ) # => <BSON::Binary... type=ciphertext...> azure_data_key_id = client_encryption.create_data_key( 'azure', { master_key: { key_vault_endpoint: 'AZURE-KEY-VAULT-ENDPOINT', key_name: 'AZURE-KEY-NAME' } } ) # => <BSON::Binary... type=ciphertext...> gcp_data_key_id = client_encryption.create_data_key( 'gcp', { master_key: { project_id: 'GCP-PROJECT-ID', location: 'GCP-LOCATION', key_ring: 'GCP-KEY-RING', key_name: 'GCP-KEY-NAME', } } ) # => <BSON::Binary... type=ciphertext...>
有关生成新的远程主密钥和查找创建数据密钥所需信息的更多信息,请参阅本教程的远程主密钥部分。
有关创建数据密钥的更多信息,请参阅 MongoDB 手册的创建数据加密密钥部分。
有关可能的 KMS TLS 选项的列表,请参阅创建客户端引用。 Mongo::ClientEncryption
构造函数接受与ssl_
相同的Mongo::Client
选项。
自动加密选项
可以使用auto_encryption_options
选项Hash
在Mongo::Client
上配置自动加密。 本部分概述了auto_encryption_options
中的字段,并说明了如何选择其值。
:key_vault_client
密钥保管库客户端是一个Mongo::Client
实例,用于连接到包含加密数据密钥的 MongoDB 集合。 例如,如果您的密钥保管库托管在位于localhost:30000
的 MongoDB 实例上:
key_vault_client = Mongo::Client.new(['localhost:30000']) Mongo::Client.new(['localhost:27017], auto_encryption_options: { key_vault_client: key_vault_client, # ... (Fill in other options here) } )
如果您的数据密钥存储在存储加密数据的同一个 MongoDB 实例中,则可以将此选项留空,顶级客户端将用于插入和获取数据密钥。
:key_vault_namespace
密钥保管库命名空间是"database_name.collection_name"
格式的String
,其中database_name
和collection_name
是您要在其中存储数据密钥的collection和数据库的名称。例如,如果您的数据密钥存储在__keyVault
集合的encryption
数据库中:
Mongo::Client.new(['localhost:27017], auto_encryption_options: { key_vault_namespace: 'encryption.__keyVault', # ... (Fill in other options here) } )
没有默认的密钥保管库命名空间,必须提供此选项。
:kms_providers
包含作为键的 KMS 提供商名称和作为值的提供商选项的哈希。
Mongo::Client.new(['localhost:27017], auto_encryption_options: { key_vault_namespace: 'encryption.__keyVault', kms_providers: { aws: { access_key_id: 'IAM-ACCESS-KEY-ID', secret_access_key: 'IAM-SECRET-ACCESS-KEY' }, azure: { tenant_id: 'AZURE-TENANT-ID', client_id: 'AZURE-CLIENT-ID', client_secret: 'AZURE-CLIENT-SECRET' }, gcp: { email: 'GCP-EMAIL', # :private_key value should be GCP private key as base64 encoded # DER RSA private key, or PEM RSA private key, if you are using MRI Ruby. private_key: 'GCP-PRIVATE-KEY', }, kmip: { # KMIP server endpoint may include port. endpoint: 'KMIP-SERVER-HOST' }, # TLS options to connect to KMIP server. kms_tls_options: { kmip: { ssl_ca_cert: 'PATH-TO-CA-FILE', ssl_cert: 'PATH-TO-CLIENT-CERT-FILE', ssl_key: 'PATH-TO-CLIENT-KEY-FILE' } } } } )
客户端可以从环境或 EC2 或 ECS 元数据端点检索 AWS 档案。 要自动检索凭证,请指定空哈希作为 AWS 的 KMS 提供程序选项:
Mongo::Client.new(['localhost:27017'], auto_encryption_options: { key_vault_namespace: 'encryption.__keyVault', kms_providers: { aws: {} } } )
有关档案检索的更多详细信息,请参阅 “自动检索档案” 。
客户端可以从 Google Compute 引擎元数据端点检索 GCP 档案。要自动检索档案,请指定空哈希作为 GCP 的 KMS 提供商选项:
Mongo::Client.new(['localhost:27017'], auto_encryption_options: { key_vault_namespace: 'encryption.__keyVault', kms_providers: { gcp: {} } } )
:kms_tls_options
包含作为键的 KMP 提供商名称以及用于连接到相应提供商的 TLS 选项的哈希。
Mongo::Client.new(['localhost:27017], auto_encryption_options: { key_vault_namespace: 'encryption.__keyVault', kms_providers: { kmip: { endpoint: 'KMIP-SERVER-HOST' } }, kms_tls_options: { kmip: { ssl_ca_cert: 'PATH-TO-CA-FILE', ssl_cert: 'PATH-TO-CLIENT-CERT-FILE', ssl_key: 'PATH-TO-CLIENT-KEY-FILE' } } } )
:schema_map
模式映射是一个哈希,其中包含有关要自动加密和解密的字段的信息。
本教程顶部的代码片段演示了如何使用 Ruby Hash
创建模式映射。 虽然这种方法可行,但模式映射可能会变得很大,并且将它们包含在 Ruby 代码中可能会很不方便。 相反,建议将它们存储在单独的 JSON(JavaScript 对象表示法)文件中。
在创建 JSON 文件之前,对数据密钥的 UUID 进行 Base64 编码。
Base64.encode64(data_key_id.data) # => "sr6OTtQUwhPD..." (a base64-encoded string)
然后,按照 JSON schema Draft 4标准语法定义的格式创建一个包含模式映射的新 JSON 文件。 您可以在 MongoDB 手册的自动加密规则部分阅读有关格式化模式映射的更多信息。
{ "encryption_db.encryption_coll": { "properties": { "encrypted_field": { "encrypt": { "keyId": [{ "$binary": { "base64": "YOUR-BASE64-ENCODED-DATA-KEY-ID", "subType": "04" } }], "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } }, "bsonType": "object" } }
当您打算使用模式映射时,请使用bson
Ruby gem 中的BSON::ExtJSON
模块将其转换为 Ruby Hash
。
schema_map = BSON::ExtJSON.parse(File.read('/path/to/your/file.json')) # => { 'encryption_db.encryption_coll' => { ... } } Mongo::Client.new(['localhost:27017], auto_encryption_options: { schema_map: schema_map, # ... (Fill in other options here) } )
注意
还可以提供模式映射作为 MongoDB 集合上的验证器。 这称为“远程模式映射”,而将模式映射作为Mongo::Client
上的选项提供称为“本地模式映射”。
与依赖从服务器获取的 JSON schema 相比,提供本地模式映射可提供更高的安全性。 它可以防止恶意服务器宣传错误的 JSON schema,该模式可能会诱骗客户端发送应加密的未加密数据。
有关使用模式映射在集合上创建 JSON schema验证器的更多信息,请参阅 MongoDB 手册中的服务器端字段级加密执行。
:schema_map_path
还可以从文件加载模式映射。 如上所述准备模式映射,将其保存到文件中,然后使用:schema_map_path
选项将路径传递给文件。
Mongo::Client.new(['localhost:27017], auto_encryption_options: { schema_map_path: '/path/to/your/file.json', # ... (Fill in other options here) } )
:bypass_auto_encryption
:bypass_auto_encryption
选项是一个Boolean
,用于指定Mongo::Client
在写入数据库时是否应跳过加密。 如果:bypass_auto_encryption
为true
,客户端仍会对之前加密的数据执行自动解密。
Mongo::Client.new(['localhost:27017], auto_encryption_options: { bypass_auto_encryption: true, # ... (Fill in other options here) } )
:extra_options
:extra_options
是与生成 mongocryptd 相关的选项的Hash
。 此Hash
中的每个选项都有一个默认值,因此只需提供要覆盖其默认值的选项即可。
:mongocryptd_spawn_args
— 这是一个Array<String>
,包含用于生成 mongocryptd 的参数。 Ruby 驱动程序会在生成守护进程时将这些参数传递给 mongocryptd。 可能的参数有:"--idleShutdownTimeoutSecs"
— mongocryptd 在自行关闭之前必须保持空闲状态的秒数。 默认值为 60。"--port"
— mongocryptd 将侦听连接的端口。 默认值为 27020。
:mongocryptd_uri
- 驱动程序用于连接到 mongocryptd 的 URI。 默认为"mongodb://localhost:27020"
。:mongocryptd_spawn_path
— mongocryptd 可执行文件的路径。 默认值为"mongocryptd"
。:mongocryptd_bypass_spawn
—Boolean
指示驱动程序是否应跳过生成 mongocryptd。
例如,如果您想在端口 30000 上运行 mongocryptd,请提供extra_options
,如下所示:
Mongo::Client.new(['localhost:27017], auto_encryption_options: { extra_options: { mongocryptd_spawn_args: ['--port=30000'], mongocryptd_uri: 'mongodb://localhost:30000', } # ... (Fill in other options here) } )
警告
在客户端加密 API 的未来版本中, :extra_options
的内容可能会发生变化。