Subversion Repositories oidplus

Rev

Rev 277 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
5
 * Copyright 2019 Daniel Marschall, ViaThinkSoft
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
20
class OIDplusSessionHandler {
21
 
263 daniel-mar 22
        private $secret = '';
23
        protected $sessionLifetime = '';
2 daniel-mar 24
 
263 daniel-mar 25
        public function __construct() {
261 daniel-mar 26
                $this->sessionLifetime = OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60);
27
                $this->secret = OIDplus::baseConfig()->getValue('SERVER_SECRET');
28
 
2 daniel-mar 29
                // **PREVENTING SESSION HIJACKING**
30
                // Prevents javascript XSS attacks aimed to steal the session ID
87 daniel-mar 31
                @ini_set('session.cookie_httponly', 1);
2 daniel-mar 32
 
33
                // **PREVENTING SESSION FIXATION**
34
                // Session ID cannot be passed through URLs
87 daniel-mar 35
                @ini_set('session.use_only_cookies', 1);
2 daniel-mar 36
 
87 daniel-mar 37
                @ini_set('session.use_trans_sid', 0);
85 daniel-mar 38
 
2 daniel-mar 39
                // Uses a secure connection (HTTPS) if possible
230 daniel-mar 40
                @ini_set('session.cookie_secure', OIDplus::isSslAvailable());
2 daniel-mar 41
 
227 daniel-mar 42
                $path = OIDplus::getSystemUrl(true);
83 daniel-mar 43
                if (!empty($path)) {
87 daniel-mar 44
                        @ini_set('session.cookie_path', $path);
2 daniel-mar 45
                }
46
 
87 daniel-mar 47
                @ini_set('session.cookie_samesite', 'Lax');
2 daniel-mar 48
 
87 daniel-mar 49
                @ini_set('session.use_strict_mode', 1);
2 daniel-mar 50
 
261 daniel-mar 51
                @ini_set('session.gc_maxlifetime', $this->sessionLifetime);
2 daniel-mar 52
        }
53
 
86 daniel-mar 54
        protected function sessionSafeStart() {
179 daniel-mar 55
                if (!isset($_SESSION)) {
56
                        // TODO: session_name() makes some problems. Leave it away for now.
261 daniel-mar 57
                        //session_name('OIDplus_SESHDLR');
179 daniel-mar 58
                        if (!session_start()) {
360 daniel-mar 59
                                throw new OIDplusException(_L('Session could not be started'));
179 daniel-mar 60
                        }
61
                }
86 daniel-mar 62
 
87 daniel-mar 63
                if (!isset($_SESSION['ip'])) {
64
                        if (!isset($_SERVER['REMOTE_ADDR'])) return;
86 daniel-mar 65
 
87 daniel-mar 66
                        // Remember the IP address of the user
67
                        $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
68
                } else {
69
                        if ($_SERVER['REMOTE_ADDR'] != $_SESSION['ip']) {
70
                                // Was the session hijacked?! Get out of here!
71
                                $this->destroySession();
72
                        }
86 daniel-mar 73
                }
74
        }
75
 
2 daniel-mar 76
        function __destruct() {
77
                session_write_close();
78
        }
79
 
80
        public function setValue($name, $value) {
86 daniel-mar 81
                $this->sessionSafeStart();
261 daniel-mar 82
                setcookie(session_name(),session_id(),time()+$this->sessionLifetime, ini_get('session.cookie_path'));
85 daniel-mar 83
 
2 daniel-mar 84
                $_SESSION[$name] = self::encrypt($value, $this->secret);
85
        }
86
 
87
        public function getValue($name) {
85 daniel-mar 88
                if (!isset($_COOKIE[session_name()])) return null; // GDPR: Only start a session when we really need one
89
 
86 daniel-mar 90
                $this->sessionSafeStart();
261 daniel-mar 91
                setcookie(session_name(),session_id(),time()+$this->sessionLifetime, ini_get('session.cookie_path'));
85 daniel-mar 92
 
2 daniel-mar 93
                if (!isset($_SESSION[$name])) return null;
94
                return self::decrypt($_SESSION[$name], $this->secret);
95
        }
96
 
85 daniel-mar 97
        public function destroySession() {
98
                if (!isset($_COOKIE[session_name()])) return;
99
 
86 daniel-mar 100
                $this->sessionSafeStart();
261 daniel-mar 101
                setcookie(session_name(),session_id(),time()+$this->sessionLifetime, ini_get('session.cookie_path'));
85 daniel-mar 102
 
103
                $_SESSION = array();
104
                session_destroy();
105
                session_write_close();
106
                setcookie(session_name(), "", time()-3600, ini_get('session.cookie_path')); // remove cookie, so GDPR people are happy
107
        }
108
 
83 daniel-mar 109
        public function exists($name) {
110
                return isset($_SESSION[$name]);
111
        }
112
 
2 daniel-mar 113
        protected static function encrypt($data, $key) {
6 daniel-mar 114
                $iv = random_bytes(16); // AES block size in CBC mode
115
                // Encryption
116
                $ciphertext = openssl_encrypt(
117
                        $data,
118
                        'AES-256-CBC',
119
                        mb_substr($key, 0, 32, '8bit'),
120
                        OPENSSL_RAW_DATA,
121
                        $iv
122
                );
123
                // Authentication
124
                $hmac = hash_hmac(
125
                        'SHA256',
126
                        $iv . $ciphertext,
127
                        mb_substr($key, 32, null, '8bit'),
128
                        true
129
                );
130
                return $hmac . $iv . $ciphertext;
131
        }
2 daniel-mar 132
 
6 daniel-mar 133
        protected static function decrypt($data, $key) {
134
                $hmac       = mb_substr($data, 0, 32, '8bit');
135
                $iv         = mb_substr($data, 32, 16, '8bit');
136
                $ciphertext = mb_substr($data, 48, null, '8bit');
137
                // Authentication
138
                $hmacNew = hash_hmac(
139
                        'SHA256',
140
                        $iv . $ciphertext,
141
                        mb_substr($key, 32, null, '8bit'),
142
                        true
143
                );
144
                if (!hash_equals($hmac, $hmacNew)) {
360 daniel-mar 145
                        throw new OIDplusException(_L('Authentication failed'));
6 daniel-mar 146
                }
147
                // Decryption
148
                return openssl_decrypt(
149
                        $ciphertext,
150
                        'AES-256-CBC',
151
                        mb_substr($key, 0, 32, '8bit'),
152
                        OPENSSL_RAW_DATA,
153
                        $iv
154
                );
155
        }
360 daniel-mar 156
}