Empty keys (e.g. from environment variable when not set in the current environment) shouldn't break entire site in RealAESEncryptionMethod->decrypt()

Created on 4 March 2024, about 1 year ago

Hello all, this is possibly related to #3082238: Don't fail config import if key source is not there .

Problem/Motivation

When using Real AES to provide a key for the two-factor authentication module, and the key is provided by an environment variable, it is typical for the production environment to have the key set, but for all other environments to not use that key.

The TFA module allows disabling of TFA via config-set or settings.php (e.g. $config['tfa.settings']['enabled'] = 0;).

However, even when the module is bypassed - e.g. on a non-production environment - the Real AES module is being asked to decrypt this key, which has a zero length.

This leads to a 500 exception on every page which tries to load this key.

A more-graceful solution would be to return FALSE when being asked to decrypt an empty key, instead of throwing a currently-opaque error (although I gather this is improved in 🐛 Exception difficult to debug without message Fixed .

Steps to reproduce

Follow setup steps to configure an environment variable which uses Real AES, e.g. by adding the following to settings.php:

putenv('REDACTIVE_2FA_KEY=<some valid key>');

Then comment out the line to remove the environment variable, noting that pages which call RealAESEncryptionMethod->decrypt() then fail to load.

Proposed resolution

Patch attached.

Remaining tasks

Sanity check, code review, test.

This may not be the best approach - I'm very much not an expert on encryption and this approach may well introduce other strange regressions.

That said, this patch works for our workflow and fixes our issues, so am sharing here in case anyone else finds it useful :)

/A

🐛 Bug report
Status

Active

Version

2.0

Component

Code

Created by

🇬🇧United Kingdom alexharries

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @alexharries
  • Status changed to Needs review about 1 year ago
  • 🇬🇧United Kingdom alexharries

    Optimistically moving to "needs review"... ¯\_(ツ)_/¯

  • 🇬🇧United Kingdom alexharries

    Commit added to also handle empty keys when trying to _encrypt_, too.

  • Status changed to Needs work about 1 year ago
  • 🇵🇹Portugal jcnventura

    It can't return FALSE, as the only acceptable return type is a string. Or the exception. Please make it return an empty string, and then test if that is still a solution that would work.

  • Status changed to Closed: works as designed 6 months ago
  • 🇯🇵Japan ptmkenny

    This is the documentation for EncryptionMethodInterface from the Encrypt module:

      /**
       * Decrypt text.
       *
       * @param string $text
       *   The text to be decrypted.
       * @param string $key
       *   The key to decrypt the text with.
       *
       * @return string
       *   The decrypted text
       *
       * @throws \Drupal\encrypt\Exception\EncryptException
       *   Thrown when decryption fails.
       * @throws \Drupal\encrypt\Exception\EncryptionMethodCanNotDecryptException
       *   The method should throw this exception when the plugin can not decrypt
       *   (i.e. use a public key).
       */
      public function decrypt($text, $key);
    

    The Real AES module implements this interface, and the behavior as currently implemented is correct according to the interface. So if you want to change this behavior, the Encrypt interface needs to be modified upstream in the Encrypt module. (There are a few issues related to this topic, so changing the interface is worth discussing.)

    If you still want to pursue this, please open an issue in the Encrypt module issue queue; the interface will have to be changed in order to change this module's behavior.

  • 🇵🇹Portugal jcnventura

    At one point, that interface did allow FALSE to be returned, and that created some issues, as there is really no circumstance where that should happen on the call to decrypt(). It either decrypts and the decrypted string is returned or it doesn't decrypt, and an exception should be raised. It was found that sometimes developers wanted a "soft" exception and returned FALSE, and other times threw the exception.

    However, calling decrypt("") is really an edge case here, as it might actually be that the correct answer to that is to return back the empty string. That was what I suggested in #4.

    Ideally, the tfa module that @alexharries referred to in the issue summary should be modified to somehow handle the exception or simply prevent calling the function with an empty string, which I believe is done (see https://git.drupalcode.org/project/tfa/-/blame/8.x-1.x/src/Plugin/TfaVal...).

  • 🇯🇵Japan ptmkenny

    Thank you for the additional information; that's very helpful to understand the context here.

    If it's been fixed upstream, all the more reason to leave this closed.

    I appreciate you sharing this because I've been trying to clean up the Encrypt ecosystem issue queues, so understanding more of the background is useful.

Production build 0.71.5 2024