diff options
-rw-r--r-- | config/config.sample.php | 5 | ||||
-rw-r--r-- | lib/base.php | 70 | ||||
-rw-r--r-- | lib/user.php | 9 |
3 files changed, 70 insertions, 14 deletions
diff --git a/config/config.sample.php b/config/config.sample.php index f2fd948418f..3d0a70db1d8 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -92,6 +92,9 @@ $CONFIG = array( /* Loglevel to start logging at. 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR (default is WARN) */ "loglevel" => "", +/* Lifetime of the remember login cookie, default is 15 days */ +"remember_login_cookie_lifetime" => 60*60*24*15, + /* The directory where the user data is stored, default to data in the owncloud * directory. The sqlite database is also stored here, when sqlite is used. */ @@ -110,4 +113,4 @@ $CONFIG = array( 'writable' => true, ), ), -);
\ No newline at end of file +); diff --git a/lib/base.php b/lib/base.php index 69685559608..803d3e8bde5 100644 --- a/lib/base.php +++ b/lib/base.php @@ -264,8 +264,30 @@ class OC{ } public static function initSession() { + // prevents javascript from accessing php session cookies ini_set('session.cookie_httponly', '1;'); + + // (re)-initialize session session_start(); + + // regenerate session id periodically to avoid session fixation + if (!isset($_SESSION['SID_CREATED'])) { + $_SESSION['SID_CREATED'] = time(); + } else if (time() - $_SESSION['SID_CREATED'] > 900) { + session_regenerate_id(true); + $_SESSION['SID_CREATED'] = time(); + } + + // session timeout + if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 3600)) { + if (isset($_COOKIE[session_name()])) { + setcookie(session_name(), '', time() - 42000, '/'); + } + session_unset(); + session_destroy(); + session_start(); + } + $_SESSION['LAST_ACTIVITY'] = time(); } public static function init() { @@ -453,6 +475,7 @@ class OC{ OC_App::loadApps(); OC_User::setupBackends(); if(isset($_GET["logout"]) and ($_GET["logout"])) { + OC_Preferences::deleteKey(OC_User::getUser(), 'login_token', $_COOKIE['oc_token']); OC_User::logout(); header("Location: ".OC::$WEBROOT.'/'); }else{ @@ -514,6 +537,17 @@ class OC{ OC_Util::displayLoginPage(array_unique($error)); } + protected static function cleanupLoginTokens($user) { + $cutoff = time() - OC_Config::getValue('remember_login_cookie_lifetime', 60*60*24*15); + $tokens = OC_Preferences::getKeys($user, 'login_token'); + foreach($tokens as $token) { + $time = OC_Preferences::getValue($user, 'login_token', $token); + if ($time < $cutoff) { + OC_Preferences::deleteKey($user, 'login_token', $token); + } + } + } + protected static function tryRememberLogin() { if(!isset($_COOKIE["oc_remember_login"]) || !isset($_COOKIE["oc_token"]) @@ -527,15 +561,30 @@ class OC{ OC_Log::write('core', 'Trying to login from cookie', OC_Log::DEBUG); } // confirm credentials in cookie - if(isset($_COOKIE['oc_token']) && OC_User::userExists($_COOKIE['oc_username']) && - OC_Preferences::getValue($_COOKIE['oc_username'], "login", "token") === $_COOKIE['oc_token']) - { - OC_User::setUserId($_COOKIE['oc_username']); - OC_Util::redirectToDefaultPage(); - } - else { - OC_User::unsetMagicInCookie(); + if(isset($_COOKIE['oc_token']) && OC_User::userExists($_COOKIE['oc_username'])) { + // delete outdated cookies + self::cleanupLoginTokens($_COOKIE['oc_username']); + // get stored tokens + $tokens = OC_Preferences::getKeys($_COOKIE['oc_username'], 'login_token'); + // test cookies token against stored tokens + if (in_array($_COOKIE['oc_token'], $tokens, true)) { + // replace successfully used token with a new one + OC_Preferences::deleteKey($_COOKIE['oc_username'], 'login_token', $_COOKIE['oc_token']); + $token = OC_Util::generate_random_bytes(32); + OC_Preferences::setValue($_COOKIE['oc_username'], 'login_token', $token, time()); + OC_User::setMagicInCookie($_COOKIE['oc_username'], $token); + // login + OC_User::setUserId($_COOKIE['oc_username']); + OC_Util::redirectToDefaultPage(); + // doesn't return + } + // if you reach this point you have changed your password + // or you are an attacker + // we can not delete tokens here because users may reach + // this point multiple times after a password change + OC_Log::write('core', 'Authentication cookie rejected for user '.$_COOKIE['oc_username'], OC_Log::WARN); } + OC_User::unsetMagicInCookie(); return true; } @@ -550,12 +599,13 @@ class OC{ OC_User::setupBackends(); if(OC_User::login($_POST["user"], $_POST["password"])) { + self::cleanupLoginTokens($_POST['user']); if(!empty($_POST["remember_login"])) { if(defined("DEBUG") && DEBUG) { OC_Log::write('core', 'Setting remember login to cookie', OC_Log::DEBUG); } - $token = md5($_POST["user"].time().$_POST['password']); - OC_Preferences::setValue($_POST['user'], 'login', 'token', $token); + $token = OC_Util::generate_random_bytes(32); + OC_Preferences::setValue($_POST['user'], 'login_token', $token, time()); OC_User::setMagicInCookie($_POST["user"], $token); } else { diff --git a/lib/user.php b/lib/user.php index 7de2a4b7fe6..11373a74014 100644 --- a/lib/user.php +++ b/lib/user.php @@ -329,6 +329,8 @@ class OC_User { } } } + // invalidate all login cookies + OC_Preferences::deleteApp($uid, 'login_token'); OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password )); return $success; } @@ -472,9 +474,10 @@ class OC_User { */ public static function setMagicInCookie($username, $token) { $secure_cookie = OC_Config::getValue("forcessl", false); - setcookie("oc_username", $username, time()+60*60*24*15, '', '', $secure_cookie); - setcookie("oc_token", $token, time()+60*60*24*15, '', '', $secure_cookie); - setcookie("oc_remember_login", true, time()+60*60*24*15, '', '', $secure_cookie); + $expires = time() + OC_Config::getValue('remember_login_cookie_lifetime', 60*60*24*15); + setcookie("oc_username", $username, $expires, '', '', $secure_cookie); + setcookie("oc_token", $token, $expires, '', '', $secure_cookie); + setcookie("oc_remember_login", true, $expires, '', '', $secure_cookie); } /** |