Issues (1686)

sources/Session.php (3 issues)

1
<?php
2
3
/**
4
 * Implementation of PHP's session API.
5
 *
6
 * What it does:
7
 *
8
 *  - It handles the session data in the database (more scalable.)
9
 *  - It uses the databaseSession_lifetime setting for garbage collection.
10
 *  - The custom session handler is set by loadSession().
11
 *
12
 * @package   ElkArte Forum
13
 * @copyright ElkArte Forum contributors
14
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
15
 *
16
 * This file contains code covered by:
17
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
18
 *
19
 * @version 2.0 dev
20
 *
21
 */
22
23
use ElkArte\Helper\TokenHash;
24
use ElkArte\Http\Headers;
25
use ElkArte\Sessions\SessionHandler\DatabaseHandler;
26
27
/**
28
 * Attempt to start the session, unless it already has been.
29
 */
30 1
function loadSession()
31
{
32
	global $modSettings, $boardurl, $context;
33 1
34 1
	// Attempt to change a few PHP settings.
35 1
	@ini_set('session.use_cookies', true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). 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

35
	/** @scrutinizer ignore-unhandled */ @ini_set('session.use_cookies', true);

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...
true of type true is incompatible with the type string expected by parameter $value of ini_set(). ( Ignorable by Annotation )

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

35
	@ini_set('session.use_cookies', /** @scrutinizer ignore-type */ true);
Loading history...
36 1
	@ini_set('session.use_only_cookies', false);
0 ignored issues
show
false of type false is incompatible with the type string expected by parameter $value of ini_set(). ( Ignorable by Annotation )

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

36
	@ini_set('session.use_only_cookies', /** @scrutinizer ignore-type */ false);
Loading history...
37 1
	@ini_set('url_rewriter.tags', '');
38
	@ini_set('session.use_trans_sid', false);
39
	@ini_set('arg_separator.output', '&amp;');
40 1
	// @todo admin panel setting?
41
	@ini_set('session.cookie_samesite', 'Lax');
42
43
	// Secure PHPSESSIONID
44
	if (parse_url($boardurl, PHP_URL_SCHEME) === 'https')
45 1
	{
46
		@ini_set('session.cookie_secure', true);
47
	}
48
49
	if (!empty($modSettings['globalCookies']))
50
	{
51
		$parsed_url = parse_url($boardurl);
52
53
		if (preg_match('~^\d{1,3}(\.\d{1,3}){3}$~', $parsed_url['host']) == 0 && preg_match('~(?:[^\.]+\.)?([^\.]{2,}\..+)\z~i', $parsed_url['host'], $parts) == 1)
54
		{
55
			@ini_set('session.cookie_domain', '.' . $parts[1]);
56
		}
57 1
	}
58
59
	// @todo Set the session cookie path?
60 1
	// If it's already been started... probably best to skip this.
61
	if ((ini_get('session.auto_start') == 1 && !empty($modSettings['databaseSession_enable'])) || session_id() == '')
62
	{
63
		// Attempt to end the already-started session.
64
		if (ini_get('session.auto_start') == 1)
65
		{
66 1
			session_write_close();
67
		}
68
69
		// This is here to stop people from using bad junky PHPSESSIDs.
70
		if (isset($_REQUEST[session_name()]) && preg_match('~^[A-Za-z0-9,-]{16,64}$~', $_REQUEST[session_name()]) == 0 && !isset($_COOKIE[session_name()]))
71
		{
72
			$tokenizer = new TokenHash();
73
			$session_id = hash('md5', hash('md5', 'elk_sess_' . time()) . $tokenizer->generate_hash(8));
74
			$_REQUEST[session_name()] = $session_id;
75
			$_GET[session_name()] = $session_id;
76 1
			$_POST[session_name()] = $session_id;
77
		}
78 1
79 1
		// Use database sessions?
80
		if (!empty($modSettings['databaseSession_enable']))
81 1
		{
82 1
			@ini_set('session.serialize_handler', 'php');
83 1
			@ini_set('session.gc_probability', '1');
84 1
85 1
			$handler = new DatabaseHandler(database());
86 1
			session_set_save_handler(
87 1
				[$handler, 'open'],
88 1
				[$handler, 'close'],
89
				static fn(string $sessionId): string => $handler->read($sessionId),
90
				static fn(string $sessionId, string $data): bool => $handler->write($sessionId, $data),
91
				static fn(string $sessionId): bool => $handler->destroy($sessionId),
92
				static fn(int $maxLifetime): int|bool => $handler->gc($maxLifetime)
93
			);
94
95
			/*
96
			 * Avoid unexpected side-effects from the way PHP
97 1
			 * internally destroys objects on shutdown.
98
			 *
99
			 * See notes on http://php.net/manual/en/function.session-set-save-handler.php
100
			 */
101
			register_shutdown_function('session_write_close');
102
		}
103
		elseif (ini_get('session.gc_maxlifetime') <= 1440 && !empty($modSettings['databaseSession_lifetime']))
104
		{
105
			@ini_set('session.gc_maxlifetime', max($modSettings['databaseSession_lifetime'], 60));
106
107
			// APC destroys static class members before sessions can be written.  To work around this we
108
			// explicitly call session_write_close on script end/exit bugs.php.net/bug.php?id=60657
109
			if (extension_loaded('apc') && ini_get('apc.enabled') && !extension_loaded('apcu'))
110
			{
111
				register_shutdown_function('session_write_close');
112 1
			}
113
		}
114
115 1
		// Start the session
116
		session_start();
117 1
118
		// Change it so the cache settings are a little looser than default.
119
		if (!empty($modSettings['databaseSession_loose']) || (isset($_REQUEST['action']) && $_REQUEST['action'] === 'search'))
120
		{
121
			Headers::instance()->header('Cache-Control', 'private');
122 1
		}
123
	}
124 1
125 1
	// Set the randomly generated code.
126 1
	if (!isset($_SESSION['session_var']))
127
	{
128
		$tokenizer = new TokenHash();
129
		$_SESSION['session_value'] = $tokenizer->generate_hash(32, session_id());
130 1
		$_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', $tokenizer->generate_hash(16, session_id())), 0, rand(7, 12));
131 1
	}
132 1
133
	// For injection into hidden form fields...
134
	$context['session_var'] = $_SESSION['session_var'];
135
	$context['session_id'] = $_SESSION['session_value'];
136
}
137