mongodb/mongo-ruby-driver

View on GitHub
docs/reference/in-use-encryption/queryable-encryption.txt

Summary

Maintainability
Test Coverage
.. _queryable-encryption:

**********************
Queryable Encryption
**********************

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 1
   :class: singlecol

Queryable encryption is a new feature in MongoDB 6.0. It also requires
libmongocrypt version 1.5.2 or above.

You can find more information about queryable encryption in `MongoDB Manual
<https://www.mongodb.com/docs/upcoming/core/queryable-encryption/queryable-encryption/>`_.

.. note::

  The queryable encryption feature is in public technical preview.
  Therefore, the following options should be considered experimental
  and are subject to change:

  - ``:encrypted_fields_map`` and ``:bypass_query_analysis`` in auto encryption options.
  - ``:contention_factor`` and ``:query_type`` in client encryption options.

The following examples assume you are familiar with the concepts and techniques
described in :ref:`Client-Side Encryption <client-side-encryption>`.

Below is an example of using automatic queryable encryption using the Ruby driver:

.. code-block:: ruby

  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
  # that will be used to store your data keys.
  key_vault_client = Mongo::Client.new('mongodb://localhost:27017,localhost:27018')

  # 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 an encrypted fields map, which tells the Mongo::Client which fields to encrypt.
  encrypted_fields_map = {
      'encryption_db.encryption_coll' => {
        fields: [
          {
            path: 'encrypted_field',
            bsonType: 'string',
            keyId: data_key_id,
            queries: {
              queryType: 'equality'
            }
          }
        ]
      }
    }

  # Configure the client for automatic encryption
  client = Mongo::Client.new(
    'mongodb://localhost:27017,localhost:27018',
    auto_encryption_options: {
      key_vault_namespace: 'encryption.__keyVault',
      kms_providers: kms_providers,
      encrypted_fields_map: encrypted_fields_map,
    },
    database: 'encryption_db'
  )

  # Make sure there is no data in the collection.
  client.database.drop

  # Create encrypted collection explicitly.
  collection = client['encryption_coll'].create

  # 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...>

The example above demonstrates using automatic encryption with a local master key.
For more information about using other key management services to create a
master key and create data keys, see the following sections of the :ref:`Client-Side Encryption <client-side-encryption>` tutorial:

- :ref:`Creating A Master Key <creating-a-master-key>`
- :ref:`Creating A Data Key <creating-a-data-key>`

Below is an example of explicit queryable encryption.

.. code-block:: ruby

  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
  # that will be used to store your data keys.
  key_vault_client = Mongo::Client.new('mongodb://localhost:27017,localhost:27018')

  # 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: Create an encrypted collection #
  ##########################################
  encrypted_fields = {
    fields: [
      {
        path: 'encrypted_field',
        bsonType: 'string',
        keyId: data_key_id,
        queries: {
          queryType: 'equality',
          contention: 0
        }
      }
    ]
  }

  # Create the client you will use to read and write the data to MongoDB
  # Please note that to insert or query with an "Indexed" encrypted payload,
  # you should use a ``Mongo::Client`` that is configured with ``:auto_encryption_options``.
  # ``auto_encryption_options[:bypass_query_analysis]`` may be true.
  # ``auto_encryption_options[:bypass_auto_encryption]`` must be not set or false.
  client = Mongo::Client.new(
    ['localhost:27017'],
    auto_encryption_options: {
      key_vault_namespace: 'encryption.__keyVault',
      kms_providers: kms_providers,
      bypass_query_analysis: true,
    },
    database: 'encryption_db',
  )

  # Make sure there is no data in the collection.
  client['encryption_coll'].drop(encrypted_fields: encrypted_fields)
  # Create encrypted collection explicitly.
  client['encryption_coll'].create(encrypted_fields: encrypted_fields)

  #####################################################
  # Step 4: Encrypt a string with explicit encryption #
  #####################################################

  # The value to encrypt
  value = 'sensitive data'

  # Encrypt the value
  insert_payload = client_encryption.encrypt(
    'sensitive data',
    {
      key_id: data_key_id,
      algorithm: "Indexed",
      contention_factor: 0
    }
  )

  # Insert the encrypted value into the collection
  client['encryption_coll'].insert_one(encrypted_field: insert_payload)

  # Use the client to read the encrypted value from the database, then
  # use the ClientEncryption object to decrypt it.
  find_payload = client_encryption.encrypt(
    'sensitive data',
    {
      key_id: data_key_id,
      algorithm: "Indexed",
      contention_factor: 0,
      query_type: "equality"
    }
  )

  find_result = client['encryption_coll'].find(encrypted_field: find_payload).first['encrypted_field']
  # => 'sensitive data'