Passed
Pull Request — master (#582)
by Richard
17:20
created

Security::checkBadips()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 7.0957

Importance

Changes 0
Metric Value
cc 7
eloc 7
nc 4
nop 0
dl 0
loc 10
ccs 7
cts 8
cp 0.875
crap 7.0957
rs 8.8333
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
namespace Xoops\Core;
13
14
use Xmf\Random;
15
16
/**
17
 * XOOPS security handler
18
 *
19
 * @category  Xoops\Core
20
 * @package   Security
21
 * @author    Kazumi Ono <[email protected]>
22
 * @author    Jan Pedersen <[email protected]>
23
 * @author    John Neill <[email protected]>
24
 * @author    Richard Griffith <[email protected]>
25
 * @copyright 2014-2015 XOOPS Project (http://xoops.org)
26
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
27
 * @version   Release: 1.0
28
 * @link      http://xoops.org
29
 * @since     2.0.0
30
 */
31
class Security
32
{
33
    private $errors = array();
34
35
    /**
36
     * Check if there is a valid token in $_REQUEST[$name . '_REQUEST']
37
     *
38
     * @param bool         $clearIfValid whether to clear the token after validation
39
     * @param string|false $token        token to validate
40
     * @param string       $name         name of session variable
41
     *
42
     * @return bool
43
     */
44 1
    public function check($clearIfValid = true, $token = false, $name = 'XOOPS_TOKEN')
45
    {
46 1
        return $this->validateToken($token, $clearIfValid, $name);
47
    }
48
49
    /**
50
     * Create a token in the user's session
51
     *
52
     * @param int    $timeout time in seconds the token should be valid
53
     * @param string $name    name of session variable
54
     *
55
     * @return string token value
56
     */
57 13
    public function createToken($timeout = 300, $name = 'XOOPS_TOKEN')
58
    {
59 13
        $this->garbageCollection($name);
60 13
        $timeout = ($timeout <= 0) ? 300 : $timeout;
61 13
        $token_id = Random::generateOneTimeToken();
62
        // save token data on the server
63 13
        if (!isset($_SESSION[$name . '_SESSION'])) {
64 9
            $_SESSION[$name . '_SESSION'] = array();
65
        }
66
        $token_data = array(
67 13
            'id' => $token_id, 'expire' => time() + (int)($timeout)
68
        );
69 13
        array_push($_SESSION[$name . '_SESSION'], $token_data);
70 13
        return $token_id;
71
    }
72
73
    /**
74
     * Check if a token is valid. If no token is specified, $_REQUEST[$name . '_REQUEST'] is checked
75
     *
76
     * @param string|false $token        token to validate
77
     * @param bool         $clearIfValid whether to clear the token value if valid
78
     * @param string       $name         session name to validate
79
     *
80
     * @return bool
81
     */
82 2
    public function validateToken($token = false, $clearIfValid = true, $name = 'XOOPS_TOKEN')
83
    {
84 2
        $ret = false;
85 2
        $log = array();
86 2
        $token = ($token !== false)
87 2
            ? $token
88 2
            : (isset($_REQUEST[$name . '_REQUEST']) ? $_REQUEST[$name . '_REQUEST'] : '');
89 2
        if (empty($token) || empty($_SESSION[$name . '_SESSION'])) {
90 2
            $str = 'No valid token found in request/session';
91 2
            $this->setErrors($str);
92 2
            $log[] = array('Token Validation', $str);
93
        } else {
94 2
            $token_data =& $_SESSION[$name . '_SESSION'];
95 2
            if (is_array($token_data)) {
96 2
                foreach (array_keys($token_data) as $i) {
97 2
                    if ($token === $token_data[$i]['id']) {
98 2
                        if ($this->filterToken($token_data[$i])) {
99 2
                            if ($clearIfValid) {
100
                                // token should be valid once, so clear it once validated
101 2
                                unset($token_data[$i]);
102
                            }
103 2
                            $log[] = array('Token Validation', 'Valid token found');
104 2
                            $ret = true;
105
                        } else {
106 2
                            $str = 'Valid token expired';
107 2
                            $this->setErrors($str);
108 2
                            $log[] = array('Token Validation', $str);
109
                        }
110
                    }
111
                }
112
            }
113 2
            if (!$ret) {
114 2
                $log[] = array('Token Validation', 'No valid token found');
115
            }
116 2
            $this->garbageCollection($name);
117
        }
118 2
        \Xoops::getInstance()->events()->triggerEvent('core.security.validatetoken.end', array($log));
119 2
        return $ret;
120
    }
121
122
    /**
123
     * Clear all token values from user's session
124
     *
125
     * @param string $name session name
126
     *
127
     * @return void
128
     */
129 1
    public function clearTokens($name = 'XOOPS_TOKEN')
130
    {
131 1
        $_SESSION[$name . '_SESSION'] = array();
132 1
    }
133
134
    /**
135
     * Check whether a token value is expired or not
136
     *
137
     * @param string $token token
138
     *
139
     * @return bool
140
     */
141 9
    public function filterToken($token)
142
    {
143 9
        return (!empty($token['expire']) && $token['expire'] >= time());
144
    }
145
146
    /**
147
     * Perform garbage collection, clearing expired tokens
148
     *
149
     * @param string $name session name
150
     *
151
     * @return void
152
     */
153 13
    public function garbageCollection($name = 'XOOPS_TOKEN')
154
    {
155 13
        $sessionName = $name . '_SESSION';
156 13
        if (!empty($_SESSION[$sessionName]) && is_array($_SESSION[$sessionName])) {
157 9
            $_SESSION[$sessionName] = array_filter($_SESSION[$sessionName], array($this, 'filterToken'));
158
        }
159 13
    }
160
161
    /**
162
     * Check the user agent's HTTP REFERER against XOOPS_URL
163
     *
164
     * @param int $docheck 0 to not check the referer (used with XML-RPC), 1 to actively check it
165
     *
166
     * @return bool
167
     */
168 1
    public function checkReferer($docheck = 1)
169
    {
170 1
        $ref = \Xoops::getInstance()->getEnv('HTTP_REFERER');
171 1
        if ($docheck == 0) {
172 1
            return true;
173
        }
174 1
        if ($ref == '') {
175
            return false;
176
        }
177 1
        if (strpos($ref, \XoopsBaseConfig::get('url')) !== 0) {
178 1
            return false;
179
        }
180 1
        return true;
181
    }
182
183
    /**
184
     * Check if visitor's IP address is banned
185
     * Should be changed to return bool and let the action be up to the calling script
186
     *
187
     * @return void
188
     */
189 1
    public function checkBadips()
190
    {
191 1
        $xoops = \Xoops::getInstance();
192 1
        if ($xoops->getConfig('enable_badips') == 1
193 1
            && isset($_SERVER['REMOTE_ADDR'])
194 1
            && $_SERVER['REMOTE_ADDR'] != ''
195
        ) {
196 1
            foreach ($xoops->getConfig('bad_ips') as $bi) {
197 1
                if (!empty($bi) && preg_match('/' . $bi . '/', $_SERVER['REMOTE_ADDR'])) {
198
                    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...
199
                }
200
            }
201
        }
202 1
    }
203
204
    /**
205
     * Get the HTML code for a Xoops\Form\Token object - provides a hidden token field
206
     * used in forms that do not use Xoops\Form elements
207
     *
208
     * @param string $name session token name
209
     *
210
     * @return string
211
     */
212 2
    public function getTokenHTML($name = 'XOOPS_TOKEN')
213
    {
214 2
        $token = new \Xoops\Form\Token($name);
215 2
        return $token->render();
216
    }
217
218
    /**
219
     * Add an error
220
     *
221
     * @param string $error message
222
     *
223
     * @return void
224
     */
225 3
    public function setErrors($error)
226
    {
227 3
        $this->errors[] = trim($error);
228 3
    }
229
230
    /**
231
     * Get generated errors
232
     *
233
     * @param bool $ashtml Format using HTML?
234
     *
235
     * @return array|string Array of array messages OR HTML string
236
     */
237 3
    public function getErrors($ashtml = false)
238
    {
239 3
        if (!$ashtml) {
240 3
            return $this->errors;
241
        } else {
242 1
            $ret = '';
243 1
            if (is_array($this->errors)) {
0 ignored issues
show
introduced by
The condition is_array($this->errors) is always true.
Loading history...
244 1
                $ret = implode('<br />', $this->errors) . '<br />';
245
            }
246 1
            return $ret;
247
        }
248
    }
249
}
250