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

fix(Security): Don't attempt empty secret fallback on >=v3 ciphertext #46176

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions lib/private/Security/Crypto.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,25 @@ public function decrypt(string $authenticatedCiphertext, string $password = ''):
}
return $this->decryptWithoutSecret($authenticatedCiphertext, $password);
} catch (Exception $e) {
// If password is empty and the current secret didn't work, attempt with an empty secret as a fallback
// for instances where the secret might not have been set for a time *IF* the ciphertext version
// indicates an empty secret is a possibility. For example, it's pointless (and will fail hard if tried)
// to try the fallback with v3 versioned ciphertext since the keying material can't be empty with that version.
// SO: Only attempt the fallback on older versions (including non-versioned) ciphertexts
if ($password === '') {
// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
return $this->decryptWithoutSecret($authenticatedCiphertext, '');
// Determine the crypto version, if any
$parts = explode('|', $authenticatedCiphertext);
$partCount = \count($parts);
if ($partCount < 3 || $partCount > 4) {
throw new Exception('Authenticated ciphertext could not be decoded (invalid format).');
}
if ($partCount === 4) { // only newer ciphertext has a version field
$version = $parts[3];
}

if ((!empty($version) && $version <= '2') || empty($version)) { // only <3 versioned or old non-versioned ciphertext ever supported empty secrets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ((!empty($version) && $version <= '2') || empty($version)) { // only <3 versioned or old non-versioned ciphertext ever supported empty secrets
if (empty($version) || $version <= '2') { // only <3 versioned or old non-versioned ciphertext ever supported empty secrets

return $this->decryptWithoutSecret($authenticatedCiphertext, '');
}
}
throw $e;
}
Expand All @@ -110,9 +126,6 @@ private function decryptWithoutSecret(string $authenticatedCiphertext, string $p

$parts = explode('|', $authenticatedCiphertext);
$partCount = \count($parts);
if ($partCount < 3 || $partCount > 4) {
throw new Exception('Authenticated ciphertext could not be decoded.');
}
Comment on lines -113 to -115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this removed?
Should be added back.


$ciphertext = $this->hex2bin($parts[0]);
$iv = $parts[1];
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/Security/CryptoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function testWrongIV() {

public function testWrongParameters() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Authenticated ciphertext could not be decoded.');
$this->expectExceptionMessage('Authenticated ciphertext could not be decoded (invalid format).');

$encryptedString = '1|2';
$this->crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd');
Expand Down
Loading