Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Protector often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Protector, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
6 | class Protector |
||
7 | { |
||
8 | public $mydirname; |
||
9 | |||
10 | public $_conn; |
||
11 | public $_conf = array(); |
||
12 | public $_conf_serialized = ''; |
||
13 | |||
14 | public $_bad_globals = array(); |
||
15 | |||
16 | public $message = ''; |
||
17 | public $warning = false; |
||
18 | public $error = false; |
||
19 | public $_doubtful_requests = array(); |
||
20 | public $_bigumbrella_doubtfuls = array(); |
||
21 | |||
22 | public $_dblayertrap_doubtfuls = array(); |
||
23 | public $_dblayertrap_doubtful_needles = array( |
||
24 | 'information_schema', |
||
25 | 'select', |
||
26 | "'", |
||
27 | '"'); |
||
28 | |||
29 | public $_logged = false; |
||
30 | |||
31 | public $_done_badext = false; |
||
32 | public $_done_intval = false; |
||
33 | public $_done_dotdot = false; |
||
34 | public $_done_nullbyte = false; |
||
35 | public $_done_contami = false; |
||
36 | public $_done_isocom = false; |
||
37 | public $_done_union = false; |
||
38 | public $_done_dos = false; |
||
39 | |||
40 | public $_safe_badext = true; |
||
41 | public $_safe_contami = true; |
||
42 | public $_safe_isocom = true; |
||
43 | public $_safe_union = true; |
||
44 | |||
45 | public $_spamcount_uri = 0; |
||
46 | |||
47 | public $_should_be_banned_time0 = false; |
||
48 | public $_should_be_banned = false; |
||
49 | |||
50 | public $_dos_stage; |
||
51 | |||
52 | public $ip_matched_info; |
||
53 | |||
54 | public $last_error_type = 'UNKNOWN'; |
||
55 | |||
56 | /** |
||
57 | * Constructor |
||
58 | */ |
||
59 | protected function __construct() |
||
60 | { |
||
61 | $this->mydirname = 'protector'; |
||
62 | |||
63 | // Preferences from configs/cache |
||
64 | $this->_conf_serialized = @file_get_contents($this->get_filepath4confighcache()); |
||
65 | $this->_conf = @unserialize($this->_conf_serialized); |
||
66 | if (empty($this->_conf)) { |
||
67 | $this->_conf = array(); |
||
68 | } |
||
69 | |||
70 | if (!empty($this->_conf['global_disabled'])) { |
||
71 | return; |
||
72 | } |
||
73 | |||
74 | // die if PHP_SELF XSS found (disabled in 2.53) |
||
75 | // if ( preg_match( '/[<>\'";\n ]/' , @$_SERVER['PHP_SELF'] ) ) { |
||
76 | // $this->message .= "Invalid PHP_SELF '{$_SERVER['PHP_SELF']}' found.\n" ; |
||
77 | // $this->output_log( 'PHP_SELF XSS' ) ; |
||
78 | // die( 'invalid PHP_SELF' ) ; |
||
79 | // } |
||
80 | |||
81 | // sanitize against PHP_SELF/PATH_INFO XSS (disabled in 3.33) |
||
82 | // $_SERVER['PHP_SELF'] = strtr( @$_SERVER['PHP_SELF'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ; |
||
83 | // if( ! empty( $_SERVER['PATH_INFO'] ) ) $_SERVER['PATH_INFO'] = strtr( @$_SERVER['PATH_INFO'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ; |
||
84 | |||
85 | $this->_bad_globals = array( |
||
86 | 'GLOBALS', |
||
87 | '_SESSION', |
||
88 | 'HTTP_SESSION_VARS', |
||
89 | '_GET', |
||
90 | 'HTTP_GET_VARS', |
||
91 | '_POST', |
||
92 | 'HTTP_POST_VARS', |
||
93 | '_COOKIE', |
||
94 | 'HTTP_COOKIE_VARS', |
||
95 | '_SERVER', |
||
96 | 'HTTP_SERVER_VARS', |
||
97 | '_REQUEST', |
||
98 | '_ENV', |
||
99 | '_FILES', |
||
100 | 'xoopsDB', |
||
101 | 'xoopsUser', |
||
102 | 'xoopsUserId', |
||
103 | 'xoopsUserGroups', |
||
104 | 'xoopsUserIsAdmin', |
||
105 | 'xoopsConfig', |
||
106 | 'xoopsOption', |
||
107 | 'xoopsModule', |
||
108 | 'xoopsModuleConfig'); |
||
109 | |||
110 | $this->_initial_recursive($_GET, 'G'); |
||
111 | $this->_initial_recursive($_POST, 'P'); |
||
112 | $this->_initial_recursive($_COOKIE, 'C'); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * @param $val |
||
117 | * @param $key |
||
118 | */ |
||
119 | protected function _initial_recursive($val, $key) |
||
120 | { |
||
121 | if (is_array($val)) { |
||
122 | foreach ($val as $subkey => $subval) { |
||
123 | // check bad globals |
||
124 | View Code Duplication | if (in_array($subkey, $this->_bad_globals, true)) { |
|
125 | $this->message .= "Attempt to inject '$subkey' was found.\n"; |
||
126 | $this->_safe_contami = false; |
||
127 | $this->last_error_type = 'CONTAMI'; |
||
128 | } |
||
129 | $this->_initial_recursive($subval, $key . '_' . base64_encode($subkey)); |
||
130 | } |
||
131 | } else { |
||
132 | // check nullbyte attack |
||
133 | if (@$this->_conf['san_nullbyte'] && false !== strpos($val, chr(0))) { |
||
134 | $val = str_replace(chr(0), ' ', $val); |
||
135 | $this->replace_doubtful($key, $val); |
||
136 | $this->message .= "Injecting Null-byte '$val' found.\n"; |
||
137 | $this->output_log('NullByte', 0, false, 32); |
||
138 | // $this->purge() ; |
||
139 | } |
||
140 | |||
141 | // register as doubtful requests against SQL Injections |
||
142 | if (preg_match('?[\s\'"`/]?', $val)) { |
||
143 | $this->_doubtful_requests["$key"] = $val; |
||
144 | } |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * @return Protector |
||
150 | */ |
||
151 | public static function getInstance() |
||
152 | { |
||
153 | static $instance; |
||
154 | if (!isset($instance)) { |
||
155 | $instance = new Protector(); |
||
156 | } |
||
157 | |||
158 | return $instance; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * @return bool |
||
163 | */ |
||
164 | public function updateConfFromDb() |
||
165 | { |
||
166 | $constpref = '_MI_' . strtoupper($this->mydirname); |
||
167 | |||
168 | if (empty($this->_conn)) { |
||
169 | return false; |
||
170 | } |
||
171 | |||
172 | $result = @mysqli_query($this->_conn, 'SELECT conf_name,conf_value FROM ' . XOOPS_DB_PREFIX . "_config WHERE conf_title like '" . $constpref . "%'"); |
||
173 | if (!$result || mysqli_num_rows($result) < 5) { |
||
174 | return false; |
||
175 | } |
||
176 | $db_conf = array(); |
||
177 | while (list($key, $val) = mysqli_fetch_row($result)) { |
||
178 | $db_conf[$key] = $val; |
||
179 | } |
||
180 | $db_conf_serialized = serialize($db_conf); |
||
181 | |||
182 | // update config cache |
||
183 | if ($db_conf_serialized != $this->_conf_serialized) { |
||
184 | $fp = fopen($this->get_filepath4confighcache(), 'w'); |
||
185 | fwrite($fp, $db_conf_serialized); |
||
186 | fclose($fp); |
||
187 | $this->_conf = $db_conf; |
||
188 | } |
||
189 | |||
190 | return true; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * @param $conn |
||
195 | */ |
||
196 | public function setConn($conn) |
||
197 | { |
||
198 | $this->_conn = $conn; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * @return array |
||
203 | */ |
||
204 | public function getConf() |
||
205 | { |
||
206 | return $this->_conf; |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * @param bool $redirect_to_top |
||
211 | */ |
||
212 | public function purge($redirect_to_top = false) |
||
213 | { |
||
214 | // clear all session values |
||
215 | if (isset($_SESSION)) { |
||
216 | foreach ($_SESSION as $key => $val) { |
||
217 | $_SESSION[$key] = ''; |
||
218 | if (isset($GLOBALS[$key])) { |
||
219 | $GLOBALS[$key] = ''; |
||
220 | } |
||
221 | } |
||
222 | } |
||
223 | |||
224 | if (!headers_sent()) { |
||
225 | // clear typical session id of PHP |
||
226 | setcookie('PHPSESSID', '', time() - 3600, '/', '', 0); |
||
227 | if (isset($_COOKIE[session_name()])) { |
||
228 | setcookie(session_name(), '', time() - 3600, '/', '', 0); |
||
229 | } |
||
230 | |||
231 | // clear autologin cookie |
||
232 | $xoops_cookie_path = defined('XOOPS_COOKIE_PATH') ? XOOPS_COOKIE_PATH : preg_replace('?http://[^/]+(/.*)$?', "$1", XOOPS_URL); |
||
233 | if ($xoops_cookie_path == XOOPS_URL) { |
||
234 | $xoops_cookie_path = '/'; |
||
235 | } |
||
236 | setcookie('autologin_uname', '', time() - 3600, $xoops_cookie_path, '', 0); |
||
237 | setcookie('autologin_pass', '', time() - 3600, $xoops_cookie_path, '', 0); |
||
238 | } |
||
239 | |||
240 | if ($redirect_to_top) { |
||
241 | header('Location: ' . XOOPS_URL . '/'); |
||
242 | exit; |
||
|
|||
243 | } else { |
||
244 | $ret = $this->call_filter('prepurge_exit'); |
||
245 | if ($ret == false) { |
||
246 | die('Protector detects attacking actions'); |
||
247 | } |
||
248 | } |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * @param string $type |
||
253 | * @param int $uid |
||
254 | * @param bool $unique_check |
||
255 | * @param int $level |
||
256 | * |
||
257 | * @return bool |
||
258 | */ |
||
259 | public function output_log($type = 'UNKNOWN', $uid = 0, $unique_check = false, $level = 1) |
||
305 | |||
306 | /** |
||
307 | * @param $expire |
||
308 | * |
||
309 | * @return bool |
||
310 | */ |
||
311 | View Code Duplication | public function write_file_bwlimit($expire) |
|
312 | { |
||
313 | $expire = min((int)$expire, time() + 300); |
||
314 | |||
315 | $fp = @fopen($this->get_filepath4bwlimit(), 'w'); |
||
316 | if ($fp) { |
||
317 | @flock($fp, LOCK_EX); |
||
318 | fwrite($fp, $expire . "\n"); |
||
319 | @flock($fp, LOCK_UN); |
||
320 | fclose($fp); |
||
321 | |||
322 | return true; |
||
323 | } else { |
||
324 | return false; |
||
325 | } |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * @return mixed |
||
330 | */ |
||
331 | public function get_bwlimit() |
||
332 | { |
||
333 | list($expire) = @file(Protector::get_filepath4bwlimit()); |
||
334 | $expire = min((int)$expire, time() + 300); |
||
335 | |||
336 | return $expire; |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * @return string |
||
341 | */ |
||
342 | public static function get_filepath4bwlimit() |
||
343 | { |
||
344 | return XOOPS_TRUST_PATH . '/modules/protector/configs/bwlimit' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6); |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * @param $bad_ips |
||
349 | * |
||
350 | * @return bool |
||
351 | */ |
||
352 | View Code Duplication | public function write_file_badips($bad_ips) |
|
353 | { |
||
354 | asort($bad_ips); |
||
355 | |||
356 | $fp = @fopen($this->get_filepath4badips(), 'w'); |
||
357 | if ($fp) { |
||
358 | @flock($fp, LOCK_EX); |
||
359 | fwrite($fp, serialize($bad_ips) . "\n"); |
||
360 | @flock($fp, LOCK_UN); |
||
361 | fclose($fp); |
||
362 | |||
363 | return true; |
||
364 | } else { |
||
365 | return false; |
||
366 | } |
||
367 | } |
||
368 | |||
369 | /** |
||
370 | * @param int $jailed_time |
||
371 | * @param null $ip |
||
372 | * |
||
373 | * @return bool |
||
374 | */ |
||
375 | public function register_bad_ips($jailed_time = 0, $ip = null) |
||
376 | { |
||
377 | if (empty($ip)) { |
||
378 | $ip = \Xmf\IPAddress::fromRequest()->asReadable(); |
||
379 | } |
||
380 | if (empty($ip)) { |
||
381 | return false; |
||
382 | } |
||
383 | |||
384 | $bad_ips = $this->get_bad_ips(true); |
||
385 | $bad_ips[$ip] = $jailed_time ?: 0x7fffffff; |
||
386 | |||
387 | return $this->write_file_badips($bad_ips); |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * @param bool $with_jailed_time |
||
392 | * |
||
393 | * @return array|mixed |
||
394 | */ |
||
395 | public function get_bad_ips($with_jailed_time = false) |
||
396 | { |
||
397 | list($bad_ips_serialized) = @file(Protector::get_filepath4badips()); |
||
398 | $bad_ips = empty($bad_ips_serialized) ? array() : @unserialize($bad_ips_serialized); |
||
399 | if (!is_array($bad_ips) || isset($bad_ips[0])) { |
||
400 | $bad_ips = array(); |
||
401 | } |
||
402 | |||
403 | // expire jailed_time |
||
404 | $pos = 0; |
||
405 | foreach ($bad_ips as $bad_ip => $jailed_time) { |
||
406 | if ($jailed_time >= time()) { |
||
407 | break; |
||
408 | } |
||
409 | ++$pos; |
||
410 | } |
||
411 | $bad_ips = array_slice($bad_ips, $pos); |
||
412 | |||
413 | if ($with_jailed_time) { |
||
414 | return $bad_ips; |
||
415 | } else { |
||
416 | return array_keys($bad_ips); |
||
417 | } |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * @return string |
||
422 | */ |
||
423 | public static function get_filepath4badips() |
||
424 | { |
||
425 | return XOOPS_TRUST_PATH . '/modules/protector/configs/badips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6); |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * @param bool $with_info |
||
430 | * |
||
431 | * @return array|mixed |
||
432 | */ |
||
433 | public function get_group1_ips($with_info = false) |
||
434 | { |
||
435 | list($group1_ips_serialized) = @file(Protector::get_filepath4group1ips()); |
||
436 | $group1_ips = empty($group1_ips_serialized) ? array() : @unserialize($group1_ips_serialized); |
||
437 | if (!is_array($group1_ips)) { |
||
438 | $group1_ips = array(); |
||
439 | } |
||
440 | |||
441 | if ($with_info) { |
||
442 | $group1_ips = array_flip($group1_ips); |
||
443 | } |
||
444 | |||
445 | return $group1_ips; |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * @return string |
||
450 | */ |
||
451 | public static function get_filepath4group1ips() |
||
452 | { |
||
453 | return XOOPS_TRUST_PATH . '/modules/protector/configs/group1ips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6); |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * @return string |
||
458 | */ |
||
459 | public function get_filepath4confighcache() |
||
460 | { |
||
461 | return XOOPS_TRUST_PATH . '/modules/protector/configs/configcache' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6); |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * @param $ips |
||
466 | * |
||
467 | * @return bool |
||
468 | */ |
||
469 | public function ip_match($ips) |
||
470 | { |
||
471 | $requestIp = \Xmf\IPAddress::fromRequest()->asReadable(); |
||
472 | if (false === $requestIp) { // nothing to match |
||
473 | $this->ip_matched_info = null; |
||
474 | return false; |
||
475 | } |
||
476 | foreach ($ips as $ip => $info) { |
||
477 | if ($ip) { |
||
478 | switch (strtolower(substr($ip, -1))) { |
||
479 | case '.' : |
||
480 | case ':' : |
||
481 | // foward match |
||
482 | if (substr($requestIp, 0, strlen($ip)) == $ip) { |
||
483 | $this->ip_matched_info = $info; |
||
484 | return true; |
||
485 | } |
||
486 | break; |
||
487 | case '0' : |
||
488 | case '1' : |
||
489 | case '2' : |
||
490 | case '3' : |
||
491 | case '4' : |
||
492 | case '5' : |
||
493 | case '6' : |
||
494 | case '7' : |
||
495 | case '8' : |
||
496 | case '9' : |
||
497 | case 'a' : |
||
498 | case 'b' : |
||
499 | case 'c' : |
||
500 | case 'd' : |
||
501 | case 'e' : |
||
502 | case 'f' : |
||
503 | // full match |
||
504 | if ($requestIp == $ip) { |
||
505 | $this->ip_matched_info = $info; |
||
506 | return true; |
||
507 | } |
||
508 | break; |
||
509 | default : |
||
510 | // perl regex |
||
511 | if (@preg_match($ip, $requestIp)) { |
||
512 | $this->ip_matched_info = $info; |
||
513 | return true; |
||
514 | } |
||
515 | break; |
||
516 | } |
||
517 | } |
||
518 | } |
||
519 | $this->ip_matched_info = null; |
||
520 | return false; |
||
521 | } |
||
522 | |||
523 | /** |
||
524 | * @param null $ip |
||
525 | * |
||
526 | * @return bool |
||
527 | */ |
||
528 | public function deny_by_htaccess($ip = null) |
||
581 | |||
582 | /** |
||
583 | * @return array |
||
584 | */ |
||
585 | public function getDblayertrapDoubtfuls() |
||
586 | { |
||
587 | return $this->_dblayertrap_doubtfuls; |
||
588 | } |
||
589 | |||
590 | /** |
||
591 | * @param $val |
||
592 | * @return null |
||
593 | */ |
||
594 | protected function _dblayertrap_check_recursive($val) |
||
595 | { |
||
596 | if (is_array($val)) { |
||
597 | foreach ($val as $subval) { |
||
598 | $this->_dblayertrap_check_recursive($subval); |
||
599 | } |
||
600 | } else { |
||
601 | if (strlen($val) < 6) { |
||
602 | return null; |
||
603 | } |
||
604 | $val = get_magic_quotes_gpc() ? stripslashes($val) : $val; |
||
605 | foreach ($this->_dblayertrap_doubtful_needles as $needle) { |
||
606 | if (false !== stripos($val, $needle)) { |
||
607 | $this->_dblayertrap_doubtfuls[] = $val; |
||
608 | } |
||
609 | } |
||
610 | } |
||
611 | } |
||
612 | |||
613 | /** |
||
614 | * @param bool $force_override |
||
615 | * @return null |
||
616 | */ |
||
617 | public function dblayertrap_init($force_override = false) |
||
618 | { |
||
619 | if (!empty($GLOBALS['xoopsOption']['nocommon']) || defined('_LEGACY_PREVENT_EXEC_COMMON_') || defined('_LEGACY_PREVENT_LOAD_CORE_')) { |
||
620 | return null; |
||
621 | } // skip |
||
622 | |||
623 | $this->_dblayertrap_doubtfuls = array(); |
||
624 | $this->_dblayertrap_check_recursive($_GET); |
||
625 | $this->_dblayertrap_check_recursive($_POST); |
||
626 | $this->_dblayertrap_check_recursive($_COOKIE); |
||
627 | if (empty($this->_conf['dblayertrap_wo_server'])) { |
||
628 | $this->_dblayertrap_check_recursive($_SERVER); |
||
629 | } |
||
630 | |||
631 | if (!empty($this->_dblayertrap_doubtfuls) || $force_override) { |
||
632 | @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase'); |
||
633 | require_once dirname(__DIR__) . '/class/ProtectorMysqlDatabase.class.php'; |
||
634 | } |
||
635 | } |
||
636 | |||
637 | /** |
||
638 | * @param $val |
||
639 | */ |
||
640 | protected function _bigumbrella_check_recursive($val) |
||
641 | { |
||
642 | if (is_array($val)) { |
||
643 | foreach ($val as $subval) { |
||
644 | $this->_bigumbrella_check_recursive($subval); |
||
645 | } |
||
646 | } else { |
||
647 | if (preg_match('/[<\'"].{15}/s', $val, $regs)) { |
||
648 | $this->_bigumbrella_doubtfuls[] = $regs[0]; |
||
649 | } |
||
650 | } |
||
651 | } |
||
652 | |||
653 | public function bigumbrella_init() |
||
654 | { |
||
655 | $this->_bigumbrella_doubtfuls = array(); |
||
656 | $this->_bigumbrella_check_recursive($_GET); |
||
657 | $this->_bigumbrella_check_recursive(@$_SERVER['PHP_SELF']); |
||
658 | |||
659 | if (!empty($this->_bigumbrella_doubtfuls)) { |
||
660 | ob_start(array($this, 'bigumbrella_outputcheck')); |
||
661 | } |
||
662 | } |
||
663 | |||
664 | /** |
||
665 | * @param $s |
||
666 | * |
||
667 | * @return string |
||
668 | */ |
||
669 | public function bigumbrella_outputcheck($s) |
||
670 | { |
||
671 | if (defined('BIGUMBRELLA_DISABLED')) { |
||
672 | return $s; |
||
673 | } |
||
674 | |||
675 | if (function_exists('headers_list')) { |
||
676 | foreach (headers_list() as $header) { |
||
677 | if (false !== stripos($header, 'Content-Type:') && false === stripos($header, 'text/html')) { |
||
678 | return $s; |
||
679 | } |
||
680 | } |
||
681 | } |
||
682 | |||
683 | if (!is_array($this->_bigumbrella_doubtfuls)) { |
||
684 | return 'bigumbrella injection found.'; |
||
685 | } |
||
686 | |||
687 | foreach ($this->_bigumbrella_doubtfuls as $doubtful) { |
||
688 | if (false !== strpos($s, $doubtful)) { |
||
689 | return 'XSS found by Protector.'; |
||
690 | } |
||
691 | } |
||
692 | |||
693 | return $s; |
||
694 | } |
||
695 | |||
696 | /** |
||
697 | * @return bool |
||
698 | */ |
||
699 | public function intval_allrequestsendid() |
||
700 | { |
||
701 | global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS; |
||
702 | |||
703 | if ($this->_done_intval) { |
||
704 | return true; |
||
705 | } else { |
||
706 | $this->_done_intval = true; |
||
707 | } |
||
708 | |||
709 | View Code Duplication | foreach ($_GET as $key => $val) { |
|
710 | if (substr($key, -2) === 'id' && !is_array($_GET[$key])) { |
||
711 | $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val); |
||
712 | $_GET[$key] = $HTTP_GET_VARS[$key] = $newval; |
||
713 | if ($_REQUEST[$key] == $_GET[$key]) { |
||
714 | $_REQUEST[$key] = $newval; |
||
715 | } |
||
716 | } |
||
717 | } |
||
718 | View Code Duplication | foreach ($_POST as $key => $val) { |
|
719 | if (substr($key, -2) === 'id' && !is_array($_POST[$key])) { |
||
720 | $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val); |
||
721 | $_POST[$key] = $HTTP_POST_VARS[$key] = $newval; |
||
722 | if ($_REQUEST[$key] == $_POST[$key]) { |
||
723 | $_REQUEST[$key] = $newval; |
||
724 | } |
||
725 | } |
||
726 | } |
||
727 | View Code Duplication | foreach ($_COOKIE as $key => $val) { |
|
728 | if (substr($key, -2) === 'id' && !is_array($_COOKIE[$key])) { |
||
729 | $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val); |
||
730 | $_COOKIE[$key] = $HTTP_COOKIE_VARS[$key] = $newval; |
||
731 | if ($_REQUEST[$key] == $_COOKIE[$key]) { |
||
732 | $_REQUEST[$key] = $newval; |
||
733 | } |
||
734 | } |
||
735 | } |
||
736 | |||
737 | return true; |
||
738 | } |
||
739 | |||
740 | /** |
||
741 | * @return bool |
||
742 | */ |
||
743 | public function eliminate_dotdot() |
||
744 | { |
||
745 | global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS; |
||
746 | |||
747 | if ($this->_done_dotdot) { |
||
748 | return true; |
||
749 | } else { |
||
750 | $this->_done_dotdot = true; |
||
751 | } |
||
752 | |||
753 | foreach ($_GET as $key => $val) { |
||
754 | if (is_array($_GET[$key])) { |
||
755 | continue; |
||
756 | } |
||
757 | if (substr(trim($val), 0, 3) === '../' || false !== strpos($val, '/../')) { |
||
758 | $this->last_error_type = 'DirTraversal'; |
||
759 | $this->message .= "Directory Traversal '$val' found.\n"; |
||
760 | $this->output_log($this->last_error_type, 0, false, 64); |
||
761 | $sanitized_val = str_replace(chr(0), '', $val); |
||
762 | if (substr($sanitized_val, -2) !== ' .') { |
||
763 | $sanitized_val .= ' .'; |
||
764 | } |
||
765 | $_GET[$key] = $HTTP_GET_VARS[$key] = $sanitized_val; |
||
766 | if ($_REQUEST[$key] == $_GET[$key]) { |
||
767 | $_REQUEST[$key] = $sanitized_val; |
||
768 | } |
||
769 | } |
||
770 | } |
||
771 | |||
772 | /* foreach ($_POST as $key => $val) { |
||
773 | if( is_array( $_POST[ $key ] ) ) continue ; |
||
774 | if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) { |
||
775 | $this->last_error_type = 'ParentDir' ; |
||
776 | $this->message .= "Doubtful file specification '$val' found.\n" ; |
||
777 | $this->output_log( $this->last_error_type , 0 , false , 128 ) ; |
||
778 | $sanitized_val = str_replace( chr(0) , '' , $val ) ; |
||
779 | if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ; |
||
780 | $_POST[ $key ] = $HTTP_POST_VARS[ $key ] = $sanitized_val ; |
||
781 | if ($_REQUEST[ $key ] == $_POST[ $key ]) { |
||
782 | $_REQUEST[ $key ] = $sanitized_val ; |
||
783 | } |
||
784 | } |
||
785 | } |
||
786 | foreach ($_COOKIE as $key => $val) { |
||
787 | if( is_array( $_COOKIE[ $key ] ) ) continue ; |
||
788 | if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) { |
||
789 | $this->last_error_type = 'ParentDir' ; |
||
790 | $this->message .= "Doubtful file specification '$val' found.\n" ; |
||
791 | $this->output_log( $this->last_error_type , 0 , false , 128 ) ; |
||
792 | $sanitized_val = str_replace( chr(0) , '' , $val ) ; |
||
793 | if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ; |
||
794 | $_COOKIE[ $key ] = $HTTP_COOKIE_VARS[ $key ] = $sanitized_val ; |
||
795 | if ($_REQUEST[ $key ] == $_COOKIE[ $key ]) { |
||
796 | $_REQUEST[ $key ] = $sanitized_val ; |
||
797 | } |
||
798 | } |
||
799 | }*/ |
||
800 | |||
801 | return true; |
||
802 | } |
||
803 | |||
804 | /** |
||
805 | * @param $current |
||
806 | * @param $indexes |
||
807 | * |
||
808 | * @return bool |
||
809 | */ |
||
810 | public function &get_ref_from_base64index(&$current, $indexes) |
||
811 | { |
||
812 | foreach ($indexes as $index) { |
||
813 | $index = base64_decode($index); |
||
814 | if (!is_array($current)) { |
||
815 | return false; |
||
816 | } |
||
817 | $current =& $current[$index]; |
||
818 | } |
||
819 | |||
820 | return $current; |
||
821 | } |
||
822 | |||
823 | /** |
||
824 | * @param $key |
||
825 | * @param $val |
||
826 | */ |
||
827 | public function replace_doubtful($key, $val) |
||
828 | { |
||
829 | global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS; |
||
830 | |||
831 | $index_expression = ''; |
||
832 | $indexes = explode('_', $key); |
||
833 | $base_array = array_shift($indexes); |
||
834 | |||
835 | switch ($base_array) { |
||
836 | case 'G' : |
||
837 | $main_ref =& $this->get_ref_from_base64index($_GET, $indexes); |
||
838 | $legacy_ref =& $this->get_ref_from_base64index($HTTP_GET_VARS, $indexes); |
||
839 | break; |
||
840 | case 'P' : |
||
841 | $main_ref =& $this->get_ref_from_base64index($_POST, $indexes); |
||
842 | $legacy_ref =& $this->get_ref_from_base64index($HTTP_POST_VARS, $indexes); |
||
843 | break; |
||
844 | case 'C' : |
||
845 | $main_ref =& $this->get_ref_from_base64index($_COOKIE, $indexes); |
||
846 | $legacy_ref =& $this->get_ref_from_base64index($HTTP_COOKIE_VARS, $indexes); |
||
847 | break; |
||
848 | default : |
||
849 | exit; |
||
850 | } |
||
851 | if (!isset($main_ref)) { |
||
852 | exit; |
||
853 | } |
||
854 | $request_ref =& $this->get_ref_from_base64index($_REQUEST, $indexes); |
||
855 | if ($request_ref !== false && $main_ref == $request_ref) { |
||
856 | $request_ref = $val; |
||
857 | } |
||
858 | $main_ref = $val; |
||
859 | $legacy_ref = $val; |
||
860 | } |
||
861 | |||
862 | /** |
||
863 | * @return bool |
||
864 | */ |
||
865 | public function check_uploaded_files() |
||
866 | { |
||
867 | if ($this->_done_badext) { |
||
868 | return $this->_safe_badext; |
||
869 | } else { |
||
870 | $this->_done_badext = true; |
||
871 | } |
||
872 | |||
873 | // extensions never uploaded |
||
874 | $bad_extensions = array('php', 'phtml', 'phtm', 'php3', 'php4', 'cgi', 'pl', 'asp'); |
||
875 | // extensions needed image check (anti-IE Content-Type XSS) |
||
876 | $image_extensions = array( |
||
877 | 1 => 'gif', |
||
878 | 2 => 'jpg', |
||
879 | 3 => 'png', |
||
880 | 4 => 'swf', |
||
881 | 5 => 'psd', |
||
882 | 6 => 'bmp', |
||
883 | 7 => 'tif', |
||
884 | 8 => 'tif', |
||
885 | 9 => 'jpc', |
||
886 | 10 => 'jp2', |
||
887 | 11 => 'jpx', |
||
888 | 12 => 'jb2', |
||
889 | 13 => 'swc', |
||
890 | 14 => 'iff', |
||
891 | 15 => 'wbmp', |
||
892 | 16 => 'xbm'); |
||
893 | |||
894 | foreach ($_FILES as $_file) { |
||
895 | if (!empty($_file['error'])) { |
||
896 | continue; |
||
897 | } |
||
898 | if (!empty($_file['name']) && is_string($_file['name'])) { |
||
899 | $ext = strtolower(substr(strrchr($_file['name'], '.'), 1)); |
||
900 | if ($ext === 'jpeg') { |
||
901 | $ext = 'jpg'; |
||
902 | } elseif ($ext === 'tiff') { |
||
903 | $ext = 'tif'; |
||
904 | } |
||
905 | |||
906 | // anti multiple dot file (Apache mod_mime.c) |
||
907 | if (count(explode('.', str_replace('.tar.gz', '.tgz', $_file['name']))) > 2) { |
||
908 | $this->message .= "Attempt to multiple dot file {$_file['name']}.\n"; |
||
909 | $this->_safe_badext = false; |
||
910 | $this->last_error_type = 'UPLOAD'; |
||
911 | } |
||
912 | |||
913 | // anti dangerous extensions |
||
914 | View Code Duplication | if (in_array($ext, $bad_extensions)) { |
|
915 | $this->message .= "Attempt to upload {$_file['name']}.\n"; |
||
916 | $this->_safe_badext = false; |
||
917 | $this->last_error_type = 'UPLOAD'; |
||
918 | } |
||
919 | |||
920 | // anti camouflaged image file |
||
921 | if (in_array($ext, $image_extensions)) { |
||
922 | $image_attributes = @getimagesize($_file['tmp_name']); |
||
923 | if ($image_attributes === false && is_uploaded_file($_file['tmp_name'])) { |
||
924 | // open_basedir restriction |
||
925 | $temp_file = XOOPS_ROOT_PATH . '/uploads/protector_upload_temporary' . md5(time()); |
||
926 | move_uploaded_file($_file['tmp_name'], $temp_file); |
||
927 | $image_attributes = @getimagesize($temp_file); |
||
928 | @unlink($temp_file); |
||
929 | } |
||
930 | |||
931 | if ($image_attributes === false || $image_extensions[(int)$image_attributes[2]] != $ext) { |
||
932 | $this->message .= "Attempt to upload camouflaged image file {$_file['name']}.\n"; |
||
933 | $this->_safe_badext = false; |
||
934 | $this->last_error_type = 'UPLOAD'; |
||
935 | } |
||
936 | } |
||
937 | } |
||
938 | } |
||
939 | |||
940 | return $this->_safe_badext; |
||
941 | } |
||
942 | |||
943 | /** |
||
944 | * @return bool |
||
945 | */ |
||
946 | public function check_contami_systemglobals() |
||
947 | { |
||
948 | /* if( $this->_done_contami ) return $this->_safe_contami ; |
||
949 | else $this->_done_contami = true ; */ |
||
950 | |||
951 | /* foreach ($this->_bad_globals as $bad_global) { |
||
952 | if ( isset( $_REQUEST[ $bad_global ] ) ) { |
||
953 | $this->message .= "Attempt to inject '$bad_global' was found.\n" ; |
||
954 | $this->_safe_contami = false ; |
||
955 | $this->last_error_type = 'CONTAMI' ; |
||
956 | } |
||
957 | }*/ |
||
958 | |||
959 | return $this->_safe_contami; |
||
960 | } |
||
961 | |||
962 | /** |
||
963 | * @param bool $sanitize |
||
964 | * |
||
965 | * @return bool |
||
966 | */ |
||
967 | public function check_sql_isolatedcommentin($sanitize = true) |
||
968 | { |
||
969 | if ($this->_done_isocom) { |
||
970 | return $this->_safe_isocom; |
||
971 | } else { |
||
972 | $this->_done_isocom = true; |
||
973 | } |
||
974 | |||
975 | foreach ($this->_doubtful_requests as $key => $val) { |
||
976 | $str = $val; |
||
977 | while ($str = strstr($str, '/*')) { /* */ |
||
978 | $str = strstr(substr($str, 2), '*/'); |
||
979 | if ($str === false) { |
||
980 | $this->message .= "Isolated comment-in found. ($val)\n"; |
||
981 | if ($sanitize) { |
||
982 | $this->replace_doubtful($key, $val . '*/'); |
||
983 | } |
||
984 | $this->_safe_isocom = false; |
||
985 | $this->last_error_type = 'ISOCOM'; |
||
986 | } |
||
987 | } |
||
988 | } |
||
989 | |||
990 | return $this->_safe_isocom; |
||
991 | } |
||
992 | |||
993 | /** |
||
994 | * @param bool $sanitize |
||
995 | * |
||
996 | * @return bool |
||
997 | */ |
||
998 | public function check_sql_union($sanitize = true) |
||
999 | { |
||
1000 | if ($this->_done_union) { |
||
1001 | return $this->_safe_union; |
||
1002 | } else { |
||
1003 | $this->_done_union = true; |
||
1004 | } |
||
1005 | |||
1006 | foreach ($this->_doubtful_requests as $key => $val) { |
||
1007 | $str = str_replace(array('/*', '*/'), '', preg_replace('?/\*.+\*/?sU', '', $val)); |
||
1008 | if (preg_match('/\sUNION\s+(ALL|SELECT)/i', $str)) { |
||
1009 | $this->message .= "Pattern like SQL injection found. ($val)\n"; |
||
1010 | if ($sanitize) { |
||
1011 | // $this->replace_doubtful($key, preg_replace('/union/i', 'uni-on', $val)); |
||
1012 | $this->replace_doubtful($key, str_ireplace('union', 'uni-on', $val)); |
||
1013 | } |
||
1014 | $this->_safe_union = false; |
||
1015 | $this->last_error_type = 'UNION'; |
||
1016 | } |
||
1017 | } |
||
1018 | |||
1019 | return $this->_safe_union; |
||
1020 | } |
||
1021 | |||
1022 | /** |
||
1023 | * @param $uid |
||
1024 | * |
||
1025 | * @return bool |
||
1026 | */ |
||
1027 | public function stopforumspam($uid) |
||
1028 | { |
||
1029 | if (!function_exists('curl_init')) { |
||
1030 | return false; |
||
1031 | } |
||
1032 | |||
1033 | if ($_SERVER['REQUEST_METHOD'] !== 'POST') { |
||
1034 | return false; |
||
1035 | } |
||
1036 | |||
1037 | $query = 'f=serial&ip=' . $_SERVER['REMOTE_ADDR']; |
||
1038 | $query .= isset($_POST['email']) ? '&email=' . $_POST['email'] : ''; |
||
1039 | $query .= isset($_POST['uname']) ? '&username=' . $_POST['uname'] : ''; |
||
1040 | $url = 'http://www.stopforumspam.com/api?' . $query; |
||
1041 | $ch = curl_init(); |
||
1042 | curl_setopt($ch, CURLOPT_URL, $url); |
||
1043 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
||
1044 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); |
||
1045 | $result = unserialize(curl_exec($ch)); |
||
1046 | curl_close($ch); |
||
1047 | |||
1048 | $spammer = false; |
||
1049 | if (isset($result['email']) && isset($result['email']['lastseen'])) { |
||
1050 | $spammer = true; |
||
1051 | } |
||
1052 | |||
1053 | if (isset($result['ip']) && isset($result['ip']['lastseen'])) { |
||
1054 | $last = strtotime($result['ip']['lastseen']); |
||
1055 | $oneMonth = 60 * 60 * 24 * 31; |
||
1056 | $oneMonthAgo = time() - $oneMonth; |
||
1057 | if ($last > $oneMonthAgo) { |
||
1058 | $spammer = true; |
||
1059 | } |
||
1060 | } |
||
1061 | |||
1062 | if (!$spammer) { |
||
1063 | return false; |
||
1064 | } |
||
1065 | |||
1066 | $this->last_error_type = 'SPAMMER POST'; |
||
1067 | |||
1068 | switch ($this->_conf['stopforumspam_action']) { |
||
1069 | default : |
||
1070 | case 'log' : |
||
1071 | break; |
||
1072 | case 'san' : |
||
1073 | $_POST = array(); |
||
1074 | $this->message .= 'POST deleted for IP:' . $_SERVER['REMOTE_ADDR']; |
||
1075 | break; |
||
1076 | case 'biptime0' : |
||
1077 | $_POST = array(); |
||
1078 | $this->message .= 'BAN and POST deleted for IP:' . $_SERVER['REMOTE_ADDR']; |
||
1079 | $this->_should_be_banned_time0 = true; |
||
1080 | break; |
||
1081 | case 'bip' : |
||
1082 | $_POST = array(); |
||
1083 | $this->message .= 'Ban and POST deleted for IP:' . $_SERVER['REMOTE_ADDR']; |
||
1084 | $this->_should_be_banned = true; |
||
1085 | break; |
||
1086 | } |
||
1087 | |||
1088 | $this->output_log($this->last_error_type, $uid, false, 16); |
||
1089 | |||
1090 | return true; |
||
1091 | } |
||
1092 | |||
1093 | /** |
||
1094 | * @param int $uid |
||
1095 | * @param bool $can_ban |
||
1096 | * |
||
1097 | * @return bool |
||
1098 | */ |
||
1099 | public function check_dos_attack($uid = 0, $can_ban = false) |
||
1252 | |||
1253 | // |
||
1254 | /** |
||
1255 | * @return bool|null |
||
1256 | */ |
||
1257 | public function check_brute_force() |
||
1258 | { |
||
1259 | global $xoopsDB; |
||
1260 | |||
1261 | $ip = \Xmf\IPAddress::fromRequest(); |
||
1262 | if (false === $ip->asReadable()) { |
||
1263 | return true; |
||
1264 | } |
||
1265 | $uri = @$_SERVER['REQUEST_URI']; |
||
1266 | $ip4sql = $xoopsDB->quote($ip->asReadable()); |
||
1267 | $uri4sql = $xoopsDB->quote($uri); |
||
1268 | |||
1269 | $victim_uname = empty($_COOKIE['autologin_uname']) ? $_POST['uname'] : $_COOKIE['autologin_uname']; |
||
1270 | // some UA send 'deleted' as a value of the deleted cookie. |
||
1271 | if ($victim_uname === 'deleted') { |
||
1272 | return null; |
||
1273 | } |
||
1274 | $mal4sql = $xoopsDB->quote("BRUTE FORCE: $victim_uname"); |
||
1275 | |||
1276 | // gargage collection |
||
1277 | $result = $xoopsDB->queryF( |
||
1278 | 'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . ' WHERE expire < UNIX_TIMESTAMP()' |
||
1279 | ); |
||
1280 | |||
1281 | // sql for recording access log (INSERT should be placed after SELECT) |
||
1282 | $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access') |
||
1283 | . " SET ip={$ip4sql}, request_uri={$uri4sql}, malicious_actions={$mal4sql}, expire=UNIX_TIMESTAMP()+600"; |
||
1284 | |||
1285 | // count check |
||
1286 | $result = $xoopsDB->query( |
||
1287 | 'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') |
||
1288 | . " WHERE ip={$ip4sql} AND malicious_actions like 'BRUTE FORCE:%'" |
||
1289 | ); |
||
1290 | list($bf_count) = $xoopsDB->fetchRow($result); |
||
1291 | if ($bf_count > $this->_conf['bf_count']) { |
||
1292 | $this->register_bad_ips(time() + $this->_conf['banip_time0']); |
||
1293 | $this->last_error_type = 'BruteForce'; |
||
1294 | $this->message .= "Trying to login as '" . addslashes($victim_uname) . "' found.\n"; |
||
1295 | $this->output_log('BRUTE FORCE', 0, true, 1); |
||
1296 | $ret = $this->call_filter('bruteforce_overrun'); |
||
1297 | if ($ret == false) { |
||
1298 | exit; |
||
1299 | } |
||
1300 | } |
||
1301 | // delayed insert |
||
1302 | $xoopsDB->queryF($sql4insertlog); |
||
1303 | return null; |
||
1304 | } |
||
1305 | |||
1306 | /** |
||
1307 | * @param $val |
||
1308 | */ |
||
1309 | protected function _spam_check_point_recursive($val) |
||
1335 | |||
1336 | /** |
||
1337 | * @param $points4deny |
||
1338 | * @param $uid |
||
1339 | */ |
||
1340 | public function spam_check($points4deny, $uid) |
||
1354 | |||
1355 | public function disable_features() |
||
1446 | |||
1447 | /** |
||
1448 | * @param $type |
||
1449 | * @param string $dying_message |
||
1450 | * |
||
1451 | * @return int|mixed |
||
1452 | */ |
||
1453 | public function call_filter($type, $dying_message = '') |
||
1464 | } |
||
1465 |
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.