PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Security / DataEncryption.php
wp-staging / Framework / Security Last commit date
Otp 1 day ago AccessToken.php 1 day ago Auth.php 1 day ago Capabilities.php 1 day ago DataEncryption.php 1 day ago EncryptionNoticeService.php 1 day ago Nonce.php 1 day ago UniqueIdentifier.php 1 day ago
DataEncryption.php
417 lines
1 <?php
2
3 namespace WPStaging\Framework\Security;
4
5 use WPStaging\Framework\Facades\Hooks;
6 use WPStaging\Vendor\phpseclib3\Crypt\RSA;
7 use WPStaging\Vendor\phpseclib3\Crypt\PublicKeyLoader;
8
9 use function WPStaging\functions\debug_log;
10
11
12
13
14
15
16
17
18 class DataEncryption
19 {
20
21 const FILTER_FRAMEWORK_SECURITY_DATA_ENCRYPTION_USE_SSL = 'wpstg.framework.security.dataEncryption.useSsl';
22
23
24 private $hasSsl;
25
26
27 private $prefix;
28
29
30 private $key;
31
32
33 private $salt;
34
35 public function __construct()
36 {
37 if (!Hooks::applyFilters(self::FILTER_FRAMEWORK_SECURITY_DATA_ENCRYPTION_USE_SSL, true)) {
38 $this->hasSsl = false;
39 } else {
40 $this->hasSsl = extension_loaded('openssl') && function_exists('openssl_encrypt') && function_exists('openssl_decrypt') && (bool)in_array('aes-256-ctr', openssl_get_cipher_methods());
41 }
42
43 $this->prefix = '!wpstg!';
44 $this->key = $this->getDefaultKey();
45 $this->salt = $this->getDefaultSalt();
46 }
47
48 public function isPhpSecLibAvailable(): bool
49 {
50 return class_exists(\WPStaging\Vendor\phpseclib3\Crypt\PublicKeyLoader::class);
51 }
52
53
54
55
56
57 public function encrypt($value): string
58 {
59 if ($this->hasSsl) {
60 return $this->sslEncrypt($value);
61 }
62
63 return $this->base64Encrypt($value);
64 }
65
66
67
68
69
70 public function decrypt(string $value): string
71 {
72 if ($this->verifyPrefix($value, 'ssl')) {
73 return $this->sslDecrypt($value);
74 }
75
76 return $this->base64Decrypt($value);
77 }
78
79
80
81
82
83 protected function base64Encrypt($value): string
84 {
85 if (!$this->isValidKeySalt() || $value === '' || $this->isEncrypted($value)) {
86 return (string)$value;
87 }
88
89 $mykey = $this->key . $this->salt;
90 $encpad = substr($mykey, 0, 12);
91 $value = $encpad . $value;
92
93 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
94 $pad = base64_decode($mykey);
95 $valueLen = strlen($value);
96 $encrypted = '';
97 $x = 0;
98 for ($i = 0; $i < $valueLen; $i++, $x++) {
99 if (!isset($pad[$x])) {
100 $x = 0;
101 }
102
103 $padi = $pad[$x];
104 $encrypted .= chr(ord($value[$i]) ^ ord($padi));
105 }
106
107 return $this->setPrefixType('b64') . $this->normalizeBase64Encode($encrypted);
108 }
109
110
111
112
113
114 protected function base64Decrypt(string $inputValue): string
115 {
116 if (!$this->isValidKeySalt() || !is_string($inputValue) || $inputValue === '' || !$this->isEncrypted($inputValue) || !$this->verifyPrefix($inputValue, 'b64')) {
117 return $inputValue;
118 }
119
120 $value = $this->stripPrefix($inputValue, 'b64');
121 $mykey = $this->key . $this->salt;
122
123 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
124 $pad = base64_decode($mykey);
125 $encrypted = $this->normalizeBase64Decode($value);
126 $encryptedLen = strlen($encrypted);
127 $decrypted = '';
128 $x = 0;
129 for ($i = 0; $i < $encryptedLen; $i++, $x++) {
130 if (!isset($pad[$x])) {
131 $x = 0;
132 }
133
134 $padi = $pad[$x];
135 $decrypted .= chr(ord($encrypted[$i]) ^ ord($padi));
136 }
137
138 $encpad = substr($mykey, 0, 12);
139 $enclen = strlen($encpad);
140 $envpad = substr($decrypted, 0, $enclen);
141
142 if ($encpad !== $envpad) {
143 return $inputValue;
144 }
145
146 $decrypted = substr($decrypted, $enclen);
147 return $decrypted;
148 }
149
150
151
152
153
154 protected function sslEncrypt($value): string
155 {
156 if (!$this->isValidKeySalt() || $value === '' || !$this->hasSsl || $this->isEncrypted($value)) {
157 return (string)$value;
158 }
159
160 $method = 'aes-256-ctr';
161 $ivlen = openssl_cipher_iv_length($method);
162 $iv = openssl_random_pseudo_bytes($ivlen);
163
164 $rawValue = openssl_encrypt($value . $this->salt, $method, $this->key, 0, $iv);
165 if (!$rawValue) {
166 return (string)$value;
167 }
168
169 return $this->setPrefixType('ssl') . $this->normalizeBase64Encode($iv . $rawValue);
170 }
171
172
173
174
175
176 protected function sslDecrypt(string $inputValue): string
177 {
178 if (!$this->isValidKeySalt() || !is_string($inputValue) || $inputValue === '' || !$this->hasSsl || !$this->isEncrypted($inputValue) || !$this->verifyPrefix($inputValue, 'ssl')) {
179 return $inputValue;
180 }
181
182 $rawValue = $this->stripPrefix($inputValue, 'ssl');
183 $rawValue = $this->normalizeBase64Decode($rawValue);
184
185 $method = 'aes-256-ctr';
186 $ivlen = openssl_cipher_iv_length($method);
187 $iv = substr($rawValue, 0, $ivlen);
188
189 $rawValue = substr($rawValue, $ivlen);
190
191 $value = openssl_decrypt($rawValue, $method, $this->key, 0, $iv);
192 if (!$value || $this->salt !== substr($value, - strlen($this->salt))) {
193 return $inputValue;
194 }
195
196 return substr($value, 0, - strlen($this->salt));
197 }
198
199
200 protected function disableUseSssl()
201 {
202 $this->hasSsl = false;
203 return true;
204 }
205
206
207
208
209
210 public function setKey(string $value): string
211 {
212 $this->key = (string)$value;
213 return $this->key;
214 }
215
216
217
218
219
220 public function setSalt(string $value): string
221 {
222 $this->salt = (string)$value;
223 return $this->salt;
224 }
225
226
227 public function getKey(): string
228 {
229 return $this->key;
230 }
231
232
233 public function getSalt(): string
234 {
235 return $this->salt;
236 }
237
238
239
240
241
242
243 protected function verifyPrefix(string $value, string $type): bool
244 {
245 $type = '!' . $type . '!';
246 return substr($value, 0, strlen($this->prefix . $type)) === $this->prefix . $type;
247 }
248
249
250
251
252
253 public function isEncrypted(string $value): bool
254 {
255 return preg_match('@^' . preg_quote($this->prefix, '@') . '!(b64|ssl|rsa)!([a-zA-Z0-9\-_]+)$@', $value);
256 }
257
258
259 protected function isValidKeySalt(): bool
260 {
261 $key = $this->getKey();
262 $salt = $this->getSalt();
263 return (!empty($key) && is_string($key)) && (!empty($salt) && is_string($salt));
264 }
265
266
267 protected function getPrefix(): string
268 {
269 return $this->prefix;
270 }
271
272
273
274
275
276 protected function setPrefix(string $value): string
277 {
278 $this->prefix = $value;
279 return $this->prefix;
280 }
281
282
283 private function getDefaultKey()
284 {
285 if (defined('WPSTG_ENCRYPTION_KEY') && !empty(WPSTG_ENCRYPTION_KEY) && is_string(WPSTG_ENCRYPTION_KEY)) {
286 return WPSTG_ENCRYPTION_KEY;
287 }
288
289 if (defined('AUTH_KEY') && !empty(AUTH_KEY) && is_string(AUTH_KEY)) {
290 return AUTH_KEY;
291 }
292
293 return false;
294 }
295
296
297 private function getDefaultSalt()
298 {
299 if (defined('WPSTG_ENCRYPTION_SALT') && !empty(WPSTG_ENCRYPTION_SALT) && is_string(WPSTG_ENCRYPTION_SALT)) {
300 return WPSTG_ENCRYPTION_SALT;
301 }
302
303 if (defined('AUTH_SALT') && !empty(AUTH_SALT) && is_string(AUTH_SALT)) {
304 return AUTH_SALT;
305 }
306
307 return false;
308 }
309
310
311
312
313
314
315 private function stripPrefix(string $value, string $type): string
316 {
317 $type = '!' . $type . '!';
318 return substr($value, strlen($this->prefix . $type));
319 }
320
321
322
323
324
325 private function setPrefixType(string $type): string
326 {
327 return $this->prefix . '!' . $type . '!';
328 }
329
330
331
332
333
334 private function normalizeBase64Encode(string $value): string
335 {
336 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
337 return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($value));
338 }
339
340
341
342
343
344 private function normalizeBase64Decode(string $value)
345 {
346 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
347 return base64_decode(str_replace(['-', '_'], ['+', '/'], $value));
348 }
349
350 public function getPublicKey(): string
351 {
352 $keyPath = WPSTG_RESOURCES_DIR . '/wpstg-public-key.pem';
353
354 if (!file_exists($keyPath)) {
355 return '';
356 }
357
358 $keyData = file_get_contents($keyPath);
359 return $keyData;
360 }
361
362 public function generateKeys(): array
363 {
364 $privateKey = RSA::createKey();
365 $publicKey = $privateKey->getPublicKey();
366
367 return [
368 'publicKey' => $publicKey,
369 'privateKey' => $privateKey,
370 ];
371 }
372
373
374
375
376
377
378 public function rsaEncrypt($value, string $publicKey): string
379 {
380 if ($value === '' || $this->isEncrypted($value)) {
381 return (string)$value;
382 }
383
384 $keyHandle = PublicKeyLoader::load($publicKey);
385 if (!is_object($keyHandle) || !strpos(get_class($keyHandle), 'RSA\PublicKey') || !method_exists($keyHandle, 'encrypt')) {
386 debug_log(sprintf('[DataEncryption] Failed to load public key: %s', get_class($keyHandle)), 'info', false);
387 return (string)$value;
388 }
389
390 $cipherText = $keyHandle->encrypt($value);
391 return $this->setPrefixType('rsa') . $this->normalizeBase64Encode($cipherText);
392 }
393
394
395
396
397
398
399 public function rsaDecrypt(string $inputValue, string $privateKey): string
400 {
401 if (!is_string($inputValue) || $inputValue === '' || !$this->isEncrypted($inputValue) || !$this->verifyPrefix($inputValue, 'rsa')) {
402 return $inputValue;
403 }
404
405 $keyHandle = PublicKeyLoader::load($privateKey);
406 if (!is_object($keyHandle) || !strpos(get_class($keyHandle), 'RSA\PrivateKey') || !method_exists($keyHandle, 'decrypt')) {
407 debug_log(sprintf('[DataEncryption] Failed to load private key: %s', get_class($keyHandle)), 'info', false);
408 return $inputValue;
409 }
410
411 $value = $this->stripPrefix($inputValue, 'rsa');
412 $value = $this->normalizeBase64Decode($value);
413
414 return $keyHandle->decrypt($value);
415 }
416 }
417