1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
23:
24: App::uses('Hash', 'Utility');
25: App::uses('Security', 'Utility');
26:
27: 28: 29: 30: 31: 32: 33: 34:
35: class CakeSession {
36:
37: 38: 39: 40: 41:
42: public static $valid = false;
43:
44: 45: 46: 47: 48:
49: public static $error = false;
50:
51: 52: 53: 54: 55:
56: protected static $_userAgent = '';
57:
58: 59: 60: 61: 62:
63: public static $path = '/';
64:
65: 66: 67: 68: 69:
70: public static $lastError = null;
71:
72: 73: 74: 75: 76:
77: public static $time = false;
78:
79: 80: 81: 82: 83:
84: public static $cookieLifeTime;
85:
86: 87: 88: 89: 90:
91: public static $sessionTime = false;
92:
93: 94: 95: 96: 97:
98: public static $id = null;
99:
100: 101: 102: 103: 104:
105: public static $host = null;
106:
107: 108: 109: 110: 111:
112: public static $timeout = null;
113:
114: 115: 116: 117: 118: 119: 120:
121: public static $requestCountdown = 10;
122:
123: 124: 125: 126: 127:
128: protected static $_initialized = false;
129:
130: 131: 132: 133: 134:
135: protected static $_cookieName = null;
136:
137: 138: 139: 140: 141:
142: protected static $_useForwardsCompatibleTimeout = false;
143:
144: 145: 146: 147: 148:
149: protected static $_isCLI = false;
150:
151: 152: 153: 154: 155: 156:
157: public static function init($base = null) {
158: static::$time = time();
159:
160: if (env('HTTP_USER_AGENT') && !static::$_userAgent) {
161: static::$_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
162: }
163:
164: static::_setPath($base);
165: static::_setHost(env('HTTP_HOST'));
166:
167: if (!static::$_initialized) {
168: register_shutdown_function('session_write_close');
169: }
170:
171: static::$_initialized = true;
172: static::$_isCLI = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg');
173: }
174:
175: 176: 177: 178: 179: 180:
181: protected static function _setPath($base = null) {
182: if (empty($base)) {
183: static::$path = '/';
184: return;
185: }
186: if (strpos($base, 'index.php') !== false) {
187: $base = str_replace('index.php', '', $base);
188: }
189: if (strpos($base, '?') !== false) {
190: $base = str_replace('?', '', $base);
191: }
192: static::$path = $base;
193: }
194:
195: 196: 197: 198: 199: 200:
201: protected static function _setHost($host) {
202: static::$host = $host;
203: if (strpos(static::$host, ':') !== false) {
204: static::$host = substr(static::$host, 0, strpos(static::$host, ':'));
205: }
206: }
207:
208: 209: 210: 211: 212:
213: public static function start() {
214: if (static::started()) {
215: return true;
216: }
217:
218: $id = static::id();
219: static::_startSession();
220: if (!$id && static::started()) {
221: static::_checkValid();
222: }
223:
224: static::$error = false;
225: static::$valid = true;
226: return static::started();
227: }
228:
229: 230: 231: 232: 233:
234: public static function started() {
235: if (function_exists('session_status')) {
236: return isset($_SESSION) && (session_status() === PHP_SESSION_ACTIVE);
237: }
238: return isset($_SESSION) && session_id();
239: }
240:
241: 242: 243: 244: 245: 246:
247: public static function check($name) {
248: if (!static::_hasSession() || !static::start()) {
249: return false;
250: }
251: if (isset($_SESSION[$name])) {
252: return true;
253: }
254:
255: return Hash::get($_SESSION, $name) !== null;
256: }
257:
258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271:
272: public static function id($id = null) {
273: if ($id) {
274: static::$id = $id;
275: session_id(static::$id);
276: }
277: if (static::started()) {
278: return session_id();
279: }
280: return static::$id;
281: }
282:
283: 284: 285: 286: 287: 288:
289: public static function delete($name) {
290: if (static::check($name)) {
291: static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
292: return !static::check($name);
293: }
294: return false;
295: }
296:
297: 298: 299: 300: 301: 302: 303:
304: protected static function _overwrite(&$old, $new) {
305: if (!empty($old)) {
306: foreach ($old as $key => $var) {
307: if (!isset($new[$key])) {
308: unset($old[$key]);
309: }
310: }
311: }
312: foreach ($new as $key => $var) {
313: $old[$key] = $var;
314: }
315: }
316:
317: 318: 319: 320: 321: 322:
323: protected static function _error($errorNumber) {
324: if (!is_array(static::$error) || !array_key_exists($errorNumber, static::$error)) {
325: return false;
326: }
327: return static::$error[$errorNumber];
328: }
329:
330: 331: 332: 333: 334:
335: public static function error() {
336: if (static::$lastError) {
337: return static::_error(static::$lastError);
338: }
339: return false;
340: }
341:
342: 343: 344: 345: 346:
347: public static function valid() {
348: if (static::start() && static::read('Config')) {
349: if (static::_validAgentAndTime() && static::$error === false) {
350: static::$valid = true;
351: } else {
352: static::$valid = false;
353: static::_setError(1, 'Session Highjacking Attempted !!!');
354: }
355: }
356: return static::$valid;
357: }
358:
359: 360: 361: 362: 363: 364: 365: 366:
367: protected static function _validAgentAndTime() {
368: $userAgent = static::read('Config.userAgent');
369: $time = static::read('Config.time');
370: if (static::$_useForwardsCompatibleTimeout) {
371: $time += (Configure::read('Session.timeout') * 60);
372: }
373: $validAgent = (
374: Configure::read('Session.checkAgent') === false ||
375: isset($userAgent) && static::$_userAgent === $userAgent
376: );
377: return ($validAgent && static::$time <= $time);
378: }
379:
380: 381: 382: 383: 384: 385:
386: public static function userAgent($userAgent = null) {
387: if ($userAgent) {
388: static::$_userAgent = $userAgent;
389: }
390: if (empty(static::$_userAgent)) {
391: CakeSession::init(static::$path);
392: }
393: return static::$_userAgent;
394: }
395:
396: 397: 398: 399: 400: 401: 402:
403: public static function read($name = null) {
404: if (!static::_hasSession() || !static::start()) {
405: return null;
406: }
407: if ($name === null) {
408: return static::_returnSessionVars();
409: }
410: $result = Hash::get($_SESSION, $name);
411:
412: if (isset($result)) {
413: return $result;
414: }
415: return null;
416: }
417:
418: 419: 420: 421: 422:
423: protected static function _returnSessionVars() {
424: if (!empty($_SESSION)) {
425: return $_SESSION;
426: }
427: static::_setError(2, 'No Session vars set');
428: return false;
429: }
430:
431: 432: 433: 434: 435: 436: 437:
438: public static function write($name, $value = null) {
439: if (!static::start()) {
440: return false;
441: }
442:
443: $write = $name;
444: if (!is_array($name)) {
445: $write = array($name => $value);
446: }
447: foreach ($write as $key => $val) {
448: static::_overwrite($_SESSION, Hash::insert($_SESSION, $key, $val));
449: if (Hash::get($_SESSION, $key) !== $val) {
450: return false;
451: }
452: }
453: return true;
454: }
455:
456: 457: 458: 459: 460: 461: 462:
463: public static function consume($name) {
464: if (empty($name)) {
465: return null;
466: }
467: $value = static::read($name);
468: if ($value !== null) {
469: static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
470: }
471: return $value;
472: }
473:
474: 475: 476: 477: 478:
479: public static function destroy() {
480: if (!static::started()) {
481: static::_startSession();
482: }
483:
484: if (static::started()) {
485: if (session_id() && static::_hasSession()) {
486: session_write_close();
487: session_start();
488: }
489: session_destroy();
490: unset($_COOKIE[static::_cookieName()]);
491: }
492:
493: $_SESSION = null;
494: static::$id = null;
495: static::$_cookieName = null;
496: }
497:
498: 499: 500: 501: 502: 503: 504: 505:
506: public static function clear($renew = true) {
507: if (!$renew) {
508: $_SESSION = array();
509: return;
510: }
511:
512: $_SESSION = null;
513: static::$id = null;
514: static::renew();
515: }
516:
517: 518: 519: 520: 521: 522: 523: 524:
525: protected static function _configureSession() {
526: $sessionConfig = Configure::read('Session');
527:
528: if (isset($sessionConfig['defaults'])) {
529: $defaults = static::_defaultConfig($sessionConfig['defaults']);
530: if ($defaults) {
531: $sessionConfig = Hash::merge($defaults, $sessionConfig);
532: }
533: }
534: if (!isset($sessionConfig['ini']['session.cookie_secure']) && env('HTTPS')) {
535: $sessionConfig['ini']['session.cookie_secure'] = 1;
536: }
537: if (isset($sessionConfig['timeout']) && !isset($sessionConfig['cookieTimeout'])) {
538: $sessionConfig['cookieTimeout'] = $sessionConfig['timeout'];
539: }
540: if (isset($sessionConfig['useForwardsCompatibleTimeout']) && $sessionConfig['useForwardsCompatibleTimeout']) {
541: static::$_useForwardsCompatibleTimeout = true;
542: }
543:
544: if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) {
545: $sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60;
546: }
547:
548: if (!isset($sessionConfig['ini']['session.name'])) {
549: $sessionConfig['ini']['session.name'] = $sessionConfig['cookie'];
550: }
551: static::$_cookieName = $sessionConfig['ini']['session.name'];
552:
553: if (!empty($sessionConfig['handler'])) {
554: $sessionConfig['ini']['session.save_handler'] = 'user';
555:
556:
557:
558: if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
559: unset($sessionConfig['ini']['session.save_handler']);
560: }
561: } elseif (!empty($sessionConfig['session.save_path']) && Configure::read('debug')) {
562: if (!is_dir($sessionConfig['session.save_path'])) {
563: mkdir($sessionConfig['session.save_path'], 0775, true);
564: }
565: }
566:
567: if (!isset($sessionConfig['ini']['session.gc_maxlifetime'])) {
568: $sessionConfig['ini']['session.gc_maxlifetime'] = $sessionConfig['timeout'] * 60;
569: }
570: if (!isset($sessionConfig['ini']['session.cookie_httponly'])) {
571: $sessionConfig['ini']['session.cookie_httponly'] = 1;
572: }
573:
574: if (!isset($sessionConfig['cacheLimiter'])) {
575: $sessionConfig['cacheLimiter'] = 'must-revalidate';
576: }
577:
578: if (empty($_SESSION) && !headers_sent() && (!function_exists('session_status') || session_status() !== PHP_SESSION_ACTIVE)) {
579: if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
580: foreach ($sessionConfig['ini'] as $setting => $value) {
581: if (ini_set($setting, $value) === false) {
582: throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
583: }
584: }
585: }
586: }
587: if (!empty($sessionConfig['handler']) && !isset($sessionConfig['handler']['engine'])) {
588: call_user_func_array('session_set_save_handler', $sessionConfig['handler']);
589: }
590: if (!empty($sessionConfig['handler']['engine']) && !headers_sent()) {
591: $handler = static::_getHandler($sessionConfig['handler']['engine']);
592: if (!function_exists('session_status') || session_status() !== PHP_SESSION_ACTIVE) {
593: session_set_save_handler(
594: array($handler, 'open'),
595: array($handler, 'close'),
596: array($handler, 'read'),
597: array($handler, 'write'),
598: array($handler, 'destroy'),
599: array($handler, 'gc')
600: );
601: }
602: }
603: Configure::write('Session', $sessionConfig);
604: static::$sessionTime = static::$time;
605: if (!static::$_useForwardsCompatibleTimeout) {
606: static::$sessionTime += ($sessionConfig['timeout'] * 60);
607: }
608: }
609:
610: 611: 612: 613: 614:
615: protected static function _cookieName() {
616: if (static::$_cookieName !== null) {
617: return static::$_cookieName;
618: }
619:
620: static::init();
621: static::_configureSession();
622:
623: return static::$_cookieName = session_name();
624: }
625:
626: 627: 628: 629: 630:
631: protected static function _hasSession() {
632: return static::started()
633: || !ini_get('session.use_cookies')
634: || isset($_COOKIE[static::_cookieName()])
635: || static::$_isCLI
636: || (ini_get('session.use_trans_sid') && isset($_GET[session_name()]));
637: }
638:
639: 640: 641: 642: 643: 644: 645:
646: protected static function _getHandler($handler) {
647: list($plugin, $class) = pluginSplit($handler, true);
648: App::uses($class, $plugin . 'Model/Datasource/Session');
649: if (!class_exists($class)) {
650: throw new CakeSessionException(__d('cake_dev', 'Could not load %s to handle the session.', $class));
651: }
652: $handler = new $class();
653: if ($handler instanceof CakeSessionHandlerInterface) {
654: return $handler;
655: }
656: throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.'));
657: }
658:
659: 660: 661: 662: 663: 664:
665: protected static function _defaultConfig($name) {
666: $defaults = array(
667: 'php' => array(
668: 'cookie' => 'CAKEPHP',
669: 'timeout' => 240,
670: 'ini' => array(
671: 'session.use_trans_sid' => 0,
672: 'session.cookie_path' => static::$path
673: )
674: ),
675: 'cake' => array(
676: 'cookie' => 'CAKEPHP',
677: 'timeout' => 240,
678: 'ini' => array(
679: 'session.use_trans_sid' => 0,
680: 'url_rewriter.tags' => '',
681: 'session.serialize_handler' => 'php',
682: 'session.use_cookies' => 1,
683: 'session.cookie_path' => static::$path,
684: 'session.save_path' => TMP . 'sessions',
685: 'session.save_handler' => 'files'
686: )
687: ),
688: 'cache' => array(
689: 'cookie' => 'CAKEPHP',
690: 'timeout' => 240,
691: 'ini' => array(
692: 'session.use_trans_sid' => 0,
693: 'url_rewriter.tags' => '',
694: 'session.use_cookies' => 1,
695: 'session.cookie_path' => static::$path,
696: 'session.save_handler' => 'user',
697: ),
698: 'handler' => array(
699: 'engine' => 'CacheSession',
700: 'config' => 'default'
701: )
702: ),
703: 'database' => array(
704: 'cookie' => 'CAKEPHP',
705: 'timeout' => 240,
706: 'ini' => array(
707: 'session.use_trans_sid' => 0,
708: 'url_rewriter.tags' => '',
709: 'session.use_cookies' => 1,
710: 'session.cookie_path' => static::$path,
711: 'session.save_handler' => 'user',
712: 'session.serialize_handler' => 'php',
713: ),
714: 'handler' => array(
715: 'engine' => 'DatabaseSession',
716: 'model' => 'Session'
717: )
718: )
719: );
720: if (isset($defaults[$name])) {
721: return $defaults[$name];
722: }
723: return false;
724: }
725:
726: 727: 728: 729: 730:
731: protected static function _startSession() {
732: static::init();
733: session_write_close();
734: static::_configureSession();
735:
736: if (headers_sent()) {
737: if (empty($_SESSION)) {
738: $_SESSION = array();
739: }
740: } else {
741: $limit = Configure::read('Session.cacheLimiter');
742: if (!empty($limit)) {
743: session_cache_limiter($limit);
744: }
745: session_start();
746: }
747: return true;
748: }
749:
750: 751: 752: 753: 754:
755: protected static function _checkValid() {
756: $config = static::read('Config');
757: if ($config) {
758: $sessionConfig = Configure::read('Session');
759:
760: if (static::valid()) {
761: static::write('Config.time', static::$sessionTime);
762: if (isset($sessionConfig['autoRegenerate']) && $sessionConfig['autoRegenerate'] === true) {
763: $check = $config['countdown'];
764: $check -= 1;
765: static::write('Config.countdown', $check);
766:
767: if ($check < 1) {
768: static::renew();
769: static::write('Config.countdown', static::$requestCountdown);
770: }
771: }
772: } else {
773: $_SESSION = array();
774: static::destroy();
775: static::_setError(1, 'Session Highjacking Attempted !!!');
776: static::_startSession();
777: static::_writeConfig();
778: }
779: } else {
780: static::_writeConfig();
781: }
782: }
783:
784: 785: 786: 787: 788:
789: protected static function _writeConfig() {
790: static::write('Config.userAgent', static::$_userAgent);
791: static::write('Config.time', static::$sessionTime);
792: static::write('Config.countdown', static::$requestCountdown);
793: }
794:
795: 796: 797: 798: 799:
800: public static function renew() {
801: if (session_id() === '') {
802: return;
803: }
804: if (isset($_COOKIE[static::_cookieName()])) {
805: setcookie(Configure::read('Session.cookie'), '', time() - 42000, static::$path);
806: }
807: if (!headers_sent()) {
808: session_write_close();
809: session_start();
810: session_regenerate_id(true);
811: }
812: }
813:
814: 815: 816: 817: 818: 819: 820:
821: protected static function _setError($errorNumber, $errorMessage) {
822: if (static::$error === false) {
823: static::$error = array();
824: }
825: static::$error[$errorNumber] = $errorMessage;
826: static::$lastError = $errorNumber;
827: }
828:
829: }
830: