Subversion Repositories oidplus

Rev

Rev 86 | 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
 
85 daniel-mar 20
define('SESSION_LIFETIME', 10*60); // TODO: Configure
21
 
2 daniel-mar 22
class OIDplusSessionHandler {
23
 
24
        protected $secret = '';
25
 
26
        function __construct($secret) {
27
                // **PREVENTING SESSION HIJACKING**
28
                // Prevents javascript XSS attacks aimed to steal the session ID
87 daniel-mar 29
                @ini_set('session.cookie_httponly', 1);
2 daniel-mar 30
 
31
                // **PREVENTING SESSION FIXATION**
32
                // Session ID cannot be passed through URLs
87 daniel-mar 33
                @ini_set('session.use_only_cookies', 1);
2 daniel-mar 34
 
87 daniel-mar 35
                @ini_set('session.use_trans_sid', 0);
85 daniel-mar 36
 
2 daniel-mar 37
                // Uses a secure connection (HTTPS) if possible
87 daniel-mar 38
                @ini_set('session.cookie_secure', OIDPLUS_SSL_AVAILABLE);
2 daniel-mar 39
 
83 daniel-mar 40
                $path = OIDplus::system_url(true);
41
                if (!empty($path)) {
87 daniel-mar 42
                        @ini_set('session.cookie_path', $path);
2 daniel-mar 43
                }
44
 
87 daniel-mar 45
                @ini_set('session.cookie_samesite', 'Lax');
2 daniel-mar 46
 
87 daniel-mar 47
                @ini_set('session.use_strict_mode', 1);
2 daniel-mar 48
 
87 daniel-mar 49
                @ini_set('session.gc_maxlifetime', SESSION_LIFETIME);
85 daniel-mar 50
 
2 daniel-mar 51
                $this->secret = $secret;
85 daniel-mar 52
 
53
                session_name('OIDPLUS_SESHDLR');
2 daniel-mar 54
        }
55
 
86 daniel-mar 56
        protected function sessionSafeStart() {
57
                @session_start();
58
 
87 daniel-mar 59
                if (!isset($_SESSION['ip'])) {
60
                        if (!isset($_SERVER['REMOTE_ADDR'])) return;
86 daniel-mar 61
 
87 daniel-mar 62
                        // Remember the IP address of the user
63
                        $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
64
                } else {
65
                        if ($_SERVER['REMOTE_ADDR'] != $_SESSION['ip']) {
66
                                // Was the session hijacked?! Get out of here!
67
                                $this->destroySession();
68
                        }
86 daniel-mar 69
                }
70
        }
71
 
2 daniel-mar 72
        function __destruct() {
73
                session_write_close();
74
        }
75
 
76
        public function setValue($name, $value) {
86 daniel-mar 77
                $this->sessionSafeStart();
85 daniel-mar 78
                setcookie(session_name(),session_id(),time()+SESSION_LIFETIME, ini_get('session.cookie_path'));
79
 
2 daniel-mar 80
                $_SESSION[$name] = self::encrypt($value, $this->secret);
81
        }
82
 
83
        public function getValue($name) {
85 daniel-mar 84
                if (!isset($_COOKIE[session_name()])) return null; // GDPR: Only start a session when we really need one
85
 
86 daniel-mar 86
                $this->sessionSafeStart();
85 daniel-mar 87
                setcookie(session_name(),session_id(),time()+SESSION_LIFETIME, ini_get('session.cookie_path'));
88
 
2 daniel-mar 89
                if (!isset($_SESSION[$name])) return null;
90
                return self::decrypt($_SESSION[$name], $this->secret);
91
        }
92
 
85 daniel-mar 93
        public function destroySession() {
94
                if (!isset($_COOKIE[session_name()])) return;
95
 
86 daniel-mar 96
                $this->sessionSafeStart();
85 daniel-mar 97
                setcookie(session_name(),session_id(),time()+SESSION_LIFETIME, ini_get('session.cookie_path'));
98
 
99
                $_SESSION = array();
100
                session_destroy();
101
                session_write_close();
102
                setcookie(session_name(), "", time()-3600, ini_get('session.cookie_path')); // remove cookie, so GDPR people are happy
103
        }
104
 
83 daniel-mar 105
        public function exists($name) {
106
                return isset($_SESSION[$name]);
107
        }
108
 
2 daniel-mar 109
        protected static function encrypt($data, $key) {
6 daniel-mar 110
                $iv = random_bytes(16); // AES block size in CBC mode
111
                // Encryption
112
                $ciphertext = openssl_encrypt(
113
                        $data,
114
                        'AES-256-CBC',
115
                        mb_substr($key, 0, 32, '8bit'),
116
                        OPENSSL_RAW_DATA,
117
                        $iv
118
                );
119
                // Authentication
120
                $hmac = hash_hmac(
121
                        'SHA256',
122
                        $iv . $ciphertext,
123
                        mb_substr($key, 32, null, '8bit'),
124
                        true
125
                );
126
                return $hmac . $iv . $ciphertext;
127
        }
2 daniel-mar 128
 
6 daniel-mar 129
        protected static function decrypt($data, $key) {
130
                $hmac       = mb_substr($data, 0, 32, '8bit');
131
                $iv         = mb_substr($data, 32, 16, '8bit');
132
                $ciphertext = mb_substr($data, 48, null, '8bit');
133
                // Authentication
134
                $hmacNew = hash_hmac(
135
                        'SHA256',
136
                        $iv . $ciphertext,
137
                        mb_substr($key, 32, null, '8bit'),
138
                        true
139
                );
140
                if (!hash_equals($hmac, $hmacNew)) {
141
                        throw new Exception('Authentication failed');
142
                }
143
                // Decryption
144
                return openssl_decrypt(
145
                        $ciphertext,
146
                        'AES-256-CBC',
147
                        mb_substr($key, 0, 32, '8bit'),
148
                        OPENSSL_RAW_DATA,
149
                        $iv
150
                );
151
        }
2 daniel-mar 152
}