-
Notifications
You must be signed in to change notification settings - Fork 10
/
CookieCryptTrait.php
192 lines (171 loc) · 5.46 KB
/
CookieCryptTrait.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.1.6
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Utility;
use RuntimeException;
/**
* Cookie Crypt Trait.
*
* Provides the encrypt/decrypt logic for the CookieComponent.
*
* @link https://book.cakephp.org/4/en/controllers/components/cookie.html
*/
trait CookieCryptTrait
{
/**
* Valid cipher names for encrypted cookies.
*
* @var string[]
*/
protected $_validCiphers = ['aes'];
/**
* Returns the encryption key to be used.
*
* @return string
*/
abstract protected function _getCookieEncryptionKey(): string;
/**
* Encrypts $value using public $type method in Security class
*
* @param string|array $value Value to encrypt
* @param string|false $encrypt Encryption mode to use. False
* disabled encryption.
* @param string|null $key Used as the security salt if specified.
* @return string Encoded values
*/
protected function _encrypt($value, $encrypt, ?string $key = null): string
{
if (is_array($value)) {
$value = $this->_implode($value);
}
if ($encrypt === false) {
return $value;
}
$this->_checkCipher($encrypt);
$prefix = 'Q2FrZQ==.';
$cipher = '';
if ($key === null) {
$key = $this->_getCookieEncryptionKey();
}
if ($encrypt === 'aes') {
$cipher = Security::encrypt($value, $key);
}
return $prefix . base64_encode($cipher);
}
/**
* Helper method for validating encryption cipher names.
*
* @param string $encrypt The cipher name.
* @return void
* @throws \RuntimeException When an invalid cipher is provided.
*/
protected function _checkCipher(string $encrypt): void
{
if (!in_array($encrypt, $this->_validCiphers, true)) {
$msg = sprintf(
'Invalid encryption cipher. Must be one of %s or false.',
implode(', ', $this->_validCiphers)
);
throw new RuntimeException($msg);
}
}
/**
* Decrypts $value using public $type method in Security class
*
* @param string[]|string $values Values to decrypt
* @param string|false $mode Encryption mode
* @param string|null $key Used as the security salt if specified.
* @return string|array Decrypted values
*/
protected function _decrypt($values, $mode, ?string $key = null)
{
if (is_string($values)) {
return $this->_decode($values, $mode, $key);
}
$decrypted = [];
foreach ($values as $name => $value) {
$decrypted[$name] = $this->_decode($value, $mode, $key);
}
return $decrypted;
}
/**
* Decodes and decrypts a single value.
*
* @param string $value The value to decode & decrypt.
* @param string|false $encrypt The encryption cipher to use.
* @param string|null $key Used as the security salt if specified.
* @return string|array Decoded values.
*/
protected function _decode(string $value, $encrypt, ?string $key)
{
if (!$encrypt) {
return $this->_explode($value);
}
$this->_checkCipher($encrypt);
$prefix = 'Q2FrZQ==.';
$prefixLength = strlen($prefix);
if (strncmp($value, $prefix, $prefixLength) !== 0) {
return '';
}
$value = base64_decode(substr($value, $prefixLength), true);
if ($value === false || $value === '') {
return '';
}
if ($key === null) {
$key = $this->_getCookieEncryptionKey();
}
if ($encrypt === 'aes') {
$value = Security::decrypt($value, $key);
}
if ($value === null) {
return '';
}
return $this->_explode($value);
}
/**
* Implode method to keep keys are multidimensional arrays
*
* @param array $array Map of key and values
* @return string A JSON encoded string.
*/
protected function _implode(array $array): string
{
return json_encode($array);
}
/**
* Explode method to return array from string set in CookieComponent::_implode()
* Maintains reading backwards compatibility with 1.x CookieComponent::_implode().
*
* @param string $string A string containing JSON encoded data, or a bare string.
* @return string|array Map of key and values
*/
protected function _explode(string $string)
{
$first = substr($string, 0, 1);
if ($first === '{' || $first === '[') {
$ret = json_decode($string, true);
return $ret ?? $string;
}
$array = [];
foreach (explode(',', $string) as $pair) {
$key = explode('|', $pair);
if (!isset($key[1])) {
return $key[0];
}
$array[$key[0]] = $key[1];
}
return $array;
}
}