Subversion Repositories oidplus

Rev

Rev 277 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  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.  
  22.         private $secret = '';
  23.         protected $sessionLifetime = '';
  24.  
  25.         public function __construct() {
  26.                 $this->sessionLifetime = OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60);
  27.                 $this->secret = OIDplus::baseConfig()->getValue('SERVER_SECRET');
  28.  
  29.                 // **PREVENTING SESSION HIJACKING**
  30.                 // Prevents javascript XSS attacks aimed to steal the session ID
  31.                 @ini_set('session.cookie_httponly', 1);
  32.  
  33.                 // **PREVENTING SESSION FIXATION**
  34.                 // Session ID cannot be passed through URLs
  35.                 @ini_set('session.use_only_cookies', 1);
  36.  
  37.                 @ini_set('session.use_trans_sid', 0);
  38.  
  39.                 // Uses a secure connection (HTTPS) if possible
  40.                 @ini_set('session.cookie_secure', OIDplus::isSslAvailable());
  41.  
  42.                 $path = OIDplus::getSystemUrl(true);
  43.                 if (!empty($path)) {
  44.                         @ini_set('session.cookie_path', $path);
  45.                 }
  46.  
  47.                 @ini_set('session.cookie_samesite', 'Lax');
  48.  
  49.                 @ini_set('session.use_strict_mode', 1);
  50.  
  51.                 @ini_set('session.gc_maxlifetime', $this->sessionLifetime);
  52.         }
  53.  
  54.         protected function sessionSafeStart() {
  55.                 if (!isset($_SESSION)) {
  56.                         // TODO: session_name() makes some problems. Leave it away for now.
  57.                         //session_name('OIDplus_SESHDLR');
  58.                         if (!session_start()) {
  59.                                 throw new OIDplusException(_L('Session could not be started'));
  60.                         }
  61.                 }
  62.  
  63.                 if (!isset($_SESSION['ip'])) {
  64.                         if (!isset($_SERVER['REMOTE_ADDR'])) return;
  65.  
  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.                         }
  73.                 }
  74.         }
  75.  
  76.         function __destruct() {
  77.                 session_write_close();
  78.         }
  79.  
  80.         public function setValue($name, $value) {
  81.                 $this->sessionSafeStart();
  82.                 setcookie(session_name(),session_id(),time()+$this->sessionLifetime, ini_get('session.cookie_path'));
  83.  
  84.                 $_SESSION[$name] = self::encrypt($value, $this->secret);
  85.         }
  86.  
  87.         public function getValue($name) {
  88.                 if (!isset($_COOKIE[session_name()])) return null; // GDPR: Only start a session when we really need one
  89.  
  90.                 $this->sessionSafeStart();
  91.                 setcookie(session_name(),session_id(),time()+$this->sessionLifetime, ini_get('session.cookie_path'));
  92.  
  93.                 if (!isset($_SESSION[$name])) return null;
  94.                 return self::decrypt($_SESSION[$name], $this->secret);
  95.         }
  96.  
  97.         public function destroySession() {
  98.                 if (!isset($_COOKIE[session_name()])) return;
  99.  
  100.                 $this->sessionSafeStart();
  101.                 setcookie(session_name(),session_id(),time()+$this->sessionLifetime, ini_get('session.cookie_path'));
  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.  
  109.         public function exists($name) {
  110.                 return isset($_SESSION[$name]);
  111.         }
  112.  
  113.         protected static function encrypt($data, $key) {
  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.         }
  132.  
  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)) {
  145.                         throw new OIDplusException(_L('Authentication failed'));
  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.         }
  156. }