Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

engine/lib/sessions.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Elgg session management
5
 * Functions to manage logins
6
 *
7
 * @package    Elgg.Core
8
 * @subpackage Session
9
 */
10
11
/**
12
 * Elgg magic session
13
 * @deprecated 1.9
14
 */
15
global $SESSION;
16
17
/**
18
 * Gets Elgg's session object
19
 *
20
 * @return \ElggSession
21
 * @since 1.9
22
 */
23
function elgg_get_session() {
24
	return _elgg_services()->session;
25
}
26
27
/**
28
 * Return the current logged in user, or null if no user is logged in.
29
 *
30
 * @return \ElggUser
31
 */
32
function elgg_get_logged_in_user_entity() {
33
	return _elgg_services()->session->getLoggedInUser();
34
}
35
36
/**
37
 * Return the current logged in user by guid.
38
 *
39
 * @see elgg_get_logged_in_user_entity()
40
 * @return int
41
 */
42
function elgg_get_logged_in_user_guid() {
43 1
	return _elgg_services()->session->getLoggedInUserGuid();
44
}
45
46
/**
47
 * Returns whether or not the user is currently logged in
48
 *
49
 * @return bool
50
 */
51
function elgg_is_logged_in() {
52
	return _elgg_services()->session->isLoggedIn();
53
}
54
55
/**
56
 * Returns whether or not the viewer is currently logged in and an admin user.
57
 *
58
 * @return bool
59
 */
60
function elgg_is_admin_logged_in() {
61
	return _elgg_services()->session->isAdminLoggedIn();
62
}
63
64
/**
65
 * Check if the given user has full access.
66
 *
67
 * @todo: Will always return full access if the user is an admin.
68
 *
69
 * @param int $user_guid The user to check
70
 *
71
 * @return bool
72
 * @since 1.7.1
73
 */
74
function elgg_is_admin_user($user_guid) {
75
	global $CONFIG;
76
77
	$user_guid = (int)$user_guid;
78
79
	$current_user = elgg_get_logged_in_user_entity();
80
	if ($current_user && $current_user->guid == $user_guid) {
81
		return $current_user->isAdmin();
82
	}
83
84
	// cannot use magic metadata here because of recursion
85
86
	// must support the old way of getting admin from metadata
87
	// in order to run the upgrade to move it into the users table.
88
	$version = (int) datalist_get('version');
89
90
	if ($version < 2010040201) {
91
		$admin = elgg_get_metastring_id('admin');
92
		$yes = elgg_get_metastring_id('yes');
93
		$one = elgg_get_metastring_id('1');
94
95
		$query = "SELECT 1 FROM {$CONFIG->dbprefix}users_entity as e,
96
			{$CONFIG->dbprefix}metadata as md
97
			WHERE (
98
				md.name_id = '$admin'
99
				AND md.value_id IN ('$yes', '$one')
100
				AND e.guid = md.entity_guid
101
				AND e.guid = {$user_guid}
102
				AND e.banned = 'no'
103
			)";
104
	} else {
105
		$query = "SELECT 1 FROM {$CONFIG->dbprefix}users_entity as e
106
			WHERE (
107
				e.guid = {$user_guid}
108
				AND e.admin = 'yes'
109
			)";
110
	}
111
112
	// normalizing the results from get_data()
113
	// See #1242
114
	$info = get_data($query);
115
	if (!((is_array($info) && count($info) < 1) || $info === false)) {
116
		return true;
117
	}
118
	return false;
119
}
120
121
/**
122
 * Perform user authentication with a given username and password.
123
 *
124
 * @warning This returns an error message on failure. Use the identical operator to check
125
 * for access: if (true === elgg_authenticate()) { ... }.
126
 *
127
 *
128
 * @see login
129
 *
130
 * @param string $username The username
131
 * @param string $password The password
132
 *
133
 * @return true|string True or an error message on failure
134
 * @access private
135
 */
136
function elgg_authenticate($username, $password) {
137
	$pam = new \ElggPAM('user');
138
	$credentials = array('username' => $username, 'password' => $password);
139
	$result = $pam->authenticate($credentials);
140
	if (!$result) {
141
		return $pam->getFailureMessage();
142
	}
143
	return true;
144
}
145
146
/**
147
 * Hook into the PAM system which accepts a username and password and attempts to authenticate
148
 * it against a known user.
149
 *
150
 * @param array $credentials Associated array of credentials passed to
151
 *                           Elgg's PAM system. This function expects
152
 *                           'username' and 'password' (cleartext).
153
 *
154
 * @return bool
155
 * @throws LoginException
156
 * @access private
157
 */
158
function pam_auth_userpass(array $credentials = array()) {
159
160
	if (!isset($credentials['username']) || !isset($credentials['password'])) {
161
		return false;
162
	}
163
164
	$user = get_user_by_username($credentials['username']);
165
	if (!$user) {
166
		throw new \LoginException(_elgg_services()->translator->translate('LoginException:UsernameFailure'));
167
	}
168
169
	if (check_rate_limit_exceeded($user->guid)) {
170
		throw new \LoginException(_elgg_services()->translator->translate('LoginException:AccountLocked'));
171
	}
172
173
	$password_svc = _elgg_services()->passwords;
174
	$password = $credentials['password'];
175
	$hash = $user->password_hash;
176
177
	if (!$hash) {
178
		// try legacy hash
179
		$legacy_hash = $password_svc->generateLegacyHash($user, $password);
180 View Code Duplication
		if ($user->password !== $legacy_hash) {
181
			log_login_failure($user->guid);
182
			throw new \LoginException(_elgg_services()->translator->translate('LoginException:PasswordFailure'));
183
		}
184
185
		// migrate password
186
		$password_svc->forcePasswordReset($user, $password);
187
		return true;
188
	}
189
190 View Code Duplication
	if (!$password_svc->verify($password, $hash)) {
191
		log_login_failure($user->guid);
192
		throw new \LoginException(_elgg_services()->translator->translate('LoginException:PasswordFailure'));
193
	}
194
195
	if ($password_svc->needsRehash($hash)) {
196
		$password_svc->forcePasswordReset($user, $password);
197
	}
198
199
	return true;
200
}
201
202
/**
203
 * Log a failed login for $user_guid
204
 *
205
 * @param int $user_guid User GUID
206
 *
207
 * @return bool
208
 */
209
function log_login_failure($user_guid) {
210
	$user_guid = (int)$user_guid;
211
	$user = get_entity($user_guid);
212
213
	if (($user_guid) && ($user) && ($user instanceof \ElggUser)) {
214
		$fails = (int)$user->getPrivateSetting("login_failures");
215
		$fails++;
216
217
		$user->setPrivateSetting("login_failures", $fails);
218
		$user->setPrivateSetting("login_failure_$fails", time());
219
		return true;
220
	}
221
222
	return false;
223
}
224
225
/**
226
 * Resets the fail login count for $user_guid
227
 *
228
 * @param int $user_guid User GUID
229
 *
230
 * @return bool true on success (success = user has no logged failed attempts)
231
 */
232
function reset_login_failure_count($user_guid) {
233
	$user_guid = (int)$user_guid;
234
	$user = get_entity($user_guid);
235
236
	if (($user_guid) && ($user) && ($user instanceof \ElggUser)) {
237
		$fails = (int)$user->getPrivateSetting("login_failures");
238
239
		if ($fails) {
240
			for ($n = 1; $n <= $fails; $n++) {
241
				$user->removePrivateSetting("login_failure_$n");
242
			}
243
244
			$user->removePrivateSetting("login_failures");
245
246
			return true;
247
		}
248
249
		// nothing to reset
250
		return true;
251
	}
252
253
	return false;
254
}
255
256
/**
257
 * Checks if the rate limit of failed logins has been exceeded for $user_guid.
258
 *
259
 * @param int $user_guid User GUID
260
 *
261
 * @return bool on exceeded limit.
262
 */
263
function check_rate_limit_exceeded($user_guid) {
264
	// 5 failures in 5 minutes causes temporary block on logins
265
	$limit = 5;
266
	$user_guid = (int)$user_guid;
267
	$user = get_entity($user_guid);
268
269
	if (($user_guid) && ($user) && ($user instanceof \ElggUser)) {
270
		$fails = (int)$user->getPrivateSetting("login_failures");
271
		if ($fails >= $limit) {
272
			$cnt = 0;
273
			$time = time();
274
			for ($n = $fails; $n > 0; $n--) {
275
				$f = $user->getPrivateSetting("login_failure_$n");
276
				if ($f > $time - (60 * 5)) {
277
					$cnt++;
278
				}
279
280
				if ($cnt == $limit) {
281
					// Limit reached
282
					return true;
283
				}
284
			}
285
		}
286
	}
287
288
	return false;
289
}
290
291
/**
292
 * Set a cookie, but allow plugins to customize it first.
293
 *
294
 * To customize all cookies, register for the 'init:cookie', 'all' event.
295
 *
296
 * @param \ElggCookie $cookie The cookie that is being set
297
 * @return bool
298
 * @since 1.9
299
 */
300
function elgg_set_cookie(\ElggCookie $cookie) {
301
	if (elgg_trigger_event('init:cookie', $cookie->name, $cookie)) {
0 ignored issues
show
$cookie is of type object<ElggCookie>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
302
		return setcookie($cookie->name, $cookie->value, $cookie->expire, $cookie->path,
303
						$cookie->domain, $cookie->secure, $cookie->httpOnly);
304
	}
305
	return false;
306
}
307
308
/**
309
 * Logs in a specified \ElggUser. For standard registration, use in conjunction
310
 * with elgg_authenticate.
311
 *
312
 * @see elgg_authenticate
313
 *
314
 * @param \ElggUser $user       A valid Elgg user object
315
 * @param boolean   $persistent Should this be a persistent login?
316
 *
317
 * @return true or throws exception
318
 * @throws LoginException
319
 */
320
function login(\ElggUser $user, $persistent = false) {
321
	if ($user->isBanned()) {
322
		throw new \LoginException(elgg_echo('LoginException:BannedUser'));
323
	}
324
325
	$session = _elgg_services()->session;
326
327
	// give plugins a chance to reject the login of this user (no user in session!)
328
	if (!elgg_trigger_before_event('login', 'user', $user)) {
0 ignored issues
show
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
329
		throw new \LoginException(elgg_echo('LoginException:Unknown'));
330
	}
331
332
	// #5933: set logged in user early so code in login event will be able to
333
	// use elgg_get_logged_in_user_entity().
334
	$session->setLoggedInUser($user);
335
336
	// deprecate event
337
	$message = "The 'login' event was deprecated. Register for 'login:before' or 'login:after'";
338
	$version = "1.9";
339
	if (!elgg_trigger_deprecated_event('login', 'user', $user, $message, $version)) {
0 ignored issues
show
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
340
		$session->removeLoggedInUser();
341
		throw new \LoginException(elgg_echo('LoginException:Unknown'));
342
	}
343
344
	// if remember me checked, set cookie with token and store hash(token) for user
345
	if ($persistent) {
346
		_elgg_services()->persistentLogin->makeLoginPersistent($user);
347
	}
348
349
	// User's privilege has been elevated, so change the session id (prevents session fixation)
350
	$session->migrate();
351
352
	set_last_login($user->guid);
353
	reset_login_failure_count($user->guid);
354
355
	elgg_trigger_after_event('login', 'user', $user);
0 ignored issues
show
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
356
357
	// if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143
358
	if (is_memcache_available()) {
359
		$guid = $user->getGUID();
360
		// this needs to happen with a shutdown function because of the timing with set_last_login()
361
		register_shutdown_function("_elgg_invalidate_memcache_for_entity", $guid);
362
	}
363
364
	return true;
365
}
366
367
/**
368
 * Log the current user out
369
 *
370
 * @return bool
371
 */
372
function logout() {
373
	$session = _elgg_services()->session;
374
	$user = $session->getLoggedInUser();
375
	if (!$user) {
376
		return false;
377
	}
378
379
	if (!elgg_trigger_before_event('logout', 'user', $user)) {
0 ignored issues
show
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
380
		return false;
381
	}
382
383
	// deprecate event
384
	$message = "The 'logout' event was deprecated. Register for 'logout:before' or 'logout:after'";
385
	$version = "1.9";
386
	if (!elgg_trigger_deprecated_event('logout', 'user', $user, $message, $version)) {
0 ignored issues
show
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
387
		return false;
388
	}
389
390
	_elgg_services()->persistentLogin->removePersistentLogin();
391
392
	// pass along any messages into new session
393
	$old_msg = $session->get('msg');
394
	$session->invalidate();
395
	$session->set('msg', $old_msg);
396
397
	elgg_trigger_after_event('logout', 'user', $user);
0 ignored issues
show
$user is of type object<ElggUser>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
398
399
	return true;
400
}
401
402
/**
403
 * Initializes the session and checks for the remember me cookie
404
 *
405
 * @return bool
406
 * @access private
407
 */
408
function _elgg_session_boot() {
409
410
	elgg_register_action('login', '', 'public');
411
	elgg_register_action('logout');
412
	register_pam_handler('pam_auth_userpass');
413
414
	$session = _elgg_services()->session;
415
	$session->start();
416
417
	// test whether we have a user session
418
	if ($session->has('guid')) {
419
		$user = _elgg_services()->entityTable->get($session->get('guid'), 'user');
420
		if (!$user) {
421
			// OMG user has been deleted.
422
			$session->invalidate();
423
			forward('');
424
		}
425
426
		$session->setLoggedInUser($user);
0 ignored issues
show
$user of type object<ElggEntity> is not a sub-type of object<ElggUser>. It seems like you assume a child class of the class ElggEntity to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
427
428
		_elgg_services()->persistentLogin->replaceLegacyToken($user);
0 ignored issues
show
$user of type object<ElggEntity> is not a sub-type of object<ElggUser>. It seems like you assume a child class of the class ElggEntity to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
429
	} else {
430
		$user = _elgg_services()->persistentLogin->bootSession();
431
		if ($user) {
432
			$session->setLoggedInUser($user);
433
		}
434
	}
435
436
	if ($session->has('guid')) {
437
		set_last_action($session->get('guid'));
438
	}
439
440
	// initialize the deprecated global session wrapper
441
	global $SESSION;
442
	$SESSION = new \Elgg\DeprecationWrapper($session, "\$SESSION is deprecated", 1.9);
443
444
	// logout a user with open session who has been banned
445
	$user = $session->getLoggedInUser();
446
	if ($user && $user->isBanned()) {
447
		logout();
448
		return false;
449
	}
450
451
	return true;
452
}
453