Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(dev/core#2258) Add API+hook to rotate keys for encrypted fields #19251

Merged
merged 2 commits into from
Jan 5, 2021

Conversation

totten
Copy link
Member

@totten totten commented Dec 21, 2020

Overview

Key rotation is the process of phasing-out an old cryptographic-key and phasing-in a new one. This patchset introduces support for rotating the key used for encrypted values in the database.

Many security practitioners regard key-rotation as best-practice because it:

  • Limits the risk/damage of leaking a key. (Ex: If a backup file leaks to the web, or if an old contractor forgets to delete work notes, or if a sysadmin's workstation gets malware, then an old key could leak. But if you rotate keys periodically, then leaking the old key doesn't pose much risk.)
  • Limits the feasibility of brute-force attacks. (When a key is rotated, the attacker has to start over.)

See also: https://lab.civicrm.org/dev/core/-/issues/2258

(NOTE: This builds on #19236, and it is a step towards #19239.)

Before

#19236 was a partial step towards key-rotation. Specifically, it allowed one to define multiple keys. For example, suppose civicrm.settings.php starts with the configuration:

define('CIVICRM_CRED_KEYS', '::aaaa');

You could add a higher-priority key in the list:

define('CIVICRM_CRED_KEYS', '::bbbb ::aaaa');

Any new credential-content will be encrypted with ::bbbb. However, existing content still uses ::aaaa. There is no defined mechanism for re-encrypting values with ::bbbb.

After

The System.rotateKey API can migrate data from the old ::aaaa to the new ::bbbb, e.g.

cv api4 System.rotateKey tag=CRED

This fires hook_civicrm_cryptoRotateKey('CRED', $log) to ensure that any data based on CRED is re-encrypted with the current key (::bbbb).

Comments

Why is there a hook? Consider that there are several bits of content that should be plausibly re-encryped, e.g.

  1. Within civicrm_settings, and within the record for mailing_backend, there is a subfield for smtpPassword which should be encryptable/rotateable.
  2. Within civicrm_settings, there are some values (likely defined in extensions) which should be encryptable/rotateable.
  3. Within civicrm_payment_processor, the password should be encryptable/rotateable.

These are 3 different data-structures, and there is no singular SQL statement to touch all of them. We have to define multiple re-encryption steps (and some of them may be extension-dependent). Using a hook provides a flexible starting point.

Here's pseudocode for a hook-implementation:

/**
 * Ensure that `some_table`.`secret_column` is encrypted with the `CRED` key.
 * Use the method `CryptoToken::rekey(...)` to re-encrypt specific values.
 */
function example_civicrm_cryptoRotateKey($tag, $log) {
  if ($tag !== 'CRED') return;
  
  $cryptoToken = Civi::service('crypto.token');

  $rows = sql('SELECT id, secret_column FROM some_table');
  foreach ($rows as $row) {
    $new = $cryptoToken->rekey($row['secret_column'], 'CRED');
    if ($new !== NULL) {
      sql('UPDATE some_table SET secret_column = %1 WHERE id = %2', $new, $row['id']);
      $log->info("Updated secret_column for #{id}", ['id' => $row['id']]);
    }
  }
}

For a working example, see #19239 which addresses the civicrm_setting.mailing_backend.smtpPassword.

@civibot
Copy link

civibot bot commented Dec 21, 2020

(Standard links)

@seamuslee001
Copy link
Contributor

@totten can you rebase this please

@totten
Copy link
Member Author

totten commented Dec 30, 2020

@seamuslee001 Rebased and did some more copy-editing on the PR description.

@seamuslee001
Copy link
Contributor

This seems fine to me merging

@seamuslee001 seamuslee001 merged commit 05ec6db into civicrm:master Jan 5, 2021
@totten totten deleted the master-crypt-rekey branch January 5, 2021 03:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants