-
Notifications
You must be signed in to change notification settings - Fork 2k
feat(encryption): Add previous keys fallback feature #9853
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
base: 4.7
Are you sure you want to change the base?
Conversation
…decryption failed
app/Config/Encryption.php
Outdated
| * If you want to enable decryption using previous keys, set them here. | ||
| * See the user guide for more info. | ||
| */ | ||
| public array $previousKeys = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The encryption config can take in values from the .env file and having array properties can be hard to be populated. I suggest this takes a string of comma separated app keys instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do that. So, this would be changed to comma separated string, right? So, we will have to do like, convert hex2bin or base64_decode on the fly when needed, or maybe, convert all previous keys and implode them as comma separated keys?
Which approach would be more better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've currently set the second approach as for now (while initializing in the BaseConfig, do the parsing stuff and then implode as comma-separated string for future use), but we can use the other approach as well.
… fallback decryption
|
I don't know why this static analysis action is failing. The Thanks for help. |
michalsn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice start. Some thoughts: I think we should only fall back to the previous keys if the $params do not contain a key. If a key is provided in the $params, it suggests we are dealing with a custom encryption key, and the previous keys are meant to work only with the key from the config file, which would be overridden.
I would explore the option to wrap decrypt() methods content into a callback.
| $result = false; | ||
|
|
||
| try { | ||
| $result = $this->decryptWithKey($data, $this->key); | ||
| sodium_memzero($this->key); | ||
| } catch (EncryptionException $e) { | ||
| $exception = $e; | ||
| sodium_memzero($this->key); | ||
| } | ||
|
|
||
| if ($result === false && $this->previousKeys !== '') { | ||
| foreach (explode(',', $this->previousKeys) as $previousKey) { | ||
| try { | ||
| $result = $this->decryptWithKey($data, $previousKey); | ||
| if (isset($result)) { | ||
| return $result; | ||
| } | ||
| } catch (EncryptionException) { | ||
| // Try next key | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (isset($exception)) { | ||
| throw $exception; | ||
| } | ||
|
|
||
| return $result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this hard to read in its current form. Wrapping the logic in a callback method might make it clearer and easier to maintain, especially since the existing method logic won't change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to make it a more readable, please check now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of adding this method to the BaseHandler:
protected function tryDecryptWithFallback($data, #[SensitiveParameter] $params, callable $decryptCallback)
{
try {
return $decryptCallback($data, $params);
} catch (EncryptionException $e) {
if ($this->previousKeys === []) {
throw $e;
}
if (is_string($params) || (is_array($params) && isset($params['key']))) {
throw $e;
}
foreach ($this->previousKeys as $previousKey) {
try {
$previousParams = is_array($params)
? array_merge($params, ['key' => $previousKey])
: $previousKey;
return $decryptCallback($data, $previousParams);
} catch (EncryptionException) {
continue;
}
}
throw $e;
}
}and then using it like:
public function decrypt($data, #[SensitiveParameter] $params = null)
{
return $this->tryDecryptWithFallback($data, $params, function ($data, $params): string {
// original method content
});
}From my perspective, this is clearer, as the original methods remain largely unchanged and a single shared method handles both handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, adding it in BaseHandler.php was also my first thought, but I thought that anyone that has created their own encryption handler would have to implement this method after update.
So, will it be okay to introduce a new method in base handler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as long as it's universal and can be used with any handler.
|
One note: Just committed it to get reviews on the working and logic of that function. I think we can solve this by introducing method inside |
| } | ||
|
|
||
| // Only use fallback keys if no custom key was provided in params | ||
| $useFallback = ! isset($params['key']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A string value (is_string($params)) should also be treated as a key.
Description
This PR adds a new feature to the encryption service. Currently encryption service only supports single key decryption. This feature adds a feature to enable fallback keys which can be used if data can't be decrypted by the main key.
For more detailed use cases and motivation, please take a look at this forum post.
For equivalent Laravel implementation, please look at this page.
Note:
This PR is not complete yet. I need help in testing as well as user guide update according to this new change. I've done basic logic implementation and hence I am creating this PR to let others have a look at the change and suggest some changes. Testing and User guide updation is still remaining.
Checklist: