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