客户端加密
新功能:从 MongoDB 4.2 版本开始,客户端加密允许管理员和开发人员在将文档插入数据库之前对其进行加密。
使用客户端加密,开发人员可以在客户端加密字段,而无需任何服务器端配置或指令。客户端加密支持必须保证未经授权的第三方,包括服务器管理员,无法读取加密数据的工作负载。
警告
启用客户端加密会减少最大写入批处理大小,可能会对性能产生负面影响。
安装
客户端加密需要安装额外的软件包。
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 并手动将所需的共享对象放置到您的计算机上,如下所示
下载所有libmongocrypt变体的tar包在此。
解压缩您下载的文件。您将看到一个目录列表,每个目录对应一个操作系统。找到与您的操作系统匹配的目录并打开它。
在该文件夹中,打开名为“nocrypto”的文件夹。在lib或lb64文件夹中,您将根据您的操作系统找到libmongocrypt.so或libmongocrypt.dylib或libmongocrypt.dll文件。
将该文件移动到您在机器上想要保存的位置。您可以删除tar包中包含的其他文件。
要从源代码构建二进制文件
请按照libmongocrypt GitHub仓库中的README说明进行操作。
一旦您在机器上有了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 客户端加密涉及三个主要步骤
创建主密钥
创建数据密钥(并使用主密钥对其进行加密)
使用数据密钥加密数据
以下示例演示了如何使用本地主密钥执行这些步骤以进行自动加密。
注意
自动加密是仅适用于企业的功能,仅适用于集合上的操作。自动加密不支持对数据库或视图的操作,并且未绕过的操作将导致错误(请参阅 自动加密允许列表)。要绕过所有操作的自动加密,请将 bypass_auto_encryption
设置为 true,在 auto_encryption_options
中。
注意
自动加密需要认证用户具有 listCollections 权限操作。
注意
当使用自动加密时,如果配置了:auto_encryption_options
的Mongo::Client
实例的连接池大小有限(即非零的:max_pool_size
,这是默认设置),则如果以下任一条件为真,将创建一个独立的内部Mongo::Client
实例
未传递
auto_encryption_options[:key_vault_client]
。未传递或为false的
auto_encryption_options[:bypass_automatic_encryption]
。
如果创建了内部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服务器的企业构建不需要使用。要执行所有显式加密和解密操作,请使用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"
上面的示例演示了使用本地主密钥进行显式加密。有关使用其他密钥管理服务创建主密钥和创建数据密钥的更多信息,请参阅本教程的以下部分
创建主密钥
自动加密和显式加密都需要一个加密主密钥。这个主密钥用于加密数据密钥,而这些数据密钥又用于加密用户数据。主密钥可以通过两种方式之一生成:创建本地密钥或创建密钥管理服务中的密钥。目前Ruby驱动支持AWS密钥管理服务(KMS)、Azure密钥库和Google Cloud密钥管理(GCP KMS)。
本地主密钥
本地主密钥是一个96字节的二进制字符串。它应该作为环境变量或文本文件保存在您的机器上。
警告
使用本地主密钥是不安全的,如果您计划在生产中使用客户端加密,则不建议这样做。
运行以下代码使用Ruby生成本地主密钥
local_master_key = SecureRandom.random_bytes(96) # => "\xB2\xBE\x8EN\xD4\x14\xC2\x13\xC3..." (a binary blob)
远程主密钥
建议您使用远程密钥管理服务创建和存储您的密钥。为此,请遵循MongoDB客户端加密文档中"设置远程主密钥"的步骤。
有关创建主密钥的更多信息,请参阅MongoDB手册中的创建主密钥部分。
创建数据密钥
一旦创建了一个主密钥,通过在 #create_data_key
方法上对一个 Mongo::ClientEncryption
类的实例进行调用,创建一个数据密钥。此方法生成一个新的数据密钥并将其插入到密钥库集合中,这是您选择存储数据密钥的 MongoDB 集合。#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...>
有关生成新本地主密钥的更多信息,请参阅本地主密钥部分。
使用远程主密钥创建数据密钥
如果您已创建 AWS KMS 主密钥,请注意具有使用该密钥权限的 IAM 用户的访问密钥 ID 和秘密访问密钥。此外,请注意 AWS 区域和您的 master 密钥的 Amazon 资源编号 (ARN)。您将使用这些信息来生成数据密钥。
如果您已创建了一个Azure主密钥,请注意具有使用该密钥权限的应用程序的租户ID、客户端ID和客户端密钥。另外,请注意主密钥的名称、密钥版本(如有)和主密钥的密钥库端点。您将使用这些信息来生成数据密钥。
如果您已创建了一个GCP KMS主密钥,请注意具有使用该密钥权限的应用程序的电子邮件、私钥和客户端密钥。另外,请注意主密钥的项目ID、位置、密钥环、密钥名称和密钥版本(如有)。您将使用这些信息来生成数据密钥。
请注意,GCP私钥可以有不同的格式。Ruby驱动程序支持作为base64编码字符串的DER编码的RSA私钥。对于MRI Ruby,驱动程序还支持PEM编码的RSA私钥。
如果您使用兼容密钥管理互操作性协议(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`构造函数接受与`Mongo::Client`相同的`ssl_`选项。
自动加密选项
自动加密可以通过使用`auto_encryption_options`选项在`Mongo::Client`上进行配置。本节概述了`auto_encryption_options`内的字段,并解释了如何选择它们的值。
:key_vault_client
密钥库客户端是一个用于连接包含您的加密数据密钥的MongoDB集合的Mongo::Client
实例。例如,如果您的密钥库托管在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
是您希望存储数据密钥的数据库和集合的名称。例如,如果您的数据密钥存储在encryption
数据库的__keyVault
集合中
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 Engine元数据端点检索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文件之前,使用Base64编码您的数据密钥的UUID。
Base64.encode64(data_key_id.data) # => "sr6OTtQUwhPD..." (a base64-encoded string)
然后,创建一个新的JSON文件,其中包含符合JSON Schema Draft 4标准语法的模式映射。您可以在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模式提供更高的安全性。它可以保护免受恶意服务器宣传虚假JSON模式的风险,这可能会欺骗客户端发送应该加密的数据。
有关使用模式映射在您的集合上创建JSON模式验证器的更多信息,请参阅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
选项是一个指定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
- 这是一个包含 mongocryptd 启动参数的Array<String>
。Ruby 驱动将在启动守护进程时将这些参数传递给 mongocryptd。可能的参数有"--idleShutdownTimeoutSecs"
- mongocryptd 在关闭自身之前必须保持空闲的秒数。默认值是 60。"--port"
- mongocryptd 将监听连接的端口。默认是 27020。
:mongocryptd_uri
- 驱动程序将使用此 URI 连接到 mongocryptd。默认情况下,这是"mongodb://localhost:27020"
。:mongocryptd_spawn_path
- mongocryptd 可执行文件的路径。默认是"mongocryptd"
。:mongocryptd_bypass_spawn
- 一个表示驱动程序是否应跳过 mongocryptd 启动的Boolean
。
例如,如果您想将 mongocryptd 运行在端口 30000 上,请按照以下方式提供 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
的内容可能会发生变化。