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