Passed
Push — master ( 5b31c2...e056d2 )
by Richard
06:44 queued 32s
created

XoopsSecurity::garbageCollection()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 1
1
<?php
2
3
use Xmf\IPAddress;
4
5
/**
6
 * XOOPS security handler
7
 *
8
 * You may not change or alter any portion of this comment or credits
9
 * of supporting developers from this source code or any supporting source code
10
 * which is considered copyrighted (c) material of the original comment or credit authors.
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 *
15
 * @author    Kazumi Ono <[email protected]>
16
 * @author    Jan Pedersen <[email protected]>
17
 * @author    John Neill <[email protected]>
18
 * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
19
 * @license   GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
20
 * @package   kernel
21
 * @since     2.0.0
22
 */
23
24
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
25
26
/**
27
 * Class XoopsSecurity
28
 */
29
class XoopsSecurity
30
{
31
    public $errors = array();
32
33
    /**
34
     * Check if there is a valid token in $_REQUEST[$name . '_REQUEST'] - can be expanded for more wide use, later (Mith)
35
     *
36
     * @param bool         $clearIfValid whether to clear the token after validation
37
     * @param string|false $token        token to validate
38
     * @param string       $name         name of session variable
39
     *
40
     * @return bool
41
     */
42
    public function check($clearIfValid = true, $token = false, $name = 'XOOPS_TOKEN')
43
    {
44
        return $this->validateToken($token, $clearIfValid, $name);
45
    }
46
47
    /**
48
     * Create a token in the user's session
49
     *
50
     * @param int|string    $timeout time in seconds the token should be valid
51
     * @param string $name    name of session variable
52
     *
53
     * @return string token value
54
     */
55
    public function createToken($timeout = 0, $name = 'XOOPS_TOKEN')
56
    {
57
        $this->garbageCollection($name);
58
        if ($timeout == 0) {
59
            $expire  = @ini_get('session.gc_maxlifetime');
60
            $timeout = ($expire > 0) ? $expire : 900;
61
        }
62
        $token_id = md5(uniqid(mt_rand(), true));
63
        // save token data on the server
64
        if (!isset($_SESSION[$name . '_SESSION'])) {
65
            $_SESSION[$name . '_SESSION'] = array();
66
        }
67
        $token_data = array(
68
            'id'     => $token_id,
69
            'expire' => time() + (int)$timeout);
70
        $_SESSION[$name . '_SESSION'][] = $token_data;
71
72
        return md5($token_id . $_SERVER['HTTP_USER_AGENT'] . XOOPS_DB_PREFIX);
73
    }
74
75
    /**
76
     * Check if a token is valid. If no token is specified, $_REQUEST[$name . '_REQUEST'] is checked
77
     *
78
     * @param string|false $token        token to validate
79
     * @param bool         $clearIfValid whether to clear the token value if valid
80
     * @param string       $name         session name to validate
81
     *
82
     * @return bool
83
     */
84
    public function validateToken($token = false, $clearIfValid = true, $name = 'XOOPS_TOKEN')
85
    {
86
        global $xoopsLogger;
87
        $token = ($token !== false) ? $token : (isset($_REQUEST[$name . '_REQUEST']) ? $_REQUEST[$name . '_REQUEST'] : '');
88
        if (empty($token) || empty($_SESSION[$name . '_SESSION'])) {
89
            $xoopsLogger->addExtra('Token Validation', 'No valid token found in request/session');
90
91
            return false;
92
        }
93
        $validFound = false;
94
        $token_data = &$_SESSION[$name . '_SESSION'];
95
        foreach (array_keys($token_data) as $i) {
96
            if ($token === md5($token_data[$i]['id'] . $_SERVER['HTTP_USER_AGENT'] . XOOPS_DB_PREFIX)) {
97
                if ($this->filterToken($token_data[$i])) {
98
                    if ($clearIfValid) {
99
                        // token should be valid once, so clear it once validated
100
                        unset($token_data[$i]);
101
                    }
102
                    $xoopsLogger->addExtra('Token Validation', 'Valid token found');
103
                    $validFound = true;
104
                } else {
105
                    $str = 'Valid token expired';
106
                    $this->setErrors($str);
107
                    $xoopsLogger->addExtra('Token Validation', $str);
108
                }
109
            }
110
        }
111
        if (!$validFound && !isset($str)) {
112
            $str = 'No valid token found';
113
            $this->setErrors($str);
114
            $xoopsLogger->addExtra('Token Validation', $str);
115
        }
116
        $this->garbageCollection($name);
117
118
        return $validFound;
119
    }
120
121
    /**
122
     * Clear all token values from user's session
123
     *
124
     * @param string $name session name
125
     *
126
     * @return void
127
     */
128
    public function clearTokens($name = 'XOOPS_TOKEN')
129
    {
130
        $_SESSION[$name . '_SESSION'] = array();
131
    }
132
133
    /**
134
     * Check whether a token value is expired or not
135
     *
136
     * @param string $token token
137
     *
138
     * @return bool
139
     */
140
    public function filterToken($token)
141
    {
142
        return (!empty($token['expire']) && $token['expire'] >= time());
143
    }
144
145
    /**
146
     * Perform garbage collection, clearing expired tokens
147
     *
148
     * @param string $name session name
149
     *
150
     * @return void
151
     */
152
    public function garbageCollection($name = 'XOOPS_TOKEN')
153
    {
154
        $sessionName = $name . '_SESSION';
155
        if (!empty($_SESSION[$sessionName]) && is_array($_SESSION[$sessionName])) {
156
            $_SESSION[$sessionName] = array_filter($_SESSION[$sessionName], array($this, 'filterToken'));
157
        }
158
    }
159
160
    /**
161
     * Check the user agent's HTTP REFERER against XOOPS_URL
162
     *
163
     * @param int $docheck 0 to not check the referer (used with XML-RPC), 1 to actively check it
164
     *
165
     * @return bool
166
     */
167
    public function checkReferer($docheck = 1)
168
    {
169
        $ref = xoops_getenv('HTTP_REFERER');
170
        if ($docheck == 0) {
171
            return true;
172
        }
173
        if ($ref == '') {
174
            return false;
175
        }
176
        return !(strpos($ref, XOOPS_URL) !== 0);
177
    }
178
179
    /**
180
     * Check superglobals for contamination
181
     *
182
     * @return void
183
     **/
184
    public function checkSuperglobals()
185
    {
186
        foreach (array(
187
                     'GLOBALS',
188
                     '_SESSION',
189
                     'HTTP_SESSION_VARS',
190
                     '_GET',
191
                     'HTTP_GET_VARS',
192
                     '_POST',
193
                     'HTTP_POST_VARS',
194
                     '_COOKIE',
195
                     'HTTP_COOKIE_VARS',
196
                     '_REQUEST',
197
                     '_SERVER',
198
                     'HTTP_SERVER_VARS',
199
                     '_ENV',
200
                     'HTTP_ENV_VARS',
201
                     '_FILES',
202
                     'HTTP_POST_FILES',
203
                     'xoopsDB',
204
                     'xoopsUser',
205
                     'xoopsUserId',
206
                     'xoopsUserGroups',
207
                     'xoopsUserIsAdmin',
208
                     'xoopsConfig',
209
                     'xoopsOption',
210
                     'xoopsModule',
211
                     'xoopsModuleConfig',
212
                     'xoopsRequestUri') as $bad_global) {
213
            if (isset($_REQUEST[$bad_global])) {
214
                header('Location: ' . XOOPS_URL . '/');
215
                exit();
216
            }
217
        }
218
    }
219
220
    /**
221
     * Check if visitor's IP address is banned
222
     * Should be changed to return bool and let the action be up to the calling script
223
     *
224
     * @return void
225
     */
226
    public function checkBadips()
227
    {
228
        global $xoopsConfig;
229
230
        $addr = IPAddress::fromRequest();
231
        $ip = $addr->asReadable();
232
        if ($xoopsConfig['enable_badips'] == 1 && $ip != '0.0.0.0') {
233
            foreach ($xoopsConfig['bad_ips'] as $bi) {
234
                if (!empty($bi) && preg_match('/' . $bi . '/', $ip)) {
235
                    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...
236
                }
237
            }
238
        }
239
    }
240
241
    /**
242
     * Get the HTML code for a XoopsFormHiddenToken object - used in forms that do not use XoopsForm elements
243
     *
244
     * @param string $name session token name
245
     *
246
     * @return string
247
     */
248
    public function getTokenHTML($name = 'XOOPS_TOKEN')
249
    {
250
        require_once XOOPS_ROOT_PATH . '/class/xoopsformloader.php';
251
        $token = new XoopsFormHiddenToken($name);
252
253
        return $token->render();
254
    }
255
256
    /**
257
     * Add an error
258
     *
259
     * @param string $error message
260
     *
261
     * @return void
262
     */
263
    public function setErrors($error)
264
    {
265
        $this->errors[] = trim($error);
266
    }
267
268
    /**
269
     * Get generated errors
270
     *
271
     * @param bool $ashtml Format using HTML?
272
     *
273
     * @return array|string Array of array messages OR HTML string
274
     */
275
    public function &getErrors($ashtml = false)
276
    {
277
        if (!$ashtml) {
278
            return $this->errors;
279
        } else {
280
            $ret = '';
281
            if (count($this->errors) > 0) {
282
                foreach ($this->errors as $error) {
283
                    $ret .= $error . '<br>';
284
                }
285
            }
286
287
            return $ret;
288
        }
289
    }
290
}
291