Completed
Push — master ( 99d70f...f49934 )
by Michael
12:30
created

Protector::getConf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Class Protector
5
 */
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
        $this->purgeNoExit();
215
216
        if ($redirect_to_top) {
217
            header('Location: ' . XOOPS_URL . '/');
218
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method purge() contains an exit expression.

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.

Loading history...
219
        } else {
220
            $ret = $this->call_filter('prepurge_exit');
221
            if ($ret == false) {
222
                die('Protector detects attacking actions');
0 ignored issues
show
Coding Style Compatibility introduced by
The method purge() contains an exit expression.

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.

Loading history...
223
            }
224
        }
225
    }
226
227
    public function purgeSession()
228
    {
229
        // clear all session values
230
        if (isset($_SESSION)) {
231
            foreach ($_SESSION as $key => $val) {
232
                $_SESSION[$key] = '';
233
                if (isset($GLOBALS[$key])) {
234
                    $GLOBALS[$key] = '';
235
                }
236
            }
237
        }
238
    }
239
240
    public function purgeCookies()
241
    {
242
        if (!headers_sent()) {
243
            // clear typical session id of PHP
244
            setcookie('PHPSESSID', '', time() - 3600, '/', '', 0);
245
            if (isset($_COOKIE[session_name()])) {
246
                setcookie(session_name(), '', time() - 3600, '/', '', 0);
247
            }
248
249
            // clear autologin cookies
250
            $xoops_cookie_path = defined('XOOPS_COOKIE_PATH') ? XOOPS_COOKIE_PATH : preg_replace('?http://[^/]+(/.*)$?', "$1", XOOPS_URL);
251
            if ($xoops_cookie_path == XOOPS_URL) {
252
                $xoops_cookie_path = '/';
253
            }
254
            setcookie($GLOBALS['xoopsConfig']['usercookie'], 0, -1, '/', XOOPS_COOKIE_DOMAIN, 0);
255
            setcookie($GLOBALS['xoopsConfig']['usercookie'], 0, -1, '/');
256
257
            setcookie('autologin_uname', '', time() - 3600, $xoops_cookie_path, '', 0);
258
            setcookie('autologin_pass', '', time() - 3600, $xoops_cookie_path, '', 0);
259
        }
260
    }
261
262
    public function purgeNoExit()
263
    {
264
        $this->purgeSession();
265
        $this->purgeCookies();
266
    }
267
268
    public function deactivateCurrentUser()
269
    {
270
        /** @var XoopsUser $xoopsUser */
271
        global $xoopsUser;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
272
273
        if (is_object($xoopsUser)) {
274
            /** @var XoopsMemberHandler */
275
            $userHandler = xoops_getHandler('user');
276
            $xoopsUser->setVar('level', 0);
277
            $actkey = substr(md5(uniqid(mt_rand(), 1)), 0, 8);
278
            $xoopsUser->setVar('actkey', $actkey);
279
            $userHandler->insert($xoopsUser);
280
        }
281
        $this->purgeNoExit();
282
    }
283
284
    /**
285
     * @param string $type
286
     * @param int    $uid
287
     * @param bool   $unique_check
288
     * @param int    $level
289
     *
290
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
291
     */
292
    public function output_log($type = 'UNKNOWN', $uid = 0, $unique_check = false, $level = 1)
