客户端字段级加密

MongoDB 4.2中的新功能是,客户端字段级加密允许应用程序在原有的MongoDB加密功能(如 Encryption at RestTLS/SSL (Transport Encryption) .

通过字段级加密,应用程序可以加密文档中的字段 先前的 通过网络将数据传输到服务器。客户端字段级加密支持应用程序必须保证未授权方(包括服务器管理员)无法读取加密数据的工作负载。

参见

MongoDB文档

client-side-field-level-encryption

依赖关系

要开始在项目中使用客户端字段级加密,您需要安装 pymongocrypt 库以及驱动程序本身。安装驱动程序和pymongocrypt的兼容版本,如下所示:

$ python -m pip install 'pymongo[encryption]'

注意,在Linux上安装需要pip19或更高版本才能支持manylinux2010控制盘。有关安装pymongocrypt的更多信息,请参见 the installation instructions on the project's PyPI page .

孟加拉语

这个 mongocryptd 二进制文件是自动客户端加密所必需的,并作为组件包含在 MongoDB Enterprise Server package . 有关详细的安装说明,请参见 the MongoDB documentation on mongocryptd .

mongocryptd 执行以下操作:

  • 分析指定给数据库连接的自动加密规则。如果JSON架构包含无效的自动加密语法或任何文档验证语法, mongocryptd 返回错误。

  • 使用指定的自动加密规则标记读写操作中的字段以进行加密。

  • 拒绝对加密字段应用时可能返回意外或错误结果的读/写操作。有关支持和不支持的操作,请参阅 Read/Write Support with Automatic Field Level Encryption .

配置了自动加密的MongoClient将自动生成 mongocryptd 从应用程序的 PATH . 作为自动加密选项的一部分,应用程序可以控制生成行为。例如,将路径设置为 mongocryptd 流程:

auto_encryption_opts = AutoEncryptionOpts(
        ...,
        mongocryptd_spawn_path='/path/to/mongocryptd')

控制的日志输出 mongocryptd 传递选项使用 mongocryptd_spawn_args ::

auto_encryption_opts = AutoEncryptionOpts(
        ...,
        mongocryptd_spawn_args=['--logpath=/path/to/mongocryptd.log', '--logappend'])

如果您的应用程序希望管理 mongocryptd 手动处理,可以禁用生成 mongocryptd ::

auto_encryption_opts = AutoEncryptionOpts(
        ...,
        mongocryptd_bypass_spawn=True,
        # URI of the local ``mongocryptd`` process.
        mongocryptd_uri='mongodb://localhost:27020')

mongocryptd 只负责支持自动客户端字段级加密,本身不执行任何加密或解密。

自动客户端字段级加密

通过创建一个 MongoClientauto_encryption_opts 选项设置为的实例 AutoEncryptionOpts . 下面的示例演示如何使用 ClientEncryption 创建新的加密数据密钥。

注解

自动客户端字段级加密需要mongodb4.2enterprise或mongodb4.2atlas集群。服务器的社区版本支持自动解密以及 显式加密 .

提供本地自动加密规则

下面的示例演示如何通过 schema_map 选项。自动加密规则使用 strict subset of the JSON Schema syntax .

提供 schema_map 提供了比依赖从服务器获取的JSON模式更安全的功能。它可以防止恶意服务器发布虚假的JSON模式,这可能诱使客户端发送应加密的未加密数据。

中提供的JSON架构 schema_map 仅适用于配置自动客户端字段级加密。JSON架构中的其他验证规则将不会由驱动程序强制执行,并将导致错误。::

import os

from bson.codec_options import CodecOptions
from bson import json_util

from pymongo import MongoClient
from pymongo.encryption import (Algorithm,
                                ClientEncryption)
from pymongo.encryption_options import AutoEncryptionOpts


def create_json_schema_file(kms_providers, key_vault_namespace,
                            key_vault_client):
    client_encryption = ClientEncryption(
        kms_providers,
        key_vault_namespace,
        key_vault_client,
        # The CodecOptions class used for encrypting and decrypting.
        # This should be the same CodecOptions instance you have configured
        # on MongoClient, Database, or Collection. We will not be calling
        # encrypt() or decrypt() in this example so we can use any
        # CodecOptions.
        CodecOptions())

    # Create a new data key and json schema for the encryptedField.
    # https://dochub.mongodb.org/core/client-side-field-level-encryption-automatic-encryption-rules
    data_key_id = client_encryption.create_data_key(
        'local', key_alt_names=['pymongo_encryption_example_1'])
    schema = {
        "properties": {
            "encryptedField": {
                "encrypt": {
                    "keyId": [data_key_id],
                    "bsonType": "string",
                    "algorithm":
                        Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic
                }
            }
        },
        "bsonType": "object"
    }
    # Use CANONICAL_JSON_OPTIONS so that other drivers and tools will be
    # able to parse the MongoDB extended JSON file.
    json_schema_string = json_util.dumps(
        schema, json_options=json_util.CANONICAL_JSON_OPTIONS)

    with open('jsonSchema.json', 'w') as file:
        file.write(json_schema_string)


def main():
    # The MongoDB namespace (db.collection) used to store the
    # encrypted documents in this example.
    encrypted_namespace = "test.coll"

    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # The MongoClient used to access the key vault (key_vault_namespace).
    key_vault_client = MongoClient()
    key_vault = key_vault_client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    key_vault.drop()
    key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {"$exists": True}})

    create_json_schema_file(
        kms_providers, key_vault_namespace, key_vault_client)

    # Load the JSON Schema and construct the local schema_map option.
    with open('jsonSchema.json', 'r') as file:
        json_schema_string = file.read()
    json_schema = json_util.loads(json_schema_string)
    schema_map = {encrypted_namespace: json_schema}

    auto_encryption_opts = AutoEncryptionOpts(
        kms_providers, key_vault_namespace, schema_map=schema_map)

    client = MongoClient(auto_encryption_opts=auto_encryption_opts)
    db_name, coll_name = encrypted_namespace.split(".", 1)
    coll = client[db_name][coll_name]
    # Clear old data
    coll.drop()

    coll.insert_one({"encryptedField": "123456789"})
    print('Decrypted document: %s' % (coll.find_one(),))
    unencrypted_coll = MongoClient()[db_name][coll_name]
    print('Encrypted document: %s' % (unencrypted_coll.find_one(),))


