genecommerce / module-encryption-key-manager

Tooling to help generate and invalidate magento encryption keys
GNU Lesser General Public License v3.0
54 stars 14 forks source link

Invalidate Question #22

Closed andrew-ebsco closed 1 month ago

andrew-ebsco commented 1 month ago

Hello Gene Commerce Team,

Thank you very much for the module. It has been very helpful for rotating encryption keys and re-encrypting values in our database. However, I have a question about the invalidate key process. What is the functional advantage of placing the old crypt key in the 'invalidate_key' section? Basically, I want to know what would be the downside of just leaving the old crypt key in the crypt/key config. If all the encrypted values have been upgraded to the new key, then the old one will not be used anyway. It is unclear from the documentation why invalidating the key is an improvement. I appreciate any feedback or clarification.

Thanks!

Andrew

convenient commented 1 month ago

Hello @andrew-ebsco

On the surface of it that makes sense. If you have successfully migrated every encrypted pieces of data in your system to use the new keys at the higher index, then essentially nothing should be looking at the old key index and leaving it in place shouldn't hurt.

However that is an assumption that is hard to verify, and this is actually about more than just the data you have encrypted in your system.

The hotfix release today (which achieves very much the same thing as this module did) looks like so

diff --git a/vendor/magento/module-jwt-user-token/Model/SecretBasedJwksFactory.php b/vendor/magento/module-jwt-user-token/Model/SecretBasedJwksFactory.php
--- a/vendor/magento/module-jwt-user-token/Model/SecretBasedJwksFactory.php (revision 022e64b08a88658667bc2d6b922eada2b7910965)
+++ b/vendor/magento/module-jwt-user-token/Model/SecretBasedJwksFactory.php (revision 8d2b0c1c6b421cdcd7f62a48a5edc9b0211d92a2)
@@ -35,6 +35,7 @@
     public function __construct(DeploymentConfig $deploymentConfig, JwkFactory $jwkFactory)
     {
         $this->keys = preg_split('/\s+/s', trim((string)$deploymentConfig->get('crypt/key')));
+        $this->keys = [end($this->keys)];
         //Making sure keys are large enough.
         foreach ($this->keys as &$key) {
             $key = str_pad($key, 2048, '&', STR_PAD_BOTH);

In this scenario it wasn't just encrypted values that were using the old key, but other parts of the system were using the encryption keys from env.php to initialise other logic.

This SecretBasedJwksFactory.php has been patched now, and should help secure against cosmicsting however that does not account for

The only 100% verifiable way to properly verify the old key is no longer in use is to remove it from the crypt/key section. This module moves it to crypt/invalidated_key as both a reference point, and so that the media gallery can still be generated based on the original values.

The process of invalidating your key does sound scary however consider the following

At this stage we must basically assume our keys are public and that bad actors will be trying to find things to do with them. If the key is still active and plugged into your system as part of crypt/key, then there are chances that some code may pick it up and execute it in manners that are not desirable. I would not assume that we're safe because we blocked the current known attack vector, and would rather have a repeatable process to actually rotate encryption keys which includes invalidating the old one.

andrew-ebsco commented 1 month ago

Hey @convenient,

Thank you for the timely response! I understand now that invalidating the old key is a precaution against other parts of the system using it. In the scenario you mentioned, is it possible to remove the replacement key that is added in crypt/key as part of the invalidation process? If there is a chance Magento will use the key at index 0, I would rather it not use the placeholder key like geneinvalidatedkeys669a7e7ac5d3d and just use the new encryption key. Could I run a query to update all database encrypted values from 1:3:xyz456 to 0:3:xyz456 and the only have one key under crypt/key?

Thanks again for putting so much effort into this.

Andrew

convenient commented 1 month ago

Hello @andrew-ebsco

I would rather it not use the placeholder key like geneinvalidatedkeys669a7e7ac5d3d

If you have worked through the updating of your data correctly, and enabled the logger to verify, then nothing will ever actually use the placeholder value. It is only there to ensure the index is kept at 1 for the newly generated key.

Taking this approach allows you to

This module is coded to spot geneinvalidatedkeys so that it knows what to move along into the crypt/invalidated_key section the next time you have a key to rotate, but i guess if you really don't want to have that string in your env.php then you could simply duplicate your newly generated key at index 0 and index 1 manually. That would maintain the positioning and avoid any stubbed in data. But again, if nothing is looking at index 0 then it won't actually be being used anyway.

To me it seems like it would be a lot of effort to generate a new key, get all your data happy at 1:3: and then convert them all again to being 0:3: considering magento expects each new key to have a new index 🤷

convenient commented 1 month ago

Thinking out loud, I could probably make the invalidate process better by using a shorter prefix (for matching against, a few chars) and using the same process to generate the remainder of the key.

That would make them not as predictable and maintain the security. Would that be enough do you think?

andrew-ebsco commented 1 month ago

Hey @convenient,

I think that would be a good solution. Maybe you could use random_bytes instead of uniqid. That way on the off chance Magento uses the key at index 0, its at least not something as guessable. Thanks for listening!

Andrew

convenient commented 1 month ago

Hello @andrew-ebsco can you please take a look at #23 ? Thanks for talking through this with me, this is a good addition I hadn't really considered.

With this change we can let magento operate how it expects to as far as the key indexing goes, while still properly removing the old keys so they're revoked.

andrew-ebsco commented 1 month ago

@convenient, that was quick! That PR looks good to me.

convenient commented 1 month ago

@andrew-ebsco Thanks for the questions and feedback here, got some real good output of this I think.

https://github.com/genecommerce/module-encryption-key-manager/releases/tag/v0.0.8-alpha

andrew-ebsco commented 1 month ago

@convenient, I agree. I'm installing the new version now. Thanks!

convenient commented 1 month ago

@andrew-ebsco do reach out if you have any other ideas 💡