293
    {
294
        if ($this->_logged) {
295
            return true;
296
        }
297
298
        if (!($this->_conf['log_level'] & $level)) {
299
            return true;
300
        }
301
302
        if (empty($this->_conn)) {
303
            $this->_conn = new mysqli(XOOPS_DB_HOST, XOOPS_DB_USER, XOOPS_DB_PASS);
304
            if (0 !== $this->_conn->connect_errno) {
305
                die('db connection failed.');
0 ignored issues
show
Coding Style Compatibility introduced by
The method output_log() contains an exit expression.

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.

Loading history...
306
            }
307
            if (!mysqli_select_db($this->_conn, XOOPS_DB_NAME)) {
308
                die('db selection failed.');
0 ignored issues
show
Coding Style Compatibility introduced by
The method output_log() contains an exit expression.

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.

Loading history...
309
            }
310
        }
311
312
        $ip    = \Xmf\IPAddress::fromRequest()->asReadable();
313
        $agent = @$_SERVER['HTTP_USER_AGENT'];
314
315
        if ($unique_check) {
316
            $result = mysqli_query($this->_conn, 'SELECT ip,type FROM ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . '_log ORDER BY timestamp DESC LIMIT 1');
317
            list($last_ip, $last_type) = mysqli_fetch_row($result);
318
            if ($last_ip == $ip && $last_type == $type) {
319
                $this->_logged = true;
320
321
                return true;
322
            }
323
        }
324
325
        mysqli_query(
326
            $this->_conn,
327
            'INSERT INTO ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . "_log SET ip='"
328
            . mysqli_real_escape_string($this->_conn, $ip) . "',agent='"
329
            . mysqli_real_escape_string($this->_conn, $agent) . "',type='"
330
            . mysqli_real_escape_string($this->_conn, $type) . "',description='"
331
            . mysqli_real_escape_string($this->_conn, $this->message) . "',uid='"
332
            . (int)$uid . "',timestamp=NOW()"
333
        );
334
        $this->_logged = true;
335
336
        return true;
337
    }
338
339
    /**
340
     * @param $expire
341
     *
342
     * @return bool
343
     */
344 View Code Duplication
    public function write_file_bwlimit($expire)
345
    {
346
        $expire = min((int)$expire, time() + 300);
347
348
        $fp = @fopen($this->get_filepath4bwlimit(), 'w');
349
        if ($fp) {
350
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
351
            fwrite($fp, $expire . "\n");
352
            @flock($fp, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
353
            fclose($fp);
354
355
            return true;
356
        } else {
357
            return false;
358
        }
359
    }
360
361
    /**
362
     * @return mixed
363
     */
364
    public function get_bwlimit()
365
    {
366
        list($expire) = @file(Protector::get_filepath4bwlimit());
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
367
        $expire = min((int)$expire, time() + 300);
368
369
        return $expire;
370
    }
371
372
    /**
373
     * @return string
374
     */
375
    public static function get_filepath4bwlimit()
376
    {
377
        return XOOPS_TRUST_PATH . '/modules/protector/configs/bwlimit' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
378
    }
379
380
    /**
381
     * @param $bad_ips
382
     *
383
     * @return bool
384
     */
385 View Code Duplication
    public function write_file_badips($bad_ips)
386
    {
387
        asort($bad_ips);
388
389
        $fp = @fopen($this->get_filepath4badips(), 'w');
390
        if ($fp) {
391
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
392
            fwrite($fp, serialize($bad_ips) . "\n");
393
            @flock($fp, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
394
            fclose($fp);
395
396
            return true;
397
        } else {
398
            return false;
399
        }
400
    }
401
402
    /**
403
     * @param int  $jailed_time
404
     * @param null|string|false $ip
405
     *
406
     * @return bool
407
     */
408
    public function register_bad_ips($jailed_time = 0, $ip = null)
409
    {
410
        if (empty($ip)) {
411
            $ip = \Xmf\IPAddress::fromRequest()->asReadable();
412
        }
413
        if (empty($ip)) {
414
            return false;
415
        }
416
417
        $bad_ips      = $this->get_bad_ips(true);
418
        $bad_ips[$ip] = $jailed_time ?: 0x7fffffff;
419
420
        return $this->write_file_badips($bad_ips);
421
    }
422
423
    /**
424
     * @param bool $with_jailed_time
425
     *
426
     * @return array|mixed
427
     */
428
    public function get_bad_ips($with_jailed_time = false)
429
    {
430
        list($bad_ips_serialized) = @file(Protector::get_filepath4badips());
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
431
        $bad_ips = empty($bad_ips_serialized) ? array() : @unserialize($bad_ips_serialized);
432
        if (!is_array($bad_ips) || isset($bad_ips[0])) {
433
            $bad_ips = array();
434
        }
435
436
        // expire jailed_time
437
        $pos = 0;
438
        foreach ($bad_ips as $bad_ip => $jailed_time) {
439
            if ($jailed_time >= time()) {
440
                break;
441
            }
442
            ++$pos;
443
        }
444
        $bad_ips = array_slice($bad_ips, $pos);
445
446
        if ($with_jailed_time) {
447
            return $bad_ips;
448
        } else {
449
            return array_keys($bad_ips);
450
        }
451
    }
452
453
    /**
454
     * @return string
455
     */
456
    public static function get_filepath4badips()
457
    {
458
        return XOOPS_TRUST_PATH . '/modules/protector/configs/badips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
459
    }
460
461
    /**
462
     * @param bool $with_info
463
     *
464
     * @return array|mixed
465
     */
466
    public function get_group1_ips($with_info = false)
467
    {
468
        list($group1_ips_serialized) = @file(Protector::get_filepath4group1ips());
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
469
        $group1_ips = empty($group1_ips_serialized) ? array() : @unserialize($group1_ips_serialized);
470
        if (!is_array($group1_ips)) {
471
            $group1_ips = array();
472
        }
473
474
        if ($with_info) {
475
            $group1_ips = array_flip($group1_ips);
476
        }
477
478
        return $group1_ips;
479
    }
480
481
    /**
482
     * @return string
483
     */
484
    public static function get_filepath4group1ips()
485
    {
486
        return XOOPS_TRUST_PATH . '/modules/protector/configs/group1ips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
487
    }
488
489
    /**
490
     * @return string
491
     */
492
    public function get_filepath4confighcache()
493
    {
494
        return XOOPS_TRUST_PATH . '/modules/protector/configs/configcache' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
495
    }
496
497
    /**
498
     * @param $ips
499
     *
500
     * @return bool
501
     */
502
    public function ip_match($ips)
503
    {
504
        $requestIp = \Xmf\IPAddress::fromRequest()->asReadable();
505
        if (false === $requestIp) { // nothing to match
506
            $this->ip_matched_info = null;
507
            return false;
508
        }
509
        foreach ($ips as $ip => $info) {
510
            if ($ip) {
511
                switch (strtolower(substr($ip, -1))) {
512
                    case '.' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
513
                    case ':' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
514
                        // foward match
515
                        if (substr($requestIp, 0, strlen($ip)) == $ip) {
516
                            $this->ip_matched_info = $info;
517
                            return true;
518
                        }
519
                        break;
520
                    case '0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
521
                    case '1' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
522
                    case '2' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
523
                    case '3' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
524
                    case '4' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
525
                    case '5' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
526
                    case '6' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
527
                    case '7' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
528
                    case '8' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
529
                    case '9' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
530
                    case 'a' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
531
                    case 'b' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
532
                    case 'c' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
533
                    case 'd' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
534
                    case 'e' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
535
                    case 'f' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
536
                        // full match
537
                        if ($requestIp == $ip) {
538
                            $this->ip_matched_info = $info;
539
                            return true;
540
                        }
541
                        break;
542
                    default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
543
                        // perl regex
544
                        if (@preg_match($ip, $requestIp)) {
545
                            $this->ip_matched_info = $info;
546
                            return true;
547
                        }
548
                        break;
549
                }
550
            }
551
        }
552
        $this->ip_matched_info = null;
553
        return false;
554
    }
555
556
    /**
557
     * @param null|string|false $ip
558
     *
559
     * @return bool
560
     */
561
    public function deny_by_htaccess($ip = null)
562
    {
563
        if (empty($ip)) {
564
            $ip = \Xmf\IPAddress::fromRequest()->asReadable();
565
        }
566
        if (empty($ip)) {
567
            return false;
568
        }
569
        if (!function_exists('file_get_contents')) {
570
            return false;
571
        }
572
573
        $target_htaccess = XOOPS_ROOT_PATH . '/.htaccess';
574
        $backup_htaccess = XOOPS_ROOT_PATH . '/uploads/.htaccess.bak';
575
576
        $ht_body = file_get_contents($target_htaccess);
577
578
        // make backup as uploads/.htaccess.bak automatically
579
        if ($ht_body && !file_exists($backup_htaccess)) {
580
            $fw = fopen($backup_htaccess, 'w');
581
            fwrite($fw, $ht_body);
582
            fclose($fw);
583
        }
584
585
        // if .htaccess is broken, restore from backup
586
        if (!$ht_body && file_exists($backup_htaccess)) {
587
            $ht_body = file_get_contents($backup_htaccess);
588
        }
589
590
        // new .htaccess
591
        if ($ht_body === false) {
592
            $ht_body = '';
593
        }
594
595
        if (preg_match("/^(.*)#PROTECTOR#\s+(DENY FROM .*)\n#PROTECTOR#\n(.*)$/si", $ht_body, $regs)) {
596
            if (substr($regs[2], -strlen($ip)) == $ip) {
597
                return true;
598
            }
599
            $new_ht_body = $regs[1] . "#PROTECTOR#\n" . $regs[2] . " $ip\n#PROTECTOR#\n" . $regs[3];
600
        } else {
601
            $new_ht_body = "#PROTECTOR#\nDENY FROM $ip\n#PROTECTOR#\n" . $ht_body;
602
        }
603
604
        // error_log( "$new_ht_body\n" , 3 , "/tmp/error_log" ) ;
605
606
        $fw = fopen($target_htaccess, 'w');
607
        @flock($fw, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
608
        fwrite($fw, $new_ht_body);
609
        @flock($fw, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
610
        fclose($fw);
611
612
        return true;
613
    }
614
615
    /**
616
     * @return array
617
     */
618
    public function getDblayertrapDoubtfuls()
619
    {
620
        return $this->_dblayertrap_doubtfuls;
621
    }
622
623
    /**
624
     * @param $val
625
     * @return null
626
     */
627
    protected function _dblayertrap_check_recursive($val)
628
    {
629
        if (is_array($val)) {
630
            foreach ($val as $subval) {
631
                $this->_dblayertrap_check_recursive($subval);
632
            }
633
        } else {
634
            if (strlen($val) < 6) {
635
                return null;
636
            }
637
            $val = get_magic_quotes_gpc() ? stripslashes($val) : $val;
638
            foreach ($this->_dblayertrap_doubtful_needles as $needle) {
639
                if (false !== stripos($val, $needle)) {
640
                    $this->_dblayertrap_doubtfuls[] = $val;
641
                }
642
            }
643
        }
644
    }
645
646
    /**
647
     * @param  bool $force_override
648
     * @return null
649
     */
650
    public function dblayertrap_init($force_override = false)
651
    {
652
        if (!empty($GLOBALS['xoopsOption']['nocommon']) || defined('_LEGACY_PREVENT_EXEC_COMMON_') || defined('_LEGACY_PREVENT_LOAD_CORE_')) {
653
            return null;
654
        } // skip
655
656
        $this->_dblayertrap_doubtfuls = array();
657
        $this->_dblayertrap_check_recursive($_GET);
658
        $this->_dblayertrap_check_recursive($_POST);
659
        $this->_dblayertrap_check_recursive($_COOKIE);
660
        if (empty($this->_conf['dblayertrap_wo_server'])) {
661
            $this->_dblayertrap_check_recursive($_SERVER);
662
        }
663
664
        if (!empty($this->_dblayertrap_doubtfuls) || $force_override) {
665
            @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
666
            require_once dirname(__DIR__) . '/class/ProtectorMysqlDatabase.class.php';
667
        }
668
    }
669
670
    /**
671
     * @param $val
672
     */
673
    protected function _bigumbrella_check_recursive($val)
674
    {
675
        if (is_array($val)) {
676
            foreach ($val as $subval) {
677
                $this->_bigumbrella_check_recursive($subval);
678
            }
679
        } else {
680
            if (preg_match('/[<\'"].{15}/s', $val, $regs)) {
681
                $this->_bigumbrella_doubtfuls[] = $regs[0];
682
            }
683
        }
684
    }
685
686
    public function bigumbrella_init()
687
    {
688
        $this->_bigumbrella_doubtfuls = array();
689
        $this->_bigumbrella_check_recursive($_GET);
690
        $this->_bigumbrella_check_recursive(@$_SERVER['PHP_SELF']);
691
692
        if (!empty($this->_bigumbrella_doubtfuls)) {
693
            ob_start(array($this, 'bigumbrella_outputcheck'));
694
        }
695
    }
696
697
    /**
698
     * @param $s
699
     *
700
     * @return string
701
     */
702
    public function bigumbrella_outputcheck($s)
703
    {
704
        if (defined('BIGUMBRELLA_DISABLED')) {
705
            return $s;
706
        }
707
708
        if (function_exists('headers_list')) {
709
            foreach (headers_list() as $header) {
710
                if (false !== stripos($header, 'Content-Type:') && false === stripos($header, 'text/html')) {
711
                    return $s;
712
                }
713
            }
714
        }
715
716
        if (!is_array($this->_bigumbrella_doubtfuls)) {
717
            return 'bigumbrella injection found.';
718
        }
719
720
        foreach ($this->_bigumbrella_doubtfuls as $doubtful) {
721
            if (false !== strpos($s, $doubtful)) {
722
                return 'XSS found by Protector.';
723
            }
724
        }
725
726
        return $s;
727
    }
728
729
    /**
730
     * @return bool
731
     */
732
    public function intval_allrequestsendid()
733
    {
734
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
735
736
        if ($this->_done_intval) {
737
            return true;
738
        } else {
739
            $this->_done_intval = true;
740
        }
741
742 View Code Duplication
        foreach ($_GET as $key => $val) {
743
            if (substr($key, -2) === 'id' && !is_array($_GET[$key])) {
744
                $newval     = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
745
                $_GET[$key] = $HTTP_GET_VARS[$key] = $newval;
746
                if ($_REQUEST[$key] == $_GET[$key]) {
747
                    $_REQUEST[$key] = $newval;
748
                }
749
            }
750
        }
751 View Code Duplication
        foreach ($_POST as $key => $val) {
752
            if (substr($key, -2) === 'id' && !is_array($_POST[$key])) {
753
                $newval      = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
754
                $_POST[$key] = $HTTP_POST_VARS[$key] = $newval;
755
                if ($_REQUEST[$key] == $_POST[$key]) {
756
                    $_REQUEST[$key] = $newval;
757
                }
758
            }
759
        }
760 View Code Duplication
        foreach ($_COOKIE as $key => $val) {
761
            if (substr($key, -2) === 'id' && !is_array($_COOKIE[$key])) {
762
                $newval        = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
763
                $_COOKIE[$key] = $HTTP_COOKIE_VARS[$key] = $newval;
764
                if ($_REQUEST[$key] == $_COOKIE[$key]) {
765
                    $_REQUEST[$key] = $newval;
766
                }
767
            }
768
        }
769
770
        return true;
771
    }
772
773
    /**
774
     * @return bool
775
     */
776
    public function eliminate_dotdot()
777
    {
778
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
779
780
        if ($this->_done_dotdot) {
781
            return true;
782
        } else {
783
            $this->_done_dotdot = true;
784
        }
785
786
        foreach ($_GET as $key => $val) {
787
            if (is_array($_GET[$key])) {
788
                continue;
789
            }
790
            if (substr(trim($val), 0, 3) === '../' || false !== strpos($val, '/../')) {
791
                $this->last_error_type = 'DirTraversal';
792
                $this->message .= "Directory Traversal '$val' found.\n";
793
                $this->output_log($this->last_error_type, 0, false, 64);
794
                $sanitized_val = str_replace(chr(0), '', $val);
795
                if (substr($sanitized_val, -2) !== ' .') {
796
                    $sanitized_val .= ' .';
797
                }
798
                $_GET[$key] = $HTTP_GET_VARS[$key] = $sanitized_val;
799
                if ($_REQUEST[$key] == $_GET[$key]) {
800
                    $_REQUEST[$key] = $sanitized_val;
801
                }
802
            }
803
        }
804
805
        /*    foreach ($_POST as $key => $val) {
806
                if( is_array( $_POST[ $key ] ) ) continue ;
807
                if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
808
                    $this->last_error_type = 'ParentDir' ;
809
                    $this->message .= "Doubtful file specification '$val' found.\n" ;
810
                    $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
811
                    $sanitized_val = str_replace( chr(0) , '' , $val ) ;
812
                    if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
813
                    $_POST[ $key ] = $HTTP_POST_VARS[ $key ] = $sanitized_val ;
814
                    if ($_REQUEST[ $key ] == $_POST[ $key ]) {
815
                        $_REQUEST[ $key ] = $sanitized_val ;
816
                    }
817
                }
818
            }
819
            foreach ($_COOKIE as $key => $val) {
820
                if( is_array( $_COOKIE[ $key ] ) ) continue ;
821
                if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
822
                    $this->last_error_type = 'ParentDir' ;
823
                    $this->message .= "Doubtful file specification '$val' found.\n" ;
824
                    $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
825
                    $sanitized_val = str_replace( chr(0) , '' , $val ) ;
826
                    if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
827
                    $_COOKIE[ $key ] = $HTTP_COOKIE_VARS[ $key ] = $sanitized_val ;
828
                    if ($_REQUEST[ $key ] == $_COOKIE[ $key ]) {
829
                        $_REQUEST[ $key ] = $sanitized_val ;
830
                    }
831
                }
832
            }*/
833
834
        return true;
835
    }
836
837
    /**
838
     * @param $current
839
     * @param $indexes
840
     *
841
     * @return bool
842
     */
843
    public function &get_ref_from_base64index(&$current, $indexes)
844
    {
845
        foreach ($indexes as $index) {
846
            $index = base64_decode($index);
847
            if (!is_array($current)) {
848
                return false;
849
            }
850
            $current =& $current[$index];
851
        }
852
853
        return $current;
854
    }
855
856
    /**
857
     * @param $key
858
     * @param $val
859
     */
860
    public function replace_doubtful($key, $val)
861
    {
862
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
863
864
        $index_expression = '';
0 ignored issues
show
Unused Code introduced by
$index_expression is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
865
        $indexes          = explode('_', $key);
866
        $base_array       = array_shift($indexes);
867
868
        switch ($base_array) {
869
            case 'G' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
870
                $main_ref   =& $this->get_ref_from_base64index($_GET, $indexes);
871
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_GET_VARS, $indexes);
872
                break;
873
            case 'P' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
874
                $main_ref   =& $this->get_ref_from_base64index($_POST, $indexes);
875
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_POST_VARS, $indexes);
876
                break;
877
            case 'C' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
878
                $main_ref   =& $this->get_ref_from_base64index($_COOKIE, $indexes);
879
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_COOKIE_VARS, $indexes);
880
                break;
881
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
882
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method replace_doubtful() contains an exit expression.

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.

Loading history...
883
        }
884
        if (!isset($main_ref)) {
885
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method replace_doubtful() contains an exit expression.

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.

Loading history...
886
        }
887
        $request_ref =& $this->get_ref_from_base64index($_REQUEST, $indexes);
888
        if ($request_ref !== false && $main_ref == $request_ref) {
889
            $request_ref = $val;
890
        }
891
        $main_ref   = $val;
892
        $legacy_ref = $val;
893
    }
894
895
    /**
896
     * @return bool
897
     */
898
    public function check_uploaded_files()
899
    {
900
        if ($this->_done_badext) {
901
            return $this->_safe_badext;
902
        } else {
903
            $this->_done_badext = true;
904
        }
905
906
        // extensions never uploaded
907
        $bad_extensions = array('php', 'phtml', 'phtm', 'php3', 'php4', 'cgi', 'pl', 'asp');
908
        // extensions needed image check (anti-IE Content-Type XSS)
909
        $image_extensions = array(
910
            1  => 'gif',
911
            2  => 'jpg',
912
            3  => 'png',
913
            4  => 'swf',
914
            5  => 'psd',
915
            6  => 'bmp',
916
            7  => 'tif',
917
            8  => 'tif',
918
            9  => 'jpc',
919
            10 => 'jp2',
920
            11 => 'jpx',
921
            12 => 'jb2',
922
            13 => 'swc',
923
            14 => 'iff',
924
            15 => 'wbmp',
925
            16 => 'xbm');
926
927
        foreach ($_FILES as $_file) {
928
            if (!empty($_file['error'])) {
929
                continue;
930
            }
931
            if (!empty($_file['name']) && is_string($_file['name'])) {
932
                $ext = strtolower(substr(strrchr($_file['name'], '.'), 1));
933
                if ($ext === 'jpeg') {
934
                    $ext = 'jpg';
935
                } elseif ($ext === 'tiff') {
936
                    $ext = 'tif';
937
                }
938
939
                // anti multiple dot file (Apache mod_mime.c)
940
                if (count(explode('.', str_replace('.tar.gz', '.tgz', $_file['name']))) > 2) {
941
                    $this->message .= "Attempt to multiple dot file {$_file['name']}.\n";
942
                    $this->_safe_badext    = false;
943
                    $this->last_error_type = 'UPLOAD';
944
                }
945
946
                // anti dangerous extensions
947 View Code Duplication
                if (in_array($ext, $bad_extensions)) {
948
                    $this->message .= "Attempt to upload {$_file['name']}.\n";
949
                    $this->_safe_badext    = false;
950
                    $this->last_error_type = 'UPLOAD';
951
                }
952
953
                // anti camouflaged image file
954
                if (in_array($ext, $image_extensions)) {
955
                    $image_attributes = @getimagesize($_file['tmp_name']);
956
                    if ($image_attributes === false && is_uploaded_file($_file['tmp_name'])) {
957
                        // open_basedir restriction
958
                        $temp_file = XOOPS_ROOT_PATH . '/uploads/protector_upload_temporary' . md5(time());
959
                        move_uploaded_file($_file['tmp_name'], $temp_file);
960
                        $image_attributes = @getimagesize($temp_file);
961
                        @unlink($temp_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
962
                    }
963
964
                    if ($image_attributes === false || $image_extensions[(int)$image_attributes[2]] != $ext) {
965
                        $this->message .= "Attempt to upload camouflaged image file {$_file['name']}.\n";
966
                        $this->_safe_badext    = false;
967
                        $this->last_error_type = 'UPLOAD';
968
                    }
969
                }
970
            }
971
        }
972
973
        return $this->_safe_badext;
974
    }
975
976
    /**
977
     * @return bool
978
     */
979
    public function check_contami_systemglobals()
980
    {
981
        /*    if( $this->_done_contami ) return $this->_safe_contami ;
982
    else $this->_done_contami = true ; */
983
984
        /*    foreach ($this->_bad_globals as $bad_global) {
985
                if ( isset( $_REQUEST[ $bad_global ] ) ) {
986
                    $this->message .= "Attempt to inject '$bad_global' was found.\n" ;
987
                    $this->_safe_contami = false ;
988
                    $this->last_error_type = 'CONTAMI' ;
989
                }
990
            }*/
991
992
        return $this->_safe_contami;
993
    }
994
995
    /**
996
     * @param bool $sanitize
997
     *
998
     * @return bool
999
     */
1000
    public function check_sql_isolatedcommentin($sanitize = true)
1001
    {
1002
        if ($this->_done_isocom) {
1003
            return $this->_safe_isocom;
1004
        } else {
1005
            $this->_done_isocom = true;
1006
        }
1007
1008
        foreach ($this->_doubtful_requests as $key => $val) {
1009
            $str = $val;
1010
            while ($str = strstr($str, '/*')) { /* */
1011
                $str = strstr(substr($str, 2), '*/');
1012
                if ($str === false) {
1013
                    $this->message .= "Isolated comment-in found. ($val)\n";
1014
                    if ($sanitize) {
1015
                        $this->replace_doubtful($key, $val . '*/');
1016
                    }
1017
                    $this->_safe_isocom    = false;
1018
                    $this->last_error_type = 'ISOCOM';
1019
                }
1020
            }
1021
        }
1022
1023
        return $this->_safe_isocom;
1024
    }
1025
1026
    /**
1027
     * @param bool $sanitize
1028
     *
1029
     * @return bool
1030
     */
1031
    public function check_sql_union($sanitize = true)
1032
    {
1033
        if ($this->_done_union) {
1034
            return $this->_safe_union;
1035
        } else {
1036
            $this->_done_union = true;
1037
        }
1038
1039
        foreach ($this->_doubtful_requests as $key => $val) {
1040
            $str = str_replace(array('/*', '*/'), '', preg_replace('?/\*.+\*/?sU', '', $val));
1041
            if (preg_match('/\sUNION\s+(ALL|SELECT)/i', $str)) {
1042
                $this->message .= "Pattern like SQL injection found. ($val)\n";
1043
                if ($sanitize) {
1044
                    //                    $this->replace_doubtful($key, preg_replace('/union/i', 'uni-on', $val));
1045
                    $this->replace_doubtful($key, str_ireplace('union', 'uni-on', $val));
1046
                }
1047
                $this->_safe_union     = false;
1048
                $this->last_error_type = 'UNION';
1049
            }
1050
        }
1051
1052
        return $this->_safe_union;
1053
    }
1054
1055
    /**
1056
     * @param $uid
1057
     *
1058
     * @return bool
1059
     */
1060
    public function stopforumspam($uid)
1061
    {
1062
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
1063
            return false;
1064
        }
1065
1066
        $result = $this->stopForumSpamLookup(
1067
            isset($_POST['email']) ? $_POST['email'] : null,
1068
            $_SERVER['REMOTE_ADDR'],
1069
            isset($_POST['uname']) ? $_POST['uname'] : null
1070
        );
1071
1072
        if (false === $result) {
1073
            return false;
1074
        }
1075
1076
        $spammer = false;
1077
        if (isset($result['email']) && isset($result['email']['lastseen'])) {
1078
            $spammer = true;
1079
        }
1080
1081
        if (isset($result['ip']) && isset($result['ip']['lastseen'])) {
1082
            $last        = strtotime($result['ip']['lastseen']);
1083
            $oneMonth    = 60 * 60 * 24 * 31;
1084
            $oneMonthAgo = time() - $oneMonth;
1085
            if ($last > $oneMonthAgo) {
1086
                $spammer = true;
1087
            }
1088
        }
1089
1090
        if (!$spammer) {
1091
            return false;
1092
        }
1093
1094
        $this->last_error_type = 'SPAMMER POST';
1095
1096
        switch ($this->_conf['stopforumspam_action']) {
1097
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1098
            case 'log' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1099
                break;
1100
            case 'san' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1101
                $_POST = array();
1102
                $this->message .= 'POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1103
                break;
1104
            case 'biptime0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1105
                $_POST = array();
1106
                $this->message .= 'BAN and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1107
                $this->_should_be_banned_time0 = true;
1108
                break;
1109
            case 'bip' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1110
                $_POST = array();
1111
                $this->message .= 'Ban and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1112
                $this->_should_be_banned = true;
1113
                break;
1114
        }
1115
1116
        $this->output_log($this->last_error_type, $uid, false, 16);
1117
1118
        return true;
1119
    }
1120
1121
    public function stopForumSpamLookup($email, $ip, $username)
1122
    {
1123
        if (!function_exists('curl_init')) {
1124
            return false;
1125
        }
1126
1127
        $query = '';
1128
        $query .= (empty($ip)) ? '' : '&ip=' . $ip;
1129
        $query .= (empty($email)) ? '' : '&email=' . $email;
1130
        $query .= (empty($username)) ? '' : '&username=' . $username;
1131
1132
        if (empty($query)) {
1133
            return false;
1134
        }
1135
1136
        $url = 'http://www.stopforumspam.com/api?f=json' . $query;
1137
        $ch  = curl_init();
1138
        curl_setopt($ch, CURLOPT_URL, $url);
1139
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1140
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
1141
        $result = json_decode(curl_exec($ch), true);
1142
        curl_close($ch);
1143
1144
        return $result;
1145
    }
1146
1147
    /**
1148
     * @param int  $uid
1149
     * @param bool $can_ban
1150
     *
1151
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1152
     */
1153
    public function check_dos_attack($uid = 0, $can_ban = false)
1154
    {
1155
        global $xoopsDB;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1156
1157
        if ($this->_done_dos) {
1158
            return true;
1159
        }
1160
1161
        $ip      = \Xmf\IPAddress::fromRequest();
1162
        if (false === $ip->asReadable()) {
1163
            return true;
1164
        }
1165
        $uri     = @$_SERVER['REQUEST_URI'];
1166
        $ip4sql  = $xoopsDB->quote($ip->asReadable());
1167
        $uri4sql = $xoopsDB->quote($uri);
1168
1169
        // gargage collection
1170
        $result = $xoopsDB->queryF(
1171
            'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1172
            . ' WHERE expire < UNIX_TIMESTAMP()'
1173
        );
1174
1175
        // for older versions before updating this module
1176
        if ($result === false) {
1177
            $this->_done_dos = true;
1178
1179
            return true;
1180
        }
1181
1182
        // sql for recording access log (INSERT should be placed after SELECT)
1183
        $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
1184
                         . " SET ip={$ip4sql}, request_uri={$uri4sql},"
1185
                         . " expire=UNIX_TIMESTAMP()+'" . (int)$this->_conf['dos_expire'] . "'";
1186
1187
        // bandwidth limitation
1188
        if (@$this->_conf['bwlimit_count'] >= 10) {
1189
            $result = $xoopsDB->query('SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access'));
1190
            list($bw_count) = $xoopsDB->fetchRow($result);
1191
            if ($bw_count > $this->_conf['bwlimit_count']) {
1192
                $this->write_file_bwlimit(time() + $this->_conf['dos_expire']);
1193
            }
1194
        }
1195
1196
        // F5 attack check (High load & same URI)
1197
        $result = $xoopsDB->query(
1198
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1199
            . " WHERE ip={$ip4sql} AND request_uri={$uri4sql}");
1200
        list($f5_count) = $xoopsDB->fetchRow($result);
1201 View Code Duplication
        if ($f5_count > $this->_conf['dos_f5count']) {
1202
1203
            // delayed insert
1204
            $xoopsDB->queryF($sql4insertlog);
1205
1206
            // extends the expires of the IP with 5 minutes at least (pending)
1207
            // $result = $xoopsDB->queryF( "UPDATE ".$xoopsDB->prefix($this->mydirname.'_access')." SET expire=UNIX_TIMESTAMP()+300 WHERE ip='$ip4sql' AND expire<UNIX_TIMESTAMP()+300" ) ;
1208
1209
            // call the filter first
1210
            $ret = $this->call_filter('f5attack_overrun');
0 ignored issues
show
Unused Code introduced by
$ret is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1211
1212
            // actions for F5 Attack
1213
            $this->_done_dos       = true;
1214
            $this->last_error_type = 'DoS';
1215
            switch ($this->_conf['dos_f5action']) {
1216
                default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1217
                case 'exit' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1218
                    $this->output_log($this->last_error_type, $uid, true, 16);
1219
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method check_dos_attack() contains an exit expression.

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.

Loading history...
1220
                case 'none' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1221
                    $this->output_log($this->last_error_type, $uid, true, 16);
1222
1223
                    return true;
1224
                case 'biptime0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1225
                    if ($can_ban) {
1226
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1227
                    }
1228
                    break;
1229
                case 'bip' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1230
                    if ($can_ban) {
1231
                        $this->register_bad_ips();
1232
                    }
1233
                    break;
1234
                case 'hta' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1235
                    if ($can_ban) {
1236
                        $this->deny_by_htaccess();
1237
                    }
1238
                    break;
1239
                case 'sleep' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1240
                    sleep(5);
1241
                    break;
1242
            }
1243
1244
            return false;
1245
        }
1246
1247
        // Check its Agent
1248
        if (trim($this->_conf['dos_crsafe']) != '' && preg_match($this->_conf['dos_crsafe'], @$_SERVER['HTTP_USER_AGENT'])) {
1249
            // welcomed crawler
1250
            $this->_done_dos = true;
1251
1252
            return true;
1253
        }
1254
1255
        // Crawler check (High load & different URI)
1256
        $result = $xoopsDB->query(
1257
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . " WHERE ip={$ip4sql}"
1258
        );
1259
        list($crawler_count) = $xoopsDB->fetchRow($result);
1260
1261
        // delayed insert
1262
        $xoopsDB->queryF($sql4insertlog);
1263
1264 View Code Duplication
        if ($crawler_count > $this->_conf['dos_crcount']) {
1265
1266
            // call the filter first
1267
            $ret = $this->call_filter('crawler_overrun');
0 ignored issues
show
Unused Code introduced by
$ret is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1268
1269
            // actions for bad Crawler
1270
            $this->_done_dos       = true;
1271
            $this->last_error_type = 'CRAWLER';
1272
            switch ($this->_conf['dos_craction']) {
1273
                default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1274
                case 'exit' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1275
                    $this->output_log($this->last_error_type, $uid, true, 16);
1276
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method check_dos_attack() contains an exit expression.

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.

Loading history...
1277
                case 'none' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1278
                    $this->output_log($this->last_error_type, $uid, true, 16);
1279
1280
                    return true;
1281
                case 'biptime0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1282
                    if ($can_ban) {
1283
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1284
                    }
1285
                    break;
1286
                case 'bip' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1287
                    if ($can_ban) {
1288
                        $this->register_bad_ips();
1289
                    }
1290
                    break;
1291
                case 'hta' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1292
                    if ($can_ban) {
1293
                        $this->deny_by_htaccess();
1294
                    }
1295
                    break;
1296
                case 'sleep' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1297
                    sleep(5);
1298
                    break;
1299
            }
1300
1301
            return false;
1302
        }
1303
1304
        return true;
1305
    }
1306
1307
    //
1308
    /**
1309
     * @return bool|null
1310
     */
1311
    public function check_brute_force()
1312
    {
1313
        global $xoopsDB;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1314
1315
        $ip      = \Xmf\IPAddress::fromRequest();
1316
        if (false === $ip->asReadable()) {
1317
            return true;
1318
        }
1319
        $uri     = @$_SERVER['REQUEST_URI'];
1320
        $ip4sql  = $xoopsDB->quote($ip->asReadable());
1321
        $uri4sql = $xoopsDB->quote($uri);
1322
1323
        $victim_uname = empty($_COOKIE['autologin_uname']) ? $_POST['uname'] : $_COOKIE['autologin_uname'];
1324
        // some UA send 'deleted' as a value of the deleted cookie.
1325
        if ($victim_uname === 'deleted') {
1326
            return null;
1327
        }
1328
        $mal4sql = $xoopsDB->quote("BRUTE FORCE: $victim_uname");
1329
1330
        // gargage collection
1331
        $result = $xoopsDB->queryF(
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1332
            'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . ' WHERE expire < UNIX_TIMESTAMP()'
1333
        );
1334
1335
        // sql for recording access log (INSERT should be placed after SELECT)
1336
        $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
1337
                         . " SET ip={$ip4sql}, request_uri={$uri4sql}, malicious_actions={$mal4sql}, expire=UNIX_TIMESTAMP()+600";
1338
1339
        // count check
1340
        $result = $xoopsDB->query(
1341
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1342
            . " WHERE ip={$ip4sql} AND malicious_actions like 'BRUTE FORCE:%'"
1343
        );
1344
        list($bf_count) = $xoopsDB->fetchRow($result);
1345
        if ($bf_count > $this->_conf['bf_count']) {
1346
            $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1347
            $this->last_error_type = 'BruteForce';
1348
            $this->message .= "Trying to login as '" . addslashes($victim_uname) . "' found.\n";
1349
            $this->output_log('BRUTE FORCE', 0, true, 1);
1350
            $ret = $this->call_filter('bruteforce_overrun');
1351
            if ($ret == false) {
1352
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method check_brute_force() contains an exit expression.

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.

Loading history...
1353
            }
1354
        }
1355
        // delayed insert
1356
        $xoopsDB->queryF($sql4insertlog);
1357
        return null;
1358
    }
1359
1360
    /**
1361
     * @param $val
1362
     */
1363
    protected function _spam_check_point_recursive($val)
1364
    {
1365
        if (is_array($val)) {
1366
            foreach ($val as $subval) {
1367
                $this->_spam_check_point_recursive($subval);
1368
            }
1369
        } else {
1370
            // http_host
1371
            $path_array = parse_url(XOOPS_URL);
1372
            $http_host  = empty($path_array['host']) ? 'www.xoops.org' : $path_array['host'];
1373
1374
            // count URI up
1375
            $count = -1;
1376
            foreach (preg_split('#https?\:\/\/#i', $val) as $fragment) {
1377
                if (strncmp($fragment, $http_host, strlen($http_host)) !== 0) {
1378
                    ++$count;
1379
                }
1380
            }
1381
            if ($count > 0) {
1382
                $this->_spamcount_uri += $count;
1383
            }
1384
1385
            // count BBCode likd [url=www....] up (without [url=http://...])
1386
            $this->_spamcount_uri += count(preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
1387
        }
1388
    }
1389
1390
    /**
1391
     * @param $points4deny
1392
     * @param $uid
1393
     */
1394
    public function spam_check($points4deny, $uid)
1395
    {
1396
        $this->_spamcount_uri = 0;
1397
        $this->_spam_check_point_recursive($_POST);
1398
1399
        if ($this->_spamcount_uri >= $points4deny) {
1400
            $this->message .= @$_SERVER['REQUEST_URI'] . " SPAM POINT: $this->_spamcount_uri\n";
1401
            $this->output_log('URI SPAM', $uid, false, 128);
1402
            $ret = $this->call_filter('spamcheck_overrun');
1403
            if ($ret == false) {
1404
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method spam_check() contains an exit expression.

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.

Loading history...
1405
            }
1406
        }
1407
    }
1408
1409
    public function disable_features()
1410
    {
1411
        global $HTTP_POST_VARS, $HTTP_GET_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1412
1413
        // disable "Notice: Undefined index: ..."
1414
        $error_reporting_level = error_reporting(0);
1415
1416
        //
1417
        // bit 1 : disable XMLRPC , criteria bug
1418
        //
1419
        if ($this->_conf['disable_features'] & 1) {
1420
1421
            // zx 2005/1/5 disable xmlrpc.php in root
1422
            if (/* ! stristr( $_SERVER['SCRIPT_NAME'] , 'modules' ) && */
1423
                substr(@$_SERVER['SCRIPT_NAME'], -10) === 'xmlrpc.php'
1424
            ) {
1425
                $this->output_log('xmlrpc', 0, true, 1);
1426
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1427
            }
1428
1429
            // security bug of class/criteria.php 2005/6/27
1430
            if ($_POST['uname'] === '0' || $_COOKIE['autologin_pass'] === '0') {
1431
                $this->output_log('CRITERIA');
1432
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1433
            }
1434
        }
1435
1436
        //
1437
        // bit 11 : XSS+CSRFs in XOOPS < 2.0.10
1438
        //
1439
        if ($this->_conf['disable_features'] & 1024) {
1440
1441
            // root controllers
1442
            if (false === stripos(@$_SERVER['SCRIPT_NAME'], 'modules')) {
1443
                // zx 2004/12/13 misc.php debug (file check)
1444 View Code Duplication
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'debug' || $_POST['type'] === 'debug') && !preg_match('/^dummy_\d+\.html$/', $_GET['file'])) {
1445
                    $this->output_log('misc debug');
1446
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1447
                }
1448
1449
                // zx 2004/12/13 misc.php smilies
1450 View Code Duplication
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'smilies' || $_POST['type'] === 'smilies') && !preg_match('/^[0-9a-z_]*$/i', $_GET['target'])) {
1451
                    $this->output_log('misc smilies');
1452
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1453
                }
1454
1455
                // zx 2005/1/5 edituser.php avatarchoose
1456
                if (substr(@$_SERVER['SCRIPT_NAME'], -12) === 'edituser.php' && $_POST['op'] === 'avatarchoose' && false !== strpos($_POST['user_avatar'], '..')) {
1457
                    $this->output_log('edituser avatarchoose');
1458
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1459
                }
1460
            }