if __name__ == "__main__":
    main()

服务器端字段级加密强制

mongodb4.2服务器支持使用模式验证来强制对集合中的特定字段进行加密。此架构验证将阻止应用程序为标记为的任何字段插入未加密的值 "encrypt" JSON架构关键字。

下面的示例演示如何使用 ClientEncryption 创建新的加密数据密钥并使用 Automatic Encryption JSON Schema Syntax ::

import os

from bson.codec_options import CodecOptions
from bson.binary import STANDARD

from pymongo import MongoClient
from pymongo.encryption import (Algorithm,
                                ClientEncryption)
from pymongo.encryption_options import AutoEncryptionOpts
from pymongo.errors import OperationFailure
from pymongo.write_concern import WriteConcern


def main():
    # The MongoDB namespace (db.collection) used to store the
    # encrypted documents in this example.
    encrypted_namespace = "test.coll"

    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # The MongoClient used to access the key vault (key_vault_namespace).
    key_vault_client = MongoClient()
    key_vault = key_vault_client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    key_vault.drop()
    key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {"$exists": True}})

    client_encryption = ClientEncryption(
        kms_providers,
        key_vault_namespace,
        key_vault_client,
        # The CodecOptions class used for encrypting and decrypting.
        # This should be the same CodecOptions instance you have configured
        # on MongoClient, Database, or Collection. We will not be calling
        # encrypt() or decrypt() in this example so we can use any
        # CodecOptions.
        CodecOptions())

    # Create a new data key and json schema for the encryptedField.
    data_key_id = client_encryption.create_data_key(
        'local', key_alt_names=['pymongo_encryption_example_2'])
    json_schema = {
        "properties": {
            "encryptedField": {
                "encrypt": {
                    "keyId": [data_key_id],
                    "bsonType": "string",
                    "algorithm":
                        Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic
                }
            }
        },
        "bsonType": "object"
    }

    auto_encryption_opts = AutoEncryptionOpts(
        kms_providers, key_vault_namespace)
    client = MongoClient(auto_encryption_opts=auto_encryption_opts)
    db_name, coll_name = encrypted_namespace.split(".", 1)
    db = client[db_name]
    # Clear old data
    db.drop_collection(coll_name)
    # Create the collection with the encryption JSON Schema.
    db.create_collection(
        coll_name,
        # uuid_representation=STANDARD is required to ensure that any
        # UUIDs in the $jsonSchema document are encoded to BSON Binary
        # with the standard UUID subtype 4. This is only needed when
        # running the "create" collection command with an encryption
        # JSON Schema.
        codec_options=CodecOptions(uuid_representation=STANDARD),
        write_concern=WriteConcern(w="majority"),
        validator={"$jsonSchema": json_schema})
    coll = client[db_name][coll_name]

    coll.insert_one({"encryptedField": "123456789"})
    print('Decrypted document: %s' % (coll.find_one(),))
    unencrypted_coll = MongoClient()[db_name][coll_name]
    print('Encrypted document: %s' % (unencrypted_coll.find_one(),))
    try:
        unencrypted_coll.insert_one({"encryptedField": "123456789"})
    except OperationFailure as exc:
        print('Unencrypted insert failed: %s' % (exc.details,))


