Protector::write_file_badips()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 13
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
/**
13
 * Protector
14
 *
15
 * @copyright       XOOPS Project (http://xoops.org)
16
 * @license         GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
17
 * @package         protector
18
 * @author          trabis <[email protected]>
19
 * @version         $Id$
20
 */
21
22
class Protector
23
{
24
    var $mydirname;
25
26
    var $_conn = null;
27
28
    var $_conf = array();
29
30
    var $_conf_serialized = '';
31
32
    var $_bad_globals = array();
33
34
    var $message = '';
35
36
    var $warning = false;
37
38
    var $error = false;
39
40
    var $_doubtful_requests = array();
41
42
    var $_bigumbrella_doubtfuls = array();
43
44
    var $_dblayertrap_doubtfuls = array();
45
46
    var $_dblayertrap_doubtful_needles = array(
47
        'information_schema', 'select', "'", '"',
48
    );
49
50
    var $_logged = false;
51
52
    var $_done_badext = false;
53
54
    var $_done_intval = false;
55
56
    var $_done_dotdot = false;
57
58
    var $_done_nullbyte = false;
59
60
    var $_done_contami = false;
61
62
    var $_done_isocom = false;
63
64
    var $_done_union = false;
65
66
    var $_done_dos = false;
67
68
    var $_safe_badext = true;
69
70
    var $_safe_contami = true;
71
72
    var $_safe_isocom = true;
73
74
    var $_safe_union = true;
75
76
    var $_spamcount_uri = 0;
77
78
    var $_should_be_banned_time0 = false;
79
80
    var $_should_be_banned = false;
81
82
    var $_dos_stage = null;
83
84
    var $ip_matched_info = null;
85
86
    var $last_error_type = 'UNKNOWN';
87
88
    // Constructor
89
    function __construct()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
90
    {
91
        $this->mydirname = 'protector';
92
93
        // Preferences from configs/cache
94
        $this->_conf_serialized = @file_get_contents($this->get_filepath4confighcache());
95
        $this->_conf = @unserialize($this->_conf_serialized);
0 ignored issues
show
Bug introduced by
It seems like $this->_conf_serialized can also be of type false; however, parameter $data of unserialize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

95
        $this->_conf = @unserialize(/** @scrutinizer ignore-type */ $this->_conf_serialized);
Loading history...
96
        if (empty($this->_conf)) {
97
            $this->_conf = array();
98
        }
99
100
        if (!empty($this->_conf['global_disabled'])) {
101
            return true;
102
        }
103
104
        // die if PHP_SELF XSS found (disabled in 2.53)
105
        //  if( preg_match( '/[<>\'";\n ]/' , @$_SERVER['PHP_SELF'] ) ) {
106
        //      $this->message .= "Invalid PHP_SELF '{$_SERVER['PHP_SELF']}' found.\n" ;
107
        //      $this->output_log( 'PHP_SELF XSS' ) ;
108
        //      die( 'invalid PHP_SELF' ) ;
109
        //  }
110
111
        // sanitize against PHP_SELF/PATH_INFO XSS (disabled in 3.33)
112
        //  $_SERVER['PHP_SELF'] = strtr( @$_SERVER['PHP_SELF'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ;
113
        //  if( ! empty( $_SERVER['PATH_INFO'] ) ) $_SERVER['PATH_INFO'] = strtr( @$_SERVER['PATH_INFO'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ;
114
115
        $this->_bad_globals = array(
116
            'GLOBALS', '_SESSION', 'HTTP_SESSION_VARS', '_GET', 'HTTP_GET_VARS', '_POST', 'HTTP_POST_VARS', '_COOKIE',
117
            'HTTP_COOKIE_VARS', '_SERVER', 'HTTP_SERVER_VARS', '_REQUEST', '_ENV', '_FILES', 'xoopsDB', 'xoopsUser',
118
            'xoopsUserId', 'xoopsUserGroups', 'xoopsUserIsAdmin', 'xoopsConfig', 'xoopsOption', 'xoopsModule',
119
            'xoopsModuleConfig'
120
        );
121
122
        $this->_initial_recursive($_GET, 'G');
123
        $this->_initial_recursive($_POST, 'P');
124
        $this->_initial_recursive($_COOKIE, 'C');
125
        return true;
126
    }
127
128
    /**
129
     * @param string $key
130
     */
131
    function _initial_recursive($val, $key)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
132
    {
133
        if (is_array($val)) {
134
            foreach ($val as $subkey => $subval) {
135
                // check bad globals
136
                if (in_array($subkey, $this->_bad_globals, true)) {
137
                    $this->message .= "Attempt to inject '$subkey' was found.\n";
138
                    $this->_safe_contami = false;
139
                    $this->last_error_type = 'CONTAMI';
140
                }
141
                $this->_initial_recursive($subval, $key . '_' . base64_encode($subkey));
142
            }
143
        } else {
144
            // check nullbyte attack
145
            if (@$this->_conf['san_nullbyte'] && strstr($val, chr(0))) {
146
                $val = str_replace(chr(0), ' ', $val);
147
                $this->replace_doubtful($key, $val);
148
                $this->message .= "Injecting Null-byte '$val' found.\n";
149
                $this->output_log('NullByte', 0, false, 32);
150
                // $this->purge() ;
151
            }
152
153
            // register as doubtful requests against SQL Injections
154
            if (preg_match('?[\s\'"`/]?', $val)) {
155
                $this->_doubtful_requests["$key"] = $val;
156
            }
157
        }
158
    }
159
160
    static public function &getInstance()
161
    {
162
        static $instance;
163
        if (!isset($instance)) {
164
            $instance = new Protector();
165
        }
166
        return $instance;
167
    }
168
169
    function updateConfFromDb()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
170
    {
171
        if (empty($this->_conn)) {
172
            return false;
173
        }
174
175
        $result = @mysqli_query("SELECT conf_name,conf_value FROM " . \XoopsBaseConfig::get('db-prefix') . "_config WHERE conf_title like '" . "_MI_PROTECTOR%'", $this->_conn);
0 ignored issues
show
Bug introduced by
'SELECT conf_name,conf_v... '' . '_MI_PROTECTOR%'' of type string is incompatible with the type mysqli expected by parameter $mysql of mysqli_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
        $result = @mysqli_query(/** @scrutinizer ignore-type */ "SELECT conf_name,conf_value FROM " . \XoopsBaseConfig::get('db-prefix') . "_config WHERE conf_title like '" . "_MI_PROTECTOR%'", $this->_conn);
Loading history...
176
        if (!$result || mysql_num_rows($result) < 5) {
0 ignored issues
show
Bug introduced by
$result of type mysqli_result|true is incompatible with the type resource expected by parameter $result of mysql_num_rows(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
        if (!$result || mysql_num_rows(/** @scrutinizer ignore-type */ $result) < 5) {
Loading history...
177
            return false;
178
        }
179
        $db_conf = array();
180
        while (list($key, $val) = mysql_fetch_row($result)) {
0 ignored issues
show
Bug introduced by
$result of type mysqli_result|true is incompatible with the type resource expected by parameter $result of mysql_fetch_row(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

180
        while (list($key, $val) = mysql_fetch_row(/** @scrutinizer ignore-type */ $result)) {
Loading history...
181
            $db_conf[$key] = $val;
182
        }
183
        $db_conf_serialized = serialize($db_conf);
184
185
        // update config cache
186
        if ($db_conf_serialized != $this->_conf_serialized) {
187
            $fp = fopen($this->get_filepath4confighcache(), 'w');
188
            fwrite($fp, $db_conf_serialized);
189
            fclose($fp);
190
            $this->_conf = $db_conf;
191
        }
192
        return true;
193
    }
194
195
    function setConn($conn)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
196
    {
197
        $this->_conn = $conn;
198
    }
199
200
    function getConf()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
201
    {
202
        return $this->_conf;
203
    }
204
205
    function purge($redirect_to_top = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
206
    {
207
        // clear all session values
208
        if (isset($_SESSION)) {
209
            foreach ($_SESSION as $key => $val) {
210
                $_SESSION[$key] = '';
211
                if (isset($GLOBALS[$key])) {
212
                    $GLOBALS[$key] = '';
213
                }
214
            }
215
        }
216
217
        if (!headers_sent()) {
218
            // clear typical session id of PHP
219
            setcookie('PHPSESSID', '', time() - 3600, '/', '', 0);
220
            if (isset($_COOKIE[session_name()])) {
221
                setcookie(session_name(), '', time() - 3600, '/', '', 0);
222
            }
223
224
            // clear autologin cookie
225
            $xoops_cookie_path = defined('XOOPS_COOKIE_PATH') ? XOOPS_COOKIE_PATH : preg_replace('?http://[^/]+(/.*)$?', "$1", XOOPS_URL);
0 ignored issues
show
Bug introduced by
The constant XOOPS_COOKIE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
226
            if ($xoops_cookie_path == \XoopsBaseConfig::get('url')) {
227
                $xoops_cookie_path = '/';
228
            }
229
            setcookie('autologin_uname', '', time() - 3600, $xoops_cookie_path, '', 0);
230
            setcookie('autologin_pass', '', time() - 3600, $xoops_cookie_path, '', 0);
231
        }
232
233
        if ($redirect_to_top) {
234
            header('Location: ' . \XoopsBaseConfig::get('url') . '/');
235
            exit;
236
        } else {
237
            $ret = $this->call_filter('prepurge_exit');
238
            if ($ret == false) {
239
                die('Protector detects attacking actions');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
240
            }
241
        }
242
    }
243
244
    function output_log($type = 'UNKNOWN', $uid = 0, $unique_check = false, $level = 1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
245
    {
246
        if ($this->_logged) {
247
            return true;
248
        }
249
250
        if (!($this->_conf['log_level'] & $level)) {
251
            return true;
252
        }
253
254
        if (empty($this->_conn)) {
255
            $this->_conn = @mysql_connect(\XoopsBaseConfig::get('db-host'), \XoopsBaseConfig::get('db-user'), \XoopsBaseConfig::get('db-pass'));
256
            if (!$this->_conn) {
257
                die('db connection failed.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
258
            }
259
            if (!mysql_select_db(\XoopsBaseConfig::get('db-name'), $this->_conn)) {
260
                die('db selection failed.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
261
            }
262
        }
263
264
        $ip = @$_SERVER['REMOTE_ADDR'];
265
        $agent = @$_SERVER['HTTP_USER_AGENT'];
266
267
        if ($unique_check) {
268
            $result = mysqli_query('SELECT ip,type FROM ' . \XoopsBaseConfig::get('db-prefix') . '_' . $this->mydirname . '_log ORDER BY timestamp DESC LIMIT 1', $this->_conn);
0 ignored issues
show
Bug introduced by
'SELECT ip,type FROM ' ....timestamp DESC LIMIT 1' of type string is incompatible with the type mysqli expected by parameter $mysql of mysqli_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

268
            $result = mysqli_query(/** @scrutinizer ignore-type */ 'SELECT ip,type FROM ' . \XoopsBaseConfig::get('db-prefix') . '_' . $this->mydirname . '_log ORDER BY timestamp DESC LIMIT 1', $this->_conn);
Loading history...
269
            list($last_ip, $last_type) = mysql_fetch_row($result);
0 ignored issues
show
Bug introduced by
$result of type mysqli_result|true is incompatible with the type resource expected by parameter $result of mysql_fetch_row(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

269
            list($last_ip, $last_type) = mysql_fetch_row(/** @scrutinizer ignore-type */ $result);
Loading history...
270
            if ($last_ip == $ip && $last_type == $type) {
271
                $this->_logged = true;
272
                return true;
273
            }
274
        }
275
276
        mysqli_query("INSERT INTO " . XOOPS_DB_PREFIX . "_" . $this->mydirname . "_log SET ip='" . addslashes($ip) . "',agent='" . addslashes($agent) . "',type='" . addslashes($type) . "',description='" . addslashes($this->message) . "',uid='" . (int)($uid) . "',timestamp=NOW()", $this->_conn);
277
        $this->_logged = true;
278
        return true;
279
    }
280
281
    /**
282
     * @param integer $expire
283
     */
284
    function write_file_bwlimit($expire)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
285
    {
286
        $expire = min((int)($expire), time() + 300);
287
288
        $fp = @fopen($this->get_filepath4bwlimit(), 'w');
289
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
290
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

290
            /** @scrutinizer ignore-unhandled */ @flock($fp, LOCK_EX);

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...
291
            fwrite($fp, $expire . "\n");
292
            @flock($fp, LOCK_UN);
293
            fclose($fp);
294
            return true;
295
        } else {
296
            return false;
297
        }
298
    }
299
300
    function get_bwlimit()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
301
    {
302
        list($expire) = @file(Protector::get_filepath4bwlimit());
0 ignored issues
show
Bug Best Practice introduced by
The method Protector::get_filepath4bwlimit() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

302
        list($expire) = @file(Protector::/** @scrutinizer ignore-call */ get_filepath4bwlimit());
Loading history...
303
        $expire = min((int)($expire), time() + 300);
304
305
        return $expire;
306
    }
307
308
    function get_filepath4bwlimit()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
309
    {
310
        return \XoopsBaseConfig::get('trust-path') . '/modules/protector/configs/bwlimit' . substr(md5(\XoopsBaseConfig::get('root-path') . \XoopsBaseConfig::get('db-user') . \XoopsBaseConfig::get('db-prefix')), 0, 6);
311
    }
312
313
    function write_file_badips($bad_ips)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
314
    {
315
        asort($bad_ips);
316
317
        $fp = @fopen($this->get_filepath4badips(), 'w');
318
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
319
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

319
            /** @scrutinizer ignore-unhandled */ @flock($fp, LOCK_EX);

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...
320
            fwrite($fp, serialize($bad_ips) . "\n");
321
            @flock($fp, LOCK_UN);
322
            fclose($fp);
323
            return true;
324
        } else {
325
            return false;
326
        }
327
    }
328
329
    function register_bad_ips($jailed_time = 0, $ip = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
330
    {
331
        if (empty($ip)) {
332
            $ip = @$_SERVER['REMOTE_ADDR'];
333
        }
334
        if (empty($ip)) {
335
            return false;
336
        }
337
338
        $bad_ips = $this->get_bad_ips(true);
339
        $bad_ips[$ip] = $jailed_time ? $jailed_time : 0x7fffffff;
340
341
        return $this->write_file_badips($bad_ips);
342
    }
343
344
    function get_bad_ips($with_jailed_time = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
345
    {
346
        list($bad_ips_serialized) = @file(Protector::get_filepath4badips());
0 ignored issues
show
Bug Best Practice introduced by
The method Protector::get_filepath4badips() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

346
        list($bad_ips_serialized) = @file(Protector::/** @scrutinizer ignore-call */ get_filepath4badips());
Loading history...
347
        $bad_ips = empty($bad_ips_serialized) ? array() : @unserialize($bad_ips_serialized);
348
        if (!is_array($bad_ips) || isset($bad_ips[0])) {
349
            $bad_ips = array();
350
        }
351
352
        // expire jailed_time
353
        $pos = 0;
354
        foreach ($bad_ips as $bad_ip => $jailed_time) {
355
            if ($jailed_time >= time()) {
356
                break;
357
            }
358
            ++$pos;
359
        }
360
        $bad_ips = array_slice($bad_ips, $pos);
361
362
        if ($with_jailed_time) {
363
            return $bad_ips;
364
        } else {
365
            return array_keys($bad_ips);
366
        }
367
    }
368
369
    function get_filepath4badips()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
370
    {
371
        return \XoopsBaseConfig::get('root-path') . '/modules/protector/configs/badips' . substr(md5(\XoopsBaseConfig::get('root-path') . \XoopsBaseConfig::get('db-user') . \XoopsBaseConfig::get('db-prefix')), 0, 6);
372
    }
373
374
    function get_group1_ips($with_info = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
375
    {
376
        list($group1_ips_serialized) = @file(Protector::get_filepath4group1ips());
0 ignored issues
show
Bug Best Practice introduced by
The method Protector::get_filepath4group1ips() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

376
        list($group1_ips_serialized) = @file(Protector::/** @scrutinizer ignore-call */ get_filepath4group1ips());
Loading history...
377
        $group1_ips = empty($group1_ips_serialized) ? array() : @unserialize($group1_ips_serialized);
378
        if (!is_array($group1_ips)) {
379
            $group1_ips = array();
380
        }
381
382
        if ($with_info) {
383
            $group1_ips = array_flip($group1_ips);
384
        }
385
386
        return $group1_ips;
387
    }
388
389
    function get_filepath4group1ips()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
390
    {
391
        return \XoopsBaseConfig::get('var-path') . '/configs/protector_group1ips_' . substr(md5(\XoopsBaseConfig::get('root-path') . \XoopsBaseConfig::get('db-user') . \XoopsBaseConfig::get('db-prefix')), 0, 6);
392
    }
393
394
    function get_filepath4confighcache()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
395
    {
396
        return XOOPS_VAR_PATH . '/configs/protector_configcache_' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
397
    }
398
399
    function ip_match($ips)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
400
    {
401
        foreach ($ips as $ip => $info) {
402
            if ($ip) {
403
                switch (substr($ip, -1)) {
404
                    case '.' :
405
                        // foward match
406
                        if (substr(@$_SERVER['REMOTE_ADDR'], 0, strlen($ip)) == $ip) {
407
                            $this->ip_matched_info = $info;
408
                            return true;
409
                        }
410
                        break;
411
                    case '0' :
412
                    case '1' :
413
                    case '2' :
414
                    case '3' :
415
                    case '4' :
416
                    case '5' :
417
                    case '6' :
418
                    case '7' :
419
                    case '8' :
420
                    case '9' :
421
                        // full match
422
                        if (@$_SERVER['REMOTE_ADDR'] == $ip) {
423
                            $this->ip_matched_info = $info;
424
                            return true;
425
                        }
426
                        break;
427
                    default :
428
                        // perl regex
429
                        if (@preg_match($ip, @$_SERVER['REMOTE_ADDR'])) {
430
                            $this->ip_matched_info = $info;
431
                            return true;
432
                        }
433
                        break;
434
                }
435
            }
436
        }
437
        $this->ip_matched_info = null;
438
        return false;
439
    }
440
441
    function deny_by_htaccess($ip = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
442
    {
443
        if (empty($ip)) {
444
            $ip = @$_SERVER['REMOTE_ADDR'];
445
        }
446
        if (empty($ip)) {
447
            return false;
448
        }
449
        if (!function_exists('file_get_contents')) {
450
            return false;
451
        }
452
453
        $target_htaccess = \XoopsBaseConfig::get('root-path') . '/.htaccess';
454
        $backup_htaccess = \XoopsBaseConfig::get('root-path') . '/uploads/.htaccess.bak';
455
456
        $ht_body = file_get_contents($target_htaccess);
457
458
        // make backup as uploads/.htaccess.bak automatically
459
        if ($ht_body && !XoopsLoad::fileExists($backup_htaccess)) {
460
            $fw = fopen($backup_htaccess, "w");
461
            fwrite($fw, $ht_body);
462
            fclose($fw);
463
        }
464
465
        // if .htaccess is broken, restore from backup
466
        if (!$ht_body && XoopsLoad::fileExists($backup_htaccess)) {
467
            $ht_body = file_get_contents($backup_htaccess);
468
        }
469
470
        // new .htaccess
471
        if ($ht_body === false) {
472
            $ht_body = '';
473
        }
474
475
        if (preg_match("/^(.*)#PROTECTOR#\s+(DENY FROM .*)\n#PROTECTOR#\n(.*)$/si", $ht_body, $regs)) {
476
            if (substr($regs[2], -strlen($ip)) == $ip) {
477
                return true;
478
            }
479
            $new_ht_body = $regs[1] . "#PROTECTOR#\n" . $regs[2] . " $ip\n#PROTECTOR#\n" . $regs[3];
480
        } else {
481
            $new_ht_body = "#PROTECTOR#\nDENY FROM $ip\n#PROTECTOR#\n" . $ht_body;
482
        }
483
484
        // error_log( "$new_ht_body\n" , 3 , "/tmp/error_log" ) ;
485
486
        $fw = fopen($target_htaccess, "w");
487
        @flock($fw, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

487
        /** @scrutinizer ignore-unhandled */ @flock($fw, LOCK_EX);

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...
488
        fwrite($fw, $new_ht_body);
489
        @flock($fw, LOCK_UN);
490
        fclose($fw);
491
492
        return true;
493
    }
494
495
    function getDblayertrapDoubtfuls()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
496
    {
497
        return $this->_dblayertrap_doubtfuls;
498
    }
499
500
    function _dblayertrap_check_recursive($val)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
501
    {
502
        if (is_array($val)) {
503
            foreach ($val as $subval) {
504
                $this->_dblayertrap_check_recursive($subval);
505
            }
506
        } else {
507
            if (strlen($val) < 6) {
508
                return;
509
            }
510
            $val = get_magic_quotes_gpc() ? stripslashes($val) : $val;
511
            foreach ($this->_dblayertrap_doubtful_needles as $needle) {
512
                if (stristr($val, $needle)) {
513
                    $this->_dblayertrap_doubtfuls[] = $val;
514
                }
515
            }
516
        }
517
    }
518
519
    function dblayertrap_init($force_override = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
520
    {
521
        if (!empty($GLOBALS['xoopsOption']['nocommon']) || defined('_LEGACY_PREVENT_EXEC_COMMON_') || defined('_LEGACY_PREVENT_LOAD_CORE_')) {
522
            return;
523
        } // skip
524
525
        $this->_dblayertrap_doubtfuls = array();
526
        $this->_dblayertrap_check_recursive($_GET);
527
        $this->_dblayertrap_check_recursive($_POST);
528
        $this->_dblayertrap_check_recursive($_COOKIE);
529
        if (empty($this->_conf['dblayertrap_wo_server'])) {
530
            $this->_dblayertrap_check_recursive($_SERVER);
531
        }
532
533
        if (!empty($this->_dblayertrap_doubtfuls) || $force_override) {
534
            @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for define(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

534
            /** @scrutinizer ignore-unhandled */ @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');

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...
535
            require_once dirname(__DIR__) . '/class/ProtectorMysqlDatabase.class.php';
536
        }
537
    }
538
539
    function _bigumbrella_check_recursive($val)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
540
    {
541
        if (is_array($val)) {
542
            foreach ($val as $subval) {
543
                $this->_bigumbrella_check_recursive($subval);
544
            }
545
        } else {
546
            if (preg_match('/[<\'"].{15}/s', $val, $regs)) {
547
                $this->_bigumbrella_doubtfuls[] = $regs[0];
548
            }
549
        }
550
    }
551
552
    function bigumbrella_init()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
553
    {
554
        $this->_bigumbrella_doubtfuls = array();
555
        $this->_bigumbrella_check_recursive($_GET);
556
        $this->_bigumbrella_check_recursive(@$_SERVER['PHP_SELF']);
557
558
        if (!empty($this->_bigumbrella_doubtfuls)) {
559
            ob_start(array($this, 'bigumbrella_outputcheck'));
560
        }
561
    }
562
563
    function bigumbrella_outputcheck($s)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
564
    {
565
        if (defined('BIGUMBRELLA_DISABLED')) {
566
            return $s;
567
        }
568
569
        if (function_exists('headers_list')) {
570
            foreach (headers_list() as $header) {
571
                if (stristr($header, 'Content-Type:') && !stristr($header, 'text/html')) {
572
                    return $s;
573
                }
574
            }
575
        }
576
577
        if (!is_array($this->_bigumbrella_doubtfuls)) {
0 ignored issues
show
introduced by
The condition is_array($this->_bigumbrella_doubtfuls) is always true.
Loading history...
578
            return "bigumbrella injection found.";
579
        }
580
581
        foreach ($this->_bigumbrella_doubtfuls as $doubtful) {
582
            if (strstr($s, $doubtful)) {
583
                return "XSS found by Protector.";
584
            }
585
        }
586
        return $s;
587
    }
588
589
    function intval_allrequestsendid()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
590
    {
591
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
592
593
        if ($this->_done_intval) {
594
            return true;
595
        } else {
596
            $this->_done_intval = true;
597
        }
598
599
        foreach ($_GET as $key => $val) {
600
            if (substr($key, -2) === 'id' && !is_array($_GET[$key])) {
601
                $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
602
                $_GET[$key] = $HTTP_GET_VARS[$key] = $newval;
603
                if ($_REQUEST[$key] == $_GET[$key]) {
604
                    $_REQUEST[$key] = $newval;
605
                }
606
            }
607
        }
608
        foreach ($_POST as $key => $val) {
609
            if (substr($key, -2) === 'id' && !is_array($_POST[$key])) {
610
                $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
611
                $_POST[$key] = $HTTP_POST_VARS[$key] = $newval;
612
                if ($_REQUEST[$key] == $_POST[$key]) {
613
                    $_REQUEST[$key] = $newval;
614
                }
615
            }
616
        }
617
        foreach ($_COOKIE as $key => $val) {
618
            if (substr($key, -2) === 'id' && !is_array($_COOKIE[$key])) {
619
                $newval = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
620
                $_COOKIE[$key] = $HTTP_COOKIE_VARS[$key] = $newval;
621
                if ($_REQUEST[$key] == $_COOKIE[$key]) {
622
                    $_REQUEST[$key] = $newval;
623
                }
624
            }
625
        }
626
627
        return true;
628
    }
629
630
    function eliminate_dotdot()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
631
    {
632
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
633
634
        if ($this->_done_dotdot) {
635
            return true;
636
        } else {
637
            $this->_done_dotdot = true;
638
        }
639
640
        foreach ($_GET as $key => $val) {
641
            if (is_array($_GET[$key])) {
642
                continue;
643
            }
644
            if (substr(trim($val), 0, 3) === '../' || strstr($val, '../../')) {
645
                $this->last_error_type = 'DirTraversal';
646
                $this->message .= "Directory Traversal '$val' found.\n";
647
                $this->output_log($this->last_error_type, 0, false, 64);
648
                $sanitized_val = str_replace(chr(0), '', $val);
649
                if (substr($sanitized_val, -2) !== ' .') {
650
                    $sanitized_val .= ' .';
651
                }
652
                $_GET[$key] = $HTTP_GET_VARS[$key] = $sanitized_val;
653
                if ($_REQUEST[$key] == $_GET[$key]) {
654
                    $_REQUEST[$key] = $sanitized_val;
655
                }
656
            }
657
        }
658
        /*  foreach( $_POST as $key => $val ) {
659
          if( is_array( $_POST[ $key ] ) ) continue ;
660
          if( substr( trim( $val ) , 0 , 3 ) == '../' || strstr( $val , '../../' ) ) {
661
              $this->last_error_type = 'ParentDir' ;
662
              $this->message .= "Doubtful file specification '$val' found.\n" ;
663
              $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
664
              $sanitized_val = str_replace( chr(0) , '' , $val ) ;
665
              if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
666
              $_POST[ $key ] = $HTTP_POST_VARS[ $key ] = $sanitized_val ;
667
              if( $_REQUEST[ $key ] == $_POST[ $key ] ){
668
                  $_REQUEST[ $key ] = $sanitized_val ;
669
              }
670
          }
671
      }
672
      foreach( $_COOKIE as $key => $val ) {
673
          if( is_array( $_COOKIE[ $key ] ) ) continue ;
674
          if( substr( trim( $val ) , 0 , 3 ) == '../' || strstr( $val , '../../' ) ) {
675
              $this->last_error_type = 'ParentDir' ;
676
              $this->message .= "Doubtful file specification '$val' found.\n" ;
677
              $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
678
              $sanitized_val = str_replace( chr(0) , '' , $val ) ;
679
              if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
680
              $_COOKIE[ $key ] = $HTTP_COOKIE_VARS[ $key ] = $sanitized_val ;
681
              if( $_REQUEST[ $key ] == $_COOKIE[ $key ] ){
682
                  $_REQUEST[ $key ] = $sanitized_val ;
683
              }
684
          }
685
      }*/
686
687
        return true;
688
    }
689
690
    function &get_ref_from_base64index(&$current, $indexes)
691
    {
692
        foreach ($indexes as $index) {
693
            $index = base64_decode($index);
694
            if (!is_array($current)) {
695
                return false;
696
            }
697
            $current =& $current[$index];
698
        }
699
        return $current;
700
    }
701
702
    function replace_doubtful($key, $val)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
703
    {
704
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
705
706
        $index_expression = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $index_expression is dead and can be removed.
Loading history...
707
        $indexes = explode('_', $key);
708
        $base_array = array_shift($indexes);
709
710
        switch ($base_array) {
711
            case 'G' :
712
                $main_ref =& $this->get_ref_from_base64index($_GET, $indexes);
713
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_GET_VARS, $indexes);
714
                break;
715
            case 'P' :
716
                $main_ref =& $this->get_ref_from_base64index($_POST, $indexes);
717
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_POST_VARS, $indexes);
718
                break;
719
            case 'C' :
720
                $main_ref =& $this->get_ref_from_base64index($_COOKIE, $indexes);
721
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_COOKIE_VARS, $indexes);
722
                break;
723
            default :
724
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
725
        }
726
        if (!isset($main_ref)) {
727
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
728
        }
729
        $request_ref =& $this->get_ref_from_base64index($_REQUEST, $indexes);
730
        if ($request_ref !== false && $main_ref == $request_ref) {
731
            $request_ref = $val;
732
        }
733
        $main_ref = $val;
734
        $legacy_ref = $val;
735
    }
736
737
    function check_uploaded_files()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
738
    {
739
        if ($this->_done_badext) {
740
            return $this->_safe_badext;
741
        } else {
742
            $this->_done_badext = true;
743
        }
744
745
        // extensions never uploaded
746
        $bad_extensions = array('php', 'phtml', 'phtm', 'php3', 'php4', 'cgi', 'pl', 'asp');
747
        // extensions needed image check (anti-IE Content-Type XSS)
748
        $image_extensions = array(
749
            1  => 'gif', 2 => 'jpg', 3 => 'png', 4 => 'swf', 5 => 'psd', 6 => 'bmp', 7 => 'tif', 8 => 'tif', 9 => 'jpc',
750
            10 => 'jp2', 11 => 'jpx', 12 => 'jb2', 13 => 'swc', 14 => 'iff', 15 => 'wbmp', 16 => 'xbm'
751
        );
752
753
        foreach ($_FILES as $_file) {
754
            if (!empty($_file['error'])) {
755
                continue;
756
            }
757
            if (!empty($_file['name']) && is_string($_file['name'])) {
758
                $ext = strtolower(substr(strrchr($_file['name'], '.'), 1));
759
                if ($ext === 'jpeg') {
760
                    $ext = 'jpg';
761
                } else {
762
                    if ($ext === 'tiff') {
763
                        $ext = 'tif';
764
                    }
765
                }
766
767
                // anti multiple dot file (Apache mod_mime.c)
768
                if (count(explode('.', str_replace('.tar.gz', '.tgz', $_file['name']))) > 2) {
769
                    $this->message .= "Attempt to multiple dot file {$_file['name']}.\n";
770
                    $this->_safe_badext = false;
771
                    $this->last_error_type = 'UPLOAD';
772
                }
773
774
                // anti dangerous extensions
775
                if (in_array($ext, $bad_extensions)) {
776
                    $this->message .= "Attempt to upload {$_file['name']}.\n";
777
                    $this->_safe_badext = false;
778
                    $this->last_error_type = 'UPLOAD';
779
                }
780
781
                // anti camouflaged image file
782
                if (in_array($ext, $image_extensions)) {
783
                    $image_attributes = @getimagesize($_file['tmp_name']);
784
                    if ($image_attributes === false && is_uploaded_file($_file['tmp_name'])) {
785
                        // open_basedir restriction
786
                        $temp_file = \XoopsBaseConfig::get('root-path') . '/uploads/protector_upload_temporary' . md5(time());
787
                        move_uploaded_file($_file['tmp_name'], $temp_file);
788
                        $image_attributes = @getimagesize($temp_file);
789
                        @unlink($temp_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

789
                        /** @scrutinizer ignore-unhandled */ @unlink($temp_file);

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...
790
                    }
791
792
                    if ($image_attributes === false || $image_extensions[(int)($image_attributes[2])] != $ext) {
793
                        $this->message .= "Attempt to upload camouflaged image file {$_file['name']}.\n";
794
                        $this->_safe_badext = false;
795
                        $this->last_error_type = 'UPLOAD';
796
                    }
797
                }
798
            }
799
        }
800
801
        return $this->_safe_badext;
802
    }
803
804
    function check_contami_systemglobals()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
805
    {
806
        /*  if( $this->_done_contami ) return $this->_safe_contami ;
807
      else $this->_done_contami = true ; */
808
809
        /*  foreach( $this->_bad_globals as $bad_global ) {
810
          if( isset( $_REQUEST[ $bad_global ] ) ) {
811
              $this->message .= "Attempt to inject '$bad_global' was found.\n" ;
812
              $this->_safe_contami = false ;
813
              $this->last_error_type = 'CONTAMI' ;
814
          }
815
      }*/
816
817
        return $this->_safe_contami;
818
    }
819
820
    function check_sql_isolatedcommentin($sanitize = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
821
    {
822
        if ($this->_done_isocom) {
823
            return $this->_safe_isocom;
824
        } else {
825
            $this->_done_isocom = true;
826
        }
827
828
        foreach ($this->_doubtful_requests as $key => $val) {
829
            $str = $val;
830
            while ($str = strstr($str, '/*')) { /* */
831
                $str = strstr(substr($str, 2), '*/');
832
                if ($str === false) {
833
                    $this->message .= "Isolated comment-in found. ($val)\n";
834
                    if ($sanitize) {
835
                        $this->replace_doubtful($key, $val . '*/');
836
                    }
837
                    $this->_safe_isocom = false;
838
                    $this->last_error_type = 'ISOCOM';
839
                }
840
            }
841
        }
842
        return $this->_safe_isocom;
843
    }
844
845
    function check_sql_union($sanitize = true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
846
    {
847
        if ($this->_done_union) {
848
            return $this->_safe_union;
849
        } else {
850
            $this->_done_union = true;
851
        }
852
853
        foreach ($this->_doubtful_requests as $key => $val) {
854
855
            $str = str_replace(array('/*', '*/'), '', preg_replace('?/\*.+\*/?sU', '', $val));
856
            if (preg_match('/\sUNION\s+(ALL|SELECT)/i', $str)) {
857
                $this->message .= "Pattern like SQL injection found. ($val)\n";
858
                if ($sanitize) {
859
                    $this->replace_doubtful($key, preg_replace('/union/i', 'uni-on', $val));
860
                }
861
                $this->_safe_union = false;
862
                $this->last_error_type = 'UNION';
863
            }
864
        }
865
        return $this->_safe_union;
866
    }
867
868
    function stopforumspam($uid)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
869
    {
870
        if (!function_exists('curl_init')) {
871
            return false;
872
        }
873
874
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
875
            return false;
876
        }
877
878
        $query = "f=serial&ip=" . $_SERVER['REMOTE_ADDR'];
879
        $query .= isset($_POST['email']) ? "&email=" . $_POST['email'] : '';
880
        $query .= isset($_POST['uname']) ? "&username=" . $_POST['uname'] : '';
881
        $url = "http://www.stopforumspam.com/api?" . $query;
882
        $ch = curl_init();
883
        curl_setopt($ch, CURLOPT_URL, $url);
884
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
885
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
886
        $result = unserialize(curl_exec($ch));
0 ignored issues
show
Bug introduced by
It seems like curl_exec($ch) can also be of type true; however, parameter $data of unserialize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

886
        $result = unserialize(/** @scrutinizer ignore-type */ curl_exec($ch));
Loading history...
887
        curl_close($ch);
888
889
        $spammer = false;
890
        if (isset($result['email']) && isset($result['email']['lastseen'])) {
891
            $spammer = true;
892
        }
893
894
        if (isset($result['ip']) && isset($result['ip']['lastseen'])) {
895
            $last = strtotime($result['ip']['lastseen']);
896
            $oneMonth = 60 * 60 * 24 * 31;
897
            $oneMonthAgo = time() - $oneMonth;
898
            if ($last > $oneMonthAgo) {
899
                $spammer = true;
900
            }
901
        }
902
903
        if (!$spammer) {
904
            return false;
905
        }
906
907
        $this->last_error_type = 'SPAMMER POST';
908
909
        switch ($this->_conf['stopforumspam_action']) {
910
            default :
911
            case 'log' :
912
                break;
913
            case 'san' :
914
                $_POST = array();
915
                $this->message .= "POST deleted for IP:" . $_SERVER['REMOTE_ADDR'];
916
                break;
917
            case 'biptime0' :
918
                $_POST = array();
919
                $this->message .= "BAN and POST deleted for IP:" . $_SERVER['REMOTE_ADDR'];
920
                $this->_should_be_banned_time0 = true;
921
                break;
922
            case 'bip' :
923
                $_POST = array();
924
                $this->message .= "Ban and POST deleted for IP:" . $_SERVER['REMOTE_ADDR'];
925
                $this->_should_be_banned = true;
926
                break;
927
        }
928
929
        $this->output_log($this->last_error_type, $uid, false, 16);
930
931
        return true;
932
    }
933
934
    function check_dos_attack($uid = 0, $can_ban = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
935
    {
936
        $xoops = Xoops::getInstance();
937
        $xoops->db();
938
        global $xoopsDB;
939
        $db = $xoopsDB;
940
941
        if ($this->_done_dos) {
942
            return true;
943
        }
944
945
        $ip = @$_SERVER['REMOTE_ADDR'];
946
        $uri = @$_SERVER['REQUEST_URI'];
947
        $ip4sql = addslashes($ip);
948
        $uri4sql = addslashes($uri);
949
        if (empty($ip) || $ip == '') {
950
            return true;
951
        }
952
953
        // gargage collection
954
        $result = $db->queryF("DELETE FROM " . $db->prefix($this->mydirname . "_access") . " WHERE expire < UNIX_TIMESTAMP()");
955
956
        // for older versions before updating this module
957
        if ($result === false) {
958
            $this->_done_dos = true;
959
            return true;
960
        }
961
962
        // sql for recording access log (INSERT should be placed after SELECT)
963
        $sql4insertlog = "INSERT INTO " . $db->prefix($this->mydirname . "_access") . " SET ip='$ip4sql',request_uri='$uri4sql',expire=UNIX_TIMESTAMP()+'" . (int)($this->_conf['dos_expire']) . "'";
964
965
        // bandwidth limitation
966
        if (@$this->_conf['bwlimit_count'] >= 10) {
967
            $result = $db->query("SELECT COUNT(*) FROM " . $db->prefix($this->mydirname . "_access"));
968
            list($bw_count) = $db->fetchRow($result);
969
            if ($bw_count > $this->_conf['bwlimit_count']) {
970
                $this->write_file_bwlimit(time() + $this->_conf['dos_expire']);
971
            }
972
        }
973
974
        // F5 attack check (High load & same URI)
975
        $result = $db->query("SELECT COUNT(*) FROM " . $db->prefix($this->mydirname . "_access") . " WHERE ip='$ip4sql' AND request_uri='$uri4sql'");
976
        list($f5_count) = $db->fetchRow($result);
977
        if ($f5_count > $this->_conf['dos_f5count']) {
978
979
            // delayed insert
980
            $db->queryF($sql4insertlog);
981
982
            // extends the expires of the IP with 5 minutes at least (pending)
983
            // $result = $xoopsDB->queryF( "UPDATE ".$xoopsDB->prefix($this->mydirname."_access")." SET expire=UNIX_TIMESTAMP()+300 WHERE ip='$ip4sql' AND expire<UNIX_TIMESTAMP()+300" ) ;
984
985
            // call the filter first
986
            $ret = $this->call_filter('f5attack_overrun');
0 ignored issues
show
Unused Code introduced by
The assignment to $ret is dead and can be removed.
Loading history...
987
988
            // actions for F5 Attack
989
            $this->_done_dos = true;
990
            $this->last_error_type = 'DoS';
991
            switch ($this->_conf['dos_f5action']) {
992
                default :
993
                case 'exit' :
994
                    $this->output_log($this->last_error_type, $uid, true, 16);
995
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
996
                case 'none' :
997
                    $this->output_log($this->last_error_type, $uid, true, 16);
998
                    return true;
999
                case 'biptime0' :
1000
                    if ($can_ban) {
1001
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1002
                    }
1003
                    break;
1004
                case 'bip' :
1005
                    if ($can_ban) {
1006
                        $this->register_bad_ips();
1007
                    }
1008
                    break;
1009
                case 'hta' :
1010
                    if ($can_ban) {
1011
                        $this->deny_by_htaccess();
1012
                    }
1013
                    break;
1014
                case 'sleep' :
1015
                    sleep(5);
1016
                    break;
1017
            }
1018
            return false;
1019
        }
1020
1021
        // Check its Agent
1022
        if (trim($this->_conf['dos_crsafe']) != '' && preg_match($this->_conf['dos_crsafe'], @$_SERVER['HTTP_USER_AGENT'])) {
1023
            // welcomed crawler
1024
            $this->_done_dos = true;
1025
            return true;
1026
        }
1027
1028
        // Crawler check (High load & different URI)
1029
        $result = $db->query("SELECT COUNT(*) FROM " . $db->prefix($this->mydirname . "_access") . " WHERE ip='$ip4sql'");
1030
        list($crawler_count) = $db->fetchRow($result);
1031
1032
        // delayed insert
1033
        $db->queryF($sql4insertlog);
1034
1035
        if ($crawler_count > $this->_conf['dos_crcount']) {
1036
1037
            // call the filter first
1038
            $ret = $this->call_filter('crawler_overrun');
1039
1040
            // actions for bad Crawler
1041
            $this->_done_dos = true;
1042
            $this->last_error_type = 'CRAWLER';
1043
            switch ($this->_conf['dos_craction']) {
1044
                default :
1045
                case 'exit' :
1046
                    $this->output_log($this->last_error_type, $uid, true, 16);
1047
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1048
                case 'none' :
1049
                    $this->output_log($this->last_error_type, $uid, true, 16);
1050
                    return true;
1051
                case 'biptime0' :
1052
                    if ($can_ban) {
1053
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1054
                    }
1055
                    break;
1056
                case 'bip' :
1057
                    if ($can_ban) {
1058
                        $this->register_bad_ips();
1059
                    }
1060
                    break;
1061
                case 'hta' :
1062
                    if ($can_ban) {
1063
                        $this->deny_by_htaccess();
1064
                    }
1065
                    break;
1066
                case 'sleep' :
1067
                    sleep(5);
1068
                    break;
1069
            }
1070
            return false;
1071
        }
1072
1073
        return true;
1074
    }
1075
1076
    //
1077
    function check_brute_force()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1078
    {
1079
        $xoops = Xoops::getInstance();
1080
        $xoops->db();
1081
        global $xoopsDB;
1082
        $ip = @$_SERVER['REMOTE_ADDR'];
1083
        $uri = @$_SERVER['REQUEST_URI'];
1084
        $ip4sql = addslashes($ip);
1085
        $uri4sql = addslashes($uri);
1086
        if (empty($ip) || $ip == '') {
1087
            return true;
1088
        }
1089
1090
        $victim_uname = empty($_COOKIE['autologin_uname']) ? $_POST['uname'] : $_COOKIE['autologin_uname'];
1091
        // some UA send 'deleted' as a value of the deleted cookie.
1092
        if ($victim_uname === 'deleted') {
1093
            return;
1094
        }
1095
        $mal4sql = addslashes("BRUTE FORCE: $victim_uname");
1096
1097
        // gargage collection
1098
        $result = $xoopsDB->queryF("DELETE FROM " . $xoopsDB->prefix($this->mydirname . "_access") . " WHERE expire < UNIX_TIMESTAMP()");
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
1099
1100
        // sql for recording access log (INSERT should be placed after SELECT)
1101
        $sql4insertlog = "INSERT INTO " . $xoopsDB->prefix($this->mydirname . "_access") . " SET ip='$ip4sql',request_uri='$uri4sql',malicious_actions='$mal4sql',expire=UNIX_TIMESTAMP()+600";
1102
1103
        // count check
1104
        $result = $xoopsDB->query("SELECT COUNT(*) FROM " . $xoopsDB->prefix($this->mydirname . "_access") . " WHERE ip='$ip4sql' AND malicious_actions like 'BRUTE FORCE:%'");
1105
        list($bf_count) = $xoopsDB->fetchRow($result);
1106
        if ($bf_count > $this->_conf['bf_count']) {
1107
            $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1108
            $this->last_error_type = 'BruteForce';
1109
            $this->message .= "Trying to login as '" . addslashes($victim_uname) . "' found.\n";
1110
            $this->output_log('BRUTE FORCE', 0, true, 1);
1111
            $ret = $this->call_filter('bruteforce_overrun');
1112
            if ($ret == false) {
1113
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1114
            }
1115
        }
1116
        // delayed insert
1117
        $xoopsDB->queryF($sql4insertlog);
1118
    }
1119
1120
    function _spam_check_point_recursive($val)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1121
    {
1122
        if (is_array($val)) {
1123
            foreach ($val as $subval) {
1124
                $this->_spam_check_point_recursive($subval);
1125
            }
1126
        } else {
1127
            // http_host
1128
            $path_array = parse_url(\XoopsBaseConfig::get('url'));
1129
            $http_host = empty($path_array['host']) ? 'www.xoops.org' : $path_array['host'];
1130
1131
            // count URI up
1132
            $count = -1;
1133
            foreach (preg_split('#https?\:\/\/#i', $val) as $fragment) {
1134
                if (strncmp($fragment, $http_host, strlen($http_host)) !== 0) {
1135
                    ++$count;
1136
                }
1137
            }
1138
            if ($count > 0) {
1139
                $this->_spamcount_uri += $count;
1140
            }
1141
1142
            // count BBCode likd [url=www....] up (without [url=http://...])
1143
            $this->_spamcount_uri += count(preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
1144
        }
1145
    }
1146
1147
    /**
1148
     * @param integer $points4deny
1149
     */
1150
    function spam_check($points4deny, $uid)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1151
    {
1152
        $this->_spamcount_uri = 0;
1153
        $this->_spam_check_point_recursive($_POST);
1154
1155
        if ($this->_spamcount_uri >= $points4deny) {
1156
            $this->message .= @$_SERVER['REQUEST_URI'] . " SPAM POINT: $this->_spamcount_uri\n";
1157
            $this->output_log('URI SPAM', $uid, false, 128);
1158
            $ret = $this->call_filter('spamcheck_overrun');
1159
            if ($ret == false) {
1160
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1161
            }
1162
        }
1163
    }
1164
1165
    function disable_features()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1166
    {
1167
        global $HTTP_POST_VARS, $HTTP_GET_VARS, $HTTP_COOKIE_VARS;
1168
1169
        // disable "Notice: Undefined index: ..."
1170
        $error_reporting_level = error_reporting(0);
1171
1172
        //
1173
        // bit 1 : disable XMLRPC , criteria bug
1174
        //
1175
        if ($this->_conf['disable_features'] & 1) {
1176
1177
            // zx 2005/1/5 disable xmlrpc.php in root
1178
            if ( /* ! stristr( $_SERVER['SCRIPT_NAME'] , 'modules' ) && */
1179
                substr(@$_SERVER['SCRIPT_NAME'], -10) === 'xmlrpc.php'
1180
            ) {
1181
                $this->output_log('xmlrpc', 0, true, 1);
1182
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1183
            }
1184
1185
            // security bug of class/criteria.php 2005/6/27
1186
            if ($_POST['uname'] === '0' || $_COOKIE['autologin_pass'] === '0') {
1187
                $this->output_log('CRITERIA');
1188
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1189
            }
1190
        }
1191
1192
        //
1193
        // bit 11 : XSS+CSRFs in XOOPS < 2.0.10
1194
        //
1195
        if ($this->_conf['disable_features'] & 1024) {
1196
1197
            // root controllers
1198
            if (!stristr(@$_SERVER['SCRIPT_NAME'], 'modules')) {
1199
                // zx 2004/12/13 misc.php debug (file check)
1200
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'debug' || $_POST['type'] === 'debug') && !preg_match('/^dummy_[0-9]+\.html$/', $_GET['file'])) {
1201
                    $this->output_log('misc debug');
1202
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1203
                }
1204
1205
                // zx 2004/12/13 misc.php smilies
1206
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'smilies' || $_POST['type'] === 'smilies') && !preg_match('/^[0-9a-z_]*$/i', $_GET['target'])) {
1207
                    $this->output_log('misc smilies');
1208
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1209
                }
1210
1211
                // zx 2005/1/5 edituser.php avatarchoose
1212
                if (substr(@$_SERVER['SCRIPT_NAME'], -12) === 'edituser.php' && $_POST['op'] === 'avatarchoose' && strstr($_POST['user_avatar'], '..')) {
1213
                    $this->output_log('edituser avatarchoose');
1214
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1215
                }
1216
            }
1217
1218
            // zx 2005/1/4 findusers
1219
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'findusers' || $_POST['fct'] === 'findusers')) {
1220
                foreach ($_POST as $key => $val) {
1221
                    if (strstr($key, "'") || strstr($val, "'")) {
1222
                        $this->output_log('findusers');
1223
                        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1224
                    }
1225
                }
1226
            }
1227
1228
            // preview CSRF zx 2004/12/14
1229
            // news submit.php
1230
            if (substr(@$_SERVER['SCRIPT_NAME'], -23) === 'modules/news/submit.php' && isset($_POST['preview']) && strpos(@$_SERVER['HTTP_REFERER'], \XoopsBaseConfig::get('url') . '/modules/news/submit.php') !== 0) {
1231
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1232
            }
1233
            // news admin/index.php
1234
            if (substr(@$_SERVER['SCRIPT_NAME'], -28) === 'modules/news/admin/index.php' && ($_POST['op'] === 'preview' || $_GET['op'] === 'preview') && strpos(@$_SERVER['HTTP_REFERER'], \XoopsBaseConfig::get('url') . '/modules/news/admin/index.php') !== 0) {
1235
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1236
            }
1237
            // comment comment_post.php
1238
            if (isset($_POST['com_dopreview']) && !strstr(substr(@$_SERVER['HTTP_REFERER'], -16), 'comment_post.php')) {
1239
                $HTTP_POST_VARS['dohtml'] = $_POST['dohtml'] = 0;
1240
            }
1241
            // disable preview of system's blocksadmin
1242
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'blocksadmin' || $_POST['fct'] === 'blocksadmin') && isset($_POST['previewblock']) /* && strpos( $_SERVER['HTTP_REFERER'] , \XoopsBaseConfig::get('url').'/modules/system/admin.php' ) !== 0 */) {
1243
                die("Danger! don't use this preview. Use 'altsys module' instead.(by Protector)");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1244
            }
1245
            // tpl preview
1246
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'tplsets' || $_POST['fct'] === 'tplsets')) {
1247
                if ($_POST['op'] === 'previewpopup' || $_GET['op'] === 'previewpopup' || isset($_POST['previewtpl'])) {
1248
                    die("Danger! don't use this preview.(by Protector)");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1249
                }
1250
            }
1251
        }
1252
1253
        // restore reporting level
1254
        error_reporting($error_reporting_level);
1255
    }
1256
1257
    /**
1258
     * @param string $type
1259
     */
1260
    function call_filter($type, $dying_message = '')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1261
    {
1262
        require_once __DIR__ . '/ProtectorFilter.php';
1263
        $filter_handler = ProtectorFilterHandler::getInstance();
1264
        $ret = $filter_handler->execute($type);
1265
        if ($ret == false && $dying_message) {
1266
            die($dying_message);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1267
        }
1268
1269
        return $ret;
1270
    }
1271
}
1272