Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Session.php « core - github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d7ee14beb49c247f188647b1eed53e8837511cff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
<?php
/**
 * Piwik - free/libre analytics platform
 *
 * @link https://matomo.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 *
 */
namespace Piwik;

use Exception;
use Piwik\Container\StaticContainer;
use Piwik\Exception\MissingFilePermissionException;
use Piwik\Session\SaveHandler\DbTable;
use Psr\Log\LoggerInterface;
use Zend_Session;

/**
 * Session initialization.
 */
class Session extends Zend_Session
{
    const SESSION_NAME = 'MATOMO_SESSID';

    public static $sessionName = self::SESSION_NAME;

    protected static $sessionStarted = false;

    /**
     * Start the session
     *
     * @param array|bool $options An array of configuration options; the auto-start (bool) setting is ignored
     * @return void
     * @throws Exception if starting a session fails
     */
    public static function start($options = false)
    {
        if (headers_sent()
            || self::$sessionStarted
            || (defined('PIWIK_ENABLE_SESSION_START') && !PIWIK_ENABLE_SESSION_START)
            || session_status() == PHP_SESSION_ACTIVE
        ) {
            return;
        }
        self::$sessionStarted = true;
    
        if (defined('PIWIK_SESSION_NAME')) {
            self::$sessionName = PIWIK_SESSION_NAME;
        }

        $config = Config::getInstance();

        // use cookies to store session id on the client side
        @ini_set('session.use_cookies', '1');

        // prevent attacks involving session ids passed in URLs
        @ini_set('session.use_only_cookies', '1');

        // advise browser that session cookie should only be sent over secure connection
        if (ProxyHttp::isHttps()) {
            @ini_set('session.cookie_secure', '1');
        }

        // advise browser that session cookie should only be accessible through the HTTP protocol (i.e., not JavaScript)
        @ini_set('session.cookie_httponly', '1');

        // don't use the default: PHPSESSID
        @ini_set('session.name', self::$sessionName);

        // proxies may cause the referer check to fail and
        // incorrectly invalidate the session
        @ini_set('session.referer_check', '');

        // to preserve previous behavior piwik_auth provided when it contained a token_auth, we ensure
        // the session data won't be deleted until the cookie expires.
        @ini_set('session.gc_maxlifetime', $config->General['login_cookie_expire']);

        @ini_set('session.cookie_path', empty($config->General['login_cookie_path']) ? '/' : $config->General['login_cookie_path']);

        $currentSaveHandler = ini_get('session.save_handler');

        if (!SettingsPiwik::isMatomoInstalled()) {
            // Note: this handler doesn't work well in load-balanced environments and may have a concurrency issue with locked session files

            // for "files", use our own folder to prevent local session file hijacking
            $sessionPath = self::getSessionsDirectory();
            // We always call mkdir since it also chmods the directory which might help when permissions were reverted for some reasons
            Filesystem::mkdir($sessionPath);

            @ini_set('session.save_handler', 'files');
            @ini_set('session.save_path', $sessionPath);
        } else {
            // as of Matomo 3.7.0 we only support files session handler during installation

            // We consider these to be misconfigurations, in that:
            // - user  - we can't verify that user-defined session handler functions have already been set via session_set_save_handler()
            // - mm    - this handler is not recommended, unsupported, not available for Windows, and has a potential concurrency issue

            if (@ini_get('session.serialize_handler') !== 'php_serialize') {
                @ini_set('session.serialize_handler', 'php_serialize');
            }

            $config = array(
                'name'           => Common::prefixTable(DbTable::TABLE_NAME),
                'primary'        => 'id',
                'modifiedColumn' => 'modified',
                'dataColumn'     => 'data',
                'lifetimeColumn' => 'lifetime',
            );

            $saveHandler = new DbTable($config);
            if ($saveHandler) {
                self::setSaveHandler($saveHandler);
            }
        }

        // garbage collection may disabled by default (e.g., Debian)
        if (ini_get('session.gc_probability') == 0) {
            @ini_set('session.gc_probability', 1);
        }

        try {
            parent::start();
            register_shutdown_function(array('Zend_Session', 'writeClose'), true);
        } catch (Exception $e) {
            StaticContainer::get(LoggerInterface::class)->error('Unable to start session: {exception}', [
                'exception' => $e,
                'ignoreInScreenWriter' => true,
            ]);

            if (SettingsPiwik::isMatomoInstalled()) {
                $pathToSessions = '';
            } else {
                $pathToSessions = Filechecks::getErrorMessageMissingPermissions(self::getSessionsDirectory());
            }
            
            $message = sprintf("Error: %s %s\n<pre>Debug: the original error was \n%s</pre>",
                Piwik::translate('General_ExceptionUnableToStartSession'),
                $pathToSessions,
                $e->getMessage()
            );

            $ex = new MissingFilePermissionException($message, $e->getCode(), $e);
            $ex->setIsHtmlMessage();

            throw $ex;
        }
    }

    /**
     * Returns the directory session files are stored in.
     *
     * @return string
     */
    public static function getSessionsDirectory()
    {
        return StaticContainer::get('path.tmp') . '/sessions';
    }

    public static function close()
    {
        if (self::isSessionStarted()) {
            // only write/close session if the session was actually started by us
            // otherwise we will set the session values to base64 encoded and whoever the session started might not expect the values in that way
            parent::writeClose();
        }
    }

    public static function isSessionStarted()
    {
        return self::$sessionStarted;
    }

    public static function getSameSiteCookieValue()
    {
        $config = Config::getInstance();
        $general = $config->General;
        if (!empty($general['enable_framed_pages']) && ProxyHttp::isHttps()) {
            return 'None';
        }

        return 'Lax';
    }

    /**
     * Write cookie header.  Similar to the native setcookie() function but also supports
     * the SameSite cookie property.
     * @param $name
     * @param $value
     * @param int $expires
     * @param string $path
     * @param string $domain
     * @param bool $secure
     * @param bool $httpOnly
     * @param string $sameSite
     * @return string
     */
    public static function writeCookie($name, $value, $expires = 0, $path = '/', $domain = '/', $secure = false, $httpOnly = false, $sameSite = 'lax')
    {
        $headerStr = 'Set-Cookie: ' . rawurlencode($name) . '=' . rawurlencode($value);
        if ($expires) {
            $headerStr .= '; expires=' . rawurlencode($expires);
        }
        if ($path) {
            $headerStr .= '; path=' . rawurlencode($path);
        }
        if ($domain) {
            $headerStr .= '; domain=' . rawurlencode($domain);
        }
        if ($secure) {
            $headerStr .= '; secure';
        }
        if ($httpOnly) {
            $headerStr .= '; httponly';
        }
        if ($sameSite) {
            $headerStr .= '; SameSite=' . rawurlencode($sameSite);
        }
        return $headerStr;
    }
}