if __name__ == "__main__":
    main()

显式加密

显式加密是MongoDB社区功能,不使用 mongocryptd 过程。显式加密由 ClientEncryption 类,例如:

import os

from pymongo import MongoClient
from pymongo.encryption import (Algorithm,
                                ClientEncryption)


def main():
    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # The MongoClient used to read/write application data.
    client = MongoClient()
    coll = client.test.coll
    # Clear old data
    coll.drop()

    # Set up the key vault (key_vault_namespace) for this example.
    key_vault = client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    key_vault.drop()
    key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {"$exists": True}})

    client_encryption = ClientEncryption(
        kms_providers,
        key_vault_namespace,
        # The MongoClient to use for reading/writing to the key vault.
        # This can be the same MongoClient used by the main application.
        client,
        # The CodecOptions class used for encrypting and decrypting.
        # This should be the same CodecOptions instance you have configured
        # on MongoClient, Database, or Collection.
        coll.codec_options)

    # Create a new data key for the encryptedField.
    data_key_id = client_encryption.create_data_key(
        'local', key_alt_names=['pymongo_encryption_example_3'])

    # Explicitly encrypt a field:
    encrypted_field = client_encryption.encrypt(
        "123456789",
        Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
        key_id=data_key_id)
    coll.insert_one({"encryptedField": encrypted_field})
    doc = coll.find_one()
    print('Encrypted document: %s' % (doc,))

    # Explicitly decrypt the field:
    doc["encryptedField"] = client_encryption.decrypt(doc["encryptedField"])
    print('Decrypted document: %s' % (doc,))

    # Cleanup resources.
    client_encryption.close()
    client.close()


if __name__ == "__main__":
    main()

带自动解密的显式加密

虽然自动加密需要mongodb4.2enterprise或mongodb4.2atlas集群,但是 解密 所有用户都支持。配置自动 解密 无自动 加密 设置 bypass_auto_encryption=True 在里面 AutoEncryptionOpts ::

import os

from pymongo import MongoClient
from pymongo.encryption import (Algorithm,
                                ClientEncryption)
from pymongo.encryption_options import AutoEncryptionOpts


def main():
    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # bypass_auto_encryption=True disable automatic encryption but keeps
    # the automatic _decryption_ behavior. bypass_auto_encryption will
    # also disable spawning mongocryptd.
    auto_encryption_opts = AutoEncryptionOpts(
        kms_providers, key_vault_namespace, bypass_auto_encryption=True)

    client = MongoClient(auto_encryption_opts=auto_encryption_opts)
    coll = client.test.coll
    # Clear old data
    coll.drop()

    # Set up the key vault (key_vault_namespace) for this example.
    key_vault = client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    key_vault.drop()
    key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {"$exists": True}})

    client_encryption = ClientEncryption(
        kms_providers,
        key_vault_namespace,
        # The MongoClient to use for reading/writing to the key vault.
        # This can be the same MongoClient used by the main application.
        client,
        # The CodecOptions class used for encrypting and decrypting.
        # This should be the same CodecOptions instance you have configured
        # on MongoClient, Database, or Collection.
        coll.codec_options)

    # Create a new data key for the encryptedField.
    data_key_id = client_encryption.create_data_key(
        'local', key_alt_names=['pymongo_encryption_example_4'])

    # Explicitly encrypt a field:
    encrypted_field = client_encryption.encrypt(
        "123456789",
        Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
        key_alt_name='pymongo_encryption_example_4')
    coll.insert_one({"encryptedField": encrypted_field})
    # Automatically decrypts any encrypted fields.
    doc = coll.find_one()
    print('Decrypted document: %s' % (doc,))
    unencrypted_coll = MongoClient().test.coll
    print('Encrypted document: %s' % (unencrypted_coll.find_one(),))

    # Cleanup resources.
    client_encryption.close()
    client.close()


if __name__ == "__main__":
    main()