PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.10.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.10.2
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Helpers / global / security.php

security.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.10.2, at back/Helpers/global/security.php

375 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use AcyMailing\Helpers\UpdatemeHelper;
4
5 function acym_escape($text, bool $addSlashes = true): string
6 {
7 if (is_array($text) || is_object($text)) {
8 $text = json_encode($text);
9
10 if ($addSlashes) {
11 $text = str_replace('\\', '\\\\', $text);
12 }
13 }
14
15 if (empty($text) && !is_numeric($text)) {
16 return '';
17 }
18
19 if (!preg_match('#[&<>"\']#', $text)) {
20 return $text;
21 }
22
23 return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
24 }
25
26 function acym_escapeUrl(string $url): string
27 {
28 if (empty($url)) {
29 return '';
30 }
31
32 $url = str_replace(' ', '%20', ltrim($url));
33 $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url);
34
35 if (empty($url)) {
36 return '';
37 }
38
39 if (0 !== stripos($url, 'mailto:')) {
40 $strip = ['%0d', '%0a', '%0D', '%0A'];
41 $count = 1;
42 while ($count) {
43 $url = str_replace($strip, '', $url, $count);
44 }
45 }
46
47 $url = str_replace(';//', '://', $url);
48 if (strpos($url, ':') === false && !in_array($url[0], ['/', '#', '?'], true) && !preg_match('/^[a-z0-9-]+?\.php/i', $url)) {
49 $url = 'https://'.$url;
50 }
51
52 $url = str_replace('&amp;', '&#038;', $url);
53 $url = str_replace("'", '&#039;', $url);
54
55 if (strpos($url, '[') !== false || strpos($url, ']') !== false) {
56 $to_unset = [];
57
58 if (strpos($url, '//') === 0) {
59 $to_unset[] = 'scheme';
60 $url = 'placeholder:'.$url;
61 } elseif (strpos($url, '/') === 0) {
62 $to_unset[] = 'scheme';
63 $to_unset[] = 'host';
64 $url = 'placeholder://placeholder'.$url;
65 }
66
67 $parsed = parse_url($url);
68
69 if (!empty($parsed)) {
70 foreach ($to_unset as $key) {
71 unset($parsed[$key]);
72 }
73 }
74
75 $front = '';
76
77 if (isset($parsed['scheme'])) {
78 $front .= $parsed['scheme'].'://';
79 } elseif ('/' === $url[0]) {
80 $front .= '//';
81 }
82
83 if (isset($parsed['user'])) {
84 $front .= $parsed['user'];
85 }
86
87 if (isset($parsed['pass'])) {
88 $front .= ':'.$parsed['pass'];
89 }
90
91 if (isset($parsed['user']) || isset($parsed['pass'])) {
92 $front .= '@';
93 }
94
95 if (isset($parsed['host'])) {
96 $front .= $parsed['host'];
97 }
98
99 if (isset($parsed['port'])) {
100 $front .= ':'.$parsed['port'];
101 }
102
103 $end_dirty = str_replace($front, '', $url);
104 $end_clean = str_replace(['[', ']'], ['%5B', '%5D'], $end_dirty);
105 $url = str_replace($end_dirty, $end_clean, $url);
106 }
107
108 return $url;
109 }
110
111 function acym_arrayToInteger(array &$array): void
112 {
113 $array = @array_map('intval', $array);
114 }
115
116 function acym_getIP(): string
117 {
118 $map = [
119 'HTTP_X_FORWARDED_IP',
120 'X_FORWARDED_FOR',
121 'HTTP_X_FORWARDED_FOR',
122 'HTTP_CLIENT_IP',
123 'HTTP_X_FORWARDED',
124 'HTTP_FORWARDED_FOR',
125 'HTTP_FORWARDED',
126 'REMOTE_ADDR',
127 ];
128
129 $ipAddress = '';
130 foreach ($map as $oneAttribute) {
131 if (empty($_SERVER[$oneAttribute]) || strlen($_SERVER[$oneAttribute]) < 7) continue;
132
133 $ipAddress = $_SERVER[$oneAttribute];
134 break;
135 }
136
137 if (strstr($ipAddress, ',') !== false) {
138 $addresses = explode(',', $ipAddress);
139 $ipAddress = trim(end($addresses));
140 }
141
142 return strip_tags($ipAddress);
143 }
144
145 function acym_generateKey(int $length): string
146 {
147 $charactersPool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
148 $randomString = '';
149 $max = strlen($charactersPool) - 1;
150 for ($i = 0; $i < $length; $i++) {
151 $randomString .= $charactersPool[mt_rand(0, $max)];
152 }
153
154 return $randomString;
155 }
156
157 function acym_isRobot(): bool
158 {
159 if (empty($_SERVER)) {
160 return false;
161 }
162
163 if (!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'HEAD') {
164 return true;
165 }
166
167 if (!empty($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'spambayes') !== false) {
168 return true;
169 }
170 if (!empty($_SERVER['REMOTE_ADDR']) && version_compare($_SERVER['REMOTE_ADDR'], '64.235.144.0', '>=') && version_compare($_SERVER['REMOTE_ADDR'], '64.235.159.255', '<=')) {
171 return true;
172 }
173
174 return false;
175 }
176
177 function acym_displayErrors(): void
178 {
179 error_reporting(E_ALL);
180 @ini_set('display_errors', 1);
181 }
182
183 function acym_checkRobots(): void
184 {
185 if (preg_match('#(libwww-perl|python|googlebot)#i', @$_SERVER['HTTP_USER_AGENT'])) {
186 die('Not allowed for robots. Please contact us if you are not a robot');
187 }
188 }
189
190 function acym_noCache(): void
191 {
192 acym_header('Cache-Control: no-store, no-cache, must-revalidate');
193 acym_header('Cache-Control: post-check=0, pre-check=0', false);
194 acym_header('Pragma: no-cache');
195 acym_header('Expires: Wed, 17 Sep 1975 21:32:10 GMT');
196 }
197
198 function acym_isAllowed(string $controller, string $task = ''): bool
199 {
200 $controller = str_replace('front', '', $controller);
201
202 $config = acym_config();
203 $globalAccess = $config->get('acl_'.$controller, ACYM_ADMIN_GROUP);
204 if ($globalAccess === 'all') {
205 return true;
206 }
207
208 $globalAccess = explode(',', $globalAccess);
209 $globalAccess[] = ACYM_ADMIN_GROUP;
210
211 $userId = acym_currentUserId();
212 if (empty($userId)) {
213 return false;
214 }
215
216 $userGroups = acym_getGroupsByUser($userId);
217 if (empty($userGroups)) {
218 return false;
219 }
220
221 foreach ($userGroups as $oneGroup) {
222 if (in_array($oneGroup, $globalAccess)) {
223 return true;
224 }
225 }
226
227 if ($controller === 'mails' && in_array($task, ['autoSave', 'getTemplateAjax']) && acym_isAllowed('campaigns')) {
228 return true;
229 }
230
231 if ($controller === 'language' && acym_isAllowed('configuration')) {
232 return true;
233 }
234
235 if ($controller === 'file' && (acym_isAllowed('campaigns') || acym_isAllowed('mails'))) {
236 return true;
237 }
238
239 if (in_array($task, ['countResultsTotal', 'countGlobalBySegmentId', 'countResults']) && acym_isAllowed('campaigns')) {
240 return true;
241 }
242
243 if ($controller === 'lists' && $task === 'setAjaxListing' && (acym_isAllowed('campaigns') || acym_isAllowed('mails'))) {
244 return true;
245 }
246
247 if (
248 $controller === 'zones'
249 && (
250 acym_isAllowed('campaigns')
251 || acym_isAllowed('mails')
252 )
253 ) {
254 return true;
255 }
256
257 if (
258 $controller === 'toggle'
259 && (
260 acym_isAllowed('campaigns')
261 || acym_isAllowed('mails')
262 || acym_isAllowed('automations')
263 || acym_isAllowed('segments')
264 || acym_isAllowed('scenarios')
265 || acym_isAllowed('forms')
266 || acym_isAllowed('users')
267 || acym_isAllowed('lists')
268 || acym_isAllowed('fields')
269 || acym_isAllowed('bounces')
270 )
271 ) {
272 return true;
273 }
274
275 if (
276 $controller === 'dynamics'
277 && (
278 acym_isAllowed('campaigns')
279 || acym_isAllowed('mails')
280 || acym_isAllowed('automations')
281 || acym_isAllowed('segments')
282 || acym_isAllowed('scenarios')
283 )
284 ) {
285 return true;
286 }
287
288 if (
289 $task === 'loadEntityFront'
290 && (
291 acym_isAllowed('campaigns')
292 || acym_isAllowed('mails')
293 || acym_isAllowed('users')
294 || acym_isAllowed('lists')
295 )
296 ) {
297 return true;
298 }
299
300 if (
301 $task === 'getOption'
302 && (
303 acym_isAllowed('campaigns')
304 || acym_isAllowed('mails')
305 || acym_isAllowed('plugins')
306 )
307 ) {
308 return true;
309 }
310
311 return false;
312 }
313
314 function acym_raiseError(int $code, string $message): void
315 {
316 echo '<link type="text/css" rel="stylesheet" href="'.ACYM_CSS.'back_global.min.css?v='.filemtime(ACYM_MEDIA.'css'.DS.'back_global.min.css').'">';
317 echo '<div id="acym_wrapper">';
318 acym_display('Error '.$code.': '.$message, 'error', false);
319 echo '</div>';
320 http_response_code($code);
321 exit;
322 }
323
324 function acym_isLicenseValidWeekly(): bool
325 {
326 $config = acym_config();
327 $expirationDate = $config->get('expirationdate', 0);
328 if (empty($expirationDate) || (time() - 604800) > $config->get('lastlicensecheck', 0)) {
329 UpdatemeHelper::getLicenseInfo();
330 $config = acym_config(true);
331 $expirationDate = $config->get('expirationdate', 0);
332 }
333
334 return $expirationDate >= time();
335 }
336
337 function acym_generateAutologinToken(int $subId, string $subKey): string
338 {
339 $timestamp = time();
340 $payload = $subId.'|'.$timestamp;
341 $secret = $subKey.acym_getSiteSalt();
342 $signature = hash_hmac('sha256', $payload, $secret);
343
344 return dechex($timestamp).'.'.$signature;
345 }
346
347 function acym_verifyAutologinToken(int $subId, string $token, string $storedKey): bool
348 {
349 $parts = explode('.', $token, 2);
350 if (count($parts) !== 2) {
351 return false;
352 }
353
354 $timestamp = @hexdec($parts[0]);
355 $signature = $parts[1];
356
357 if (empty($timestamp) || $timestamp <= 0) {
358 return false;
359 }
360
361 $config = acym_config();
362 $maxAgeHours = intval($config->get('autologin_token_duration', 48));
363 $maxAge = $maxAgeHours * 3600;
364
365 if ((time() - $timestamp) > $maxAge) {
366 return false;
367 }
368
369 $payload = $subId.'|'.$timestamp;
370 $secret = $storedKey.acym_getSiteSalt();
371 $expectedSignature = hash_hmac('sha256', $payload, $secret);
372
373 return hash_equals($expectedSignature, $signature);
374 }
375