1461
1462
            // zx 2005/1/4 findusers
1463
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'findusers' || $_POST['fct'] === 'findusers')) {
1464
                foreach ($_POST as $key => $val) {
1465
                    if (false !== strpos($key, "'") || false !== strpos($val, "'")) {
1466
                        $this->output_log('findusers');
1467
                        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1468
                    }
1469
                }
1470
            }
1471
1472
            // preview CSRF zx 2004/12/14
1473
            // news submit.php
1474
            if (substr(@$_SERVER['SCRIPT_NAME'], -23) === 'modules/news/submit.php' && isset($_POST['preview']) && strpos(@$_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/submit.php') !== 0) {
1475
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1476
            }
1477
            // news admin/index.php
1478
            if (substr(@$_SERVER['SCRIPT_NAME'], -28) === 'modules/news/admin/index.php' && ($_POST['op'] === 'preview' || $_GET['op'] === 'preview') && strpos(@$_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/admin/index.php') !== 0) {
1479
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1480
            }
1481
            // comment comment_post.php
1482
            if (isset($_POST['com_dopreview']) && false === strpos(substr(@$_SERVER['HTTP_REFERER'], -16), 'comment_post.php')) {
1483
                $HTTP_POST_VARS['dohtml'] = $_POST['dohtml'] = 0;
1484
            }
1485
            // disable preview of system's blocksadmin
1486
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'blocksadmin' || $_POST['fct'] === 'blocksadmin') && isset($_POST['previewblock']) /* && strpos( $_SERVER['HTTP_REFERER'] , XOOPS_URL.'/modules/system/admin.php' ) !== 0 */) {
1487
                die("Danger! don't use this preview. Use 'altsys module' instead.(by Protector)");
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1488
            }
1489
            // tpl preview
1490
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'tplsets' || $_POST['fct'] === 'tplsets')) {
1491
                if ($_POST['op'] === 'previewpopup' || $_GET['op'] === 'previewpopup' || isset($_POST['previewtpl'])) {
1492
                    die("Danger! don't use this preview.(by Protector)");
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

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.

Loading history...
1493
                }
1494
            }
1495
        }
1496
1497
        // restore reporting level
1498
        error_reporting($error_reporting_level);
1499
    }
1500
1501
    /**
1502
     * @param        $type
1503
     * @param string $dying_message
1504
     *
1505
     * @return int|mixed
1506
     */
1507
    public function call_filter($type, $dying_message = '')
1508
    {
1509
        require_once __DIR__ . '/ProtectorFilter.php';
1510
        $filter_handler = ProtectorFilterHandler::getInstance();
1511
        $ret            = $filter_handler->execute($type);
1512
        if ($ret == false && $dying_message) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $ret of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
1513
            die($dying_message);
0 ignored issues
show
Coding Style Compatibility introduced by
The method call_filter() contains an exit expression.

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.

Loading history...
1514
        }
1515
1516
        return $ret;
1517
    }
1518
}
1519