PluginProbe
Auto Alt Text / 2.5.2
Auto Alt Text v2.5.2
3.0.3 2.8.2 1.3.1 1.3.2 2.0.0 2.1.0 2.1.1 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.7.0 2.8.0 2.8.1 All 28 releases
← All changes | src/App/Utilities/Encryption.php +80 -25 2.3.12.5.2 View file →
@@ -1,8 +1,9 @@
1 1 <?php
2 2
3 3 namespace AATXT\App\Utilities;
4 4
5 +use AATXT\Config\Constants;
5 6 use RuntimeException;
6 7
7 8 final class Encryption
8 9 {
@@ -48,44 +49,63 @@
48 49 return base64_encode($iv . $raw_value);
49 50 }
50 51
51 52 /**
53 + * Attempts to decrypt a raw value using the provided key and salt.
54 + **/
55 + private function attemptDecrypt(string $rawValue, string $key, string $salt): string
56 + {
57 + $decoded = base64_decode($rawValue, true);
58 + $method = 'aes-256-ctr';
59 + $ivLength = openssl_cipher_iv_length($method);
60 + $iv = substr($decoded, 0, $ivLength);
61 + $cipher = substr($decoded, $ivLength);
62 + $decrypted = openssl_decrypt($cipher, $method, $key, 0, $iv);
63 +
64 + // If decryption fails or the salt is not present, return an empty string
65 + if (! $decrypted || substr($decrypted, -strlen($salt)) !== $salt) {
66 + return '';
67 + }
68 + return substr($decrypted, 0, -strlen($salt));
69 + }
70 +
71 + /**
72 + * Decrypts a raw value trying with the plugin key and salt first, then falling back to the logged-in key and salt.
52 73 * @param string $rawValue
53 - * @return string|bool
74 + * @return string
54 75 */
55 76 public function decrypt(string $rawValue): string
56 77 {
57 - if (empty($rawValue)) {
78 + if (empty($rawValue) || ! extension_loaded('openssl')) {
58 79 return '';
59 80 }
60 81
61 - /** @noinspection DuplicatedCode */
62 - if (!extension_loaded('openssl')) {
63 - return $rawValue;
64 - }
82 + try {
83 + $plain = $this->attemptDecrypt($rawValue, $this->key, $this->salt);
84 + if ($plain !== '') {
85 + return $plain;
86 + }
65 87
66 - $rawValue = base64_decode($rawValue, true);
88 + // If the decryption with the plugin key fails, try as fallback with the logged-in key and salt
89 + if (defined('LOGGED_IN_KEY') && defined('LOGGED_IN_SALT')) {
90 + $plainOld = $this->attemptDecrypt($rawValue, LOGGED_IN_KEY, LOGGED_IN_SALT);
91 + if ($plainOld !== '') {
92 + return $plainOld;
93 + }
94 + }
67 95
68 - $method = 'aes-256-ctr';
69 - $ivLength = openssl_cipher_iv_length($method);
70 - $iv = substr($rawValue, 0, $ivLength);
71 -
72 - $rawValue = substr($rawValue, $ivLength);
73 -
74 - $value = openssl_decrypt($rawValue, $method, $this->key, 0, $iv);
75 -
76 - if (!$value || substr($value, -strlen($this->salt)) !== $this->salt) {
77 - throw new RuntimeException('Encryption failed.');
96 + return '';
97 + } catch (\Throwable $e) {
98 + throw new RuntimeException('Decryption failed.');
78 99 }
79 -
80 - return substr($value, 0, -strlen($this->salt));
81 100 }
82 101
83 - /**
84 - * Get key from WordPress Authentication Unique Keys and Salts
85 - */
86 102 private function getKey(): string
87 103 {
104 + if ( defined( 'AATXT_ENCRYPTION_KEY' ) && '' !== AATXT_ENCRYPTION_KEY ) {
105 + return AATXT_ENCRYPTION_KEY;
106 + }
107 + // If the constant is not defined, use the WordPress constants LOGGED_IN_KEY and LOGGED_IN_SALT
88 108 if (defined('LOGGED_IN_KEY') && '' !== LOGGED_IN_KEY) {
89 109 return LOGGED_IN_KEY;
90 110 }
91 111
@@ -92,13 +112,14 @@
92 112 // If this is reached, you're either not on a live site or have a serious security issue.
93 113 return 'warning-not-logged-in-key-constant-defined';
94 114 }
95 115
96 - /**
97 - * Get salt from WordPress Authentication Unique Keys and Salts
98 - */
99 116 public function getSalt(): string
100 117 {
118 + if ( defined( 'AATXT_ENCRYPTION_SALT' ) && '' !== AATXT_ENCRYPTION_SALT ) {
119 + return AATXT_ENCRYPTION_SALT;
120 + }
121 + // If the constant is not defined, use the WordPress constants LOGGED_IN_KEY and LOGGED_IN_SALT
101 122 if (defined('LOGGED_IN_SALT') && '' !== LOGGED_IN_SALT) {
102 123 return LOGGED_IN_SALT;
103 124 }
104 125
@@ -103,6 +124,40 @@
103 124 }
104 125
105 126 // If this is reached, you're either not on a live site or have a serious security issue.
106 127 return 'warning-not-logged-in-salt-constant-defined';
128 + }
129 +
130 + /**
131 + * Migrate legacy API keys from the old encryption method to the new one.
132 + * This is necessary for backward compatibility with older versions of the plugin.
133 + */
134 + public function migrateLegacyApiKeys(): void
135 + {
136 + $fields = [
137 + Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI,
138 + Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION,
139 + Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE,
140 + ];
141 +
142 + foreach ($fields as $optionName) {
143 + $raw = get_option($optionName);
144 + if (empty($raw)) {
145 + continue;
146 + }
147 +
148 + if (! defined('LOGGED_IN_KEY') || ! defined('LOGGED_IN_SALT')) {
149 + continue;
150 + }
151 + // Try to decrypt options using the old method with the WordPress constants LOGGED_IN_KEY and LOGGED_IN_SALT
152 + $plain = $this->attemptDecrypt($raw, LOGGED_IN_KEY, LOGGED_IN_SALT);
153 +
154 + if ($plain === '') {
155 + // Decryption failed, cause the option is encrypted with the new method or the old method failed
156 + continue;
157 + }
158 +
159 + //Resave the option as plain because the action "encryptDataOnUpdate" will encrypt before saving in the database
160 + update_option($optionName, $plain);
161 + }
107 162 }
108 163 }