Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a session wrapper to encrypt the data before storing it on disk #18482

Merged
merged 2 commits into from
Aug 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@
\OC::$server->getSession()->close();

// initialize a dummy memory session
\OC::$server->setSession(new \OC\Session\Memory(''));
$session = new \OC\Session\Memory('');
$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
$session = $cryptoWrapper->wrapSession($session);
\OC::$server->setSession($session);

$logger = \OC::$server->getLogger();

Expand Down
12 changes: 7 additions & 5 deletions lib/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -463,13 +463,15 @@ public static function initSession() {
$useCustomSession = false;
$session = self::$server->getSession();
OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession));
if($useCustomSession) {
// use the session reference as the new Session
self::$server->setSession($session);
} else {
if (!$useCustomSession) {
// set the session name to the instance id - which is unique
self::$server->setSession(new \OC\Session\Internal($sessionName));
$session = new \OC\Session\Internal($sessionName);
}

$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
$session = $cryptoWrapper->wrapSession($session);
self::$server->setSession($session);

// if session cant be started break with http 500 error
} catch (Exception $e) {
\OCP\Util::logException('base', $e);
Expand Down
41 changes: 40 additions & 1 deletion lib/private/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
use OC\AppFramework\Http\Request;
use OC\AppFramework\Db\Db;
use OC\AppFramework\Utility\SimpleContainer;
use OC\AppFramework\Utility\TimeFactory;
use OC\Command\AsyncBus;
use OC\Diagnostics\EventLogger;
use OC\Diagnostics\NullEventLogger;
Expand All @@ -56,6 +57,7 @@
use OC\Security\Hasher;
use OC\Security\SecureRandom;
use OC\Security\TrustedDomainHelper;
use OC\Session\CryptoWrapper;
use OC\Tagging\TagMapper;
use OCP\IServerContainer;
use Symfony\Component\EventDispatcher\EventDispatcher;
Expand Down Expand Up @@ -159,7 +161,12 @@ public function __construct($webRoot) {
});
$this->registerService('UserSession', function (Server $c) {
$manager = $c->getUserManager();
$userSession = new \OC\User\Session($manager, new \OC\Session\Memory(''));

$session = new \OC\Session\Memory('');
$cryptoWrapper = $c->getSessionCryptoWrapper();
$session = $cryptoWrapper->wrapSession($session);

$userSession = new \OC\User\Session($manager, $session);
$userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) {
\OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password));
});
Expand Down Expand Up @@ -461,6 +468,31 @@ public function __construct($webRoot) {
$this->registerService('EventDispatcher', function() {
return new EventDispatcher();
});
$this->registerService('CryptoWrapper', function (Server $c) {
// FIXME: Instantiiated here due to cyclic dependency
$request = new Request(
[
'get' => $_GET,
'post' => $_POST,
'files' => $_FILES,
'server' => $_SERVER,
'env' => $_ENV,
'cookies' => $_COOKIE,
'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD']))
? $_SERVER['REQUEST_METHOD']
: null,
],
new SecureRandom(),
$c->getConfig()
);

return new CryptoWrapper(
$c->getConfig(),
$c->getCrypto(),
$c->getSecureRandom(),
$request
);
});
}

/**
Expand Down Expand Up @@ -976,4 +1008,11 @@ public function getCapabilitiesManager() {
public function getEventDispatcher() {
return $this->query('EventDispatcher');
}

/**
* @return \OC\Session\CryptoWrapper
*/
public function getSessionCryptoWrapper() {
return $this->query('CryptoWrapper');
}
}
147 changes: 147 additions & 0 deletions lib/private/session/cryptosessiondata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OC\Session;

use OCP\ISession;
use OCP\Security\ICrypto;

/**
* Class CryptoSessionData
*
* @package OC\Session
*/
class CryptoSessionData implements \ArrayAccess, ISession {
/** @var ISession */
protected $session;

/** @var \OCP\Security\ICrypto */
protected $crypto;

/** @var string */
protected $passphrase;

/**
* @param ISession $session
* @param ICrypto $crypto
* @param string $passphrase
*/
public function __construct(ISession $session, ICrypto $crypto, $passphrase) {
$this->crypto = $crypto;
$this->session = $session;
$this->passphrase = $passphrase;
}

/**
* Set a value in the session
*
* @param string $key
* @param mixed $value
*/
public function set($key, $value) {
$encryptedValue = $this->crypto->encrypt(json_encode($value), $this->passphrase);
$this->session->set($key, $encryptedValue);
}

/**
* Get a value from the session
*
* @param string $key
* @return string|null Either the value or null
*/
public function get($key) {
$encryptedValue = $this->session->get($key);
if ($encryptedValue === null) {
return null;
}

try {
$value = $this->crypto->decrypt($encryptedValue, $this->passphrase);
return json_decode($value);
} catch (\Exception $e) {
return null;
}
}

/**
* Check if a named key exists in the session
*
* @param string $key
* @return bool
*/
public function exists($key) {
return $this->session->exists($key);
}

/**
* Remove a $key/$value pair from the session
*
* @param string $key
*/
public function remove($key) {
$this->session->remove($key);
}

/**
* Reset and recreate the session
*/
public function clear() {
$this->session->clear();
}

/**
* Close the session and release the lock
*/
public function close() {
$this->session->close();
}

/**
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset) {
return $this->exists($offset);
}

/**
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset) {
return $this->get($offset);
}

/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value) {
$this->set($offset, $value);
}

/**
* @param mixed $offset
*/
public function offsetUnset($offset) {
$this->remove($offset);
}
}
96 changes: 96 additions & 0 deletions lib/private/session/cryptowrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OC\Session;

use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IRequest;
use OCP\ISession;
use OCP\Security\ICrypto;
use OCP\Security\ISecureRandom;

/**
* Class CryptoWrapper provides some rough basic level of additional security by
* storing the session data in an encrypted form.
*
* The content of the session is encrypted using another cookie sent by the browser.
* One should note that an adversary with access to the source code or the system
* memory is still able to read the original session ID from the users' request.
* This thus can not be considered a strong security measure one should consider
* it as an additional small security obfuscation layer to comply with compliance
* guidelines.
*
* TODO: Remove this in a future relase with an approach such as
* https://github.com/owncloud/core/pull/17866
*
* @package OC\Session
*/
class CryptoWrapper {
const COOKIE_NAME = 'oc_sessionPassphrase';

/** @var ISession */
protected $session;

/** @var \OCP\Security\ICrypto */
protected $crypto;

/** @var ISecureRandom */
protected $random;

/**
* @param IConfig $config
* @param ICrypto $crypto
* @param ISecureRandom $random
* @param IRequest $request
*/
public function __construct(IConfig $config,
ICrypto $crypto,
ISecureRandom $random,
IRequest $request) {
$this->crypto = $crypto;
$this->config = $config;
$this->random = $random;

if (!is_null($request->getCookie(self::COOKIE_NAME))) {
$this->passphrase = $request->getCookie(self::COOKIE_NAME);
} else {
$this->passphrase = $this->random->getMediumStrengthGenerator()->generate(128);
$secureCookie = $request->getServerProtocol() === 'https';
// FIXME: Required for CI
if (!defined('PHPUNIT_RUN')) {
setcookie(self::COOKIE_NAME, $this->passphrase, 0, \OC::$WEBROOT, '', $secureCookie, true);
}
}
}

/**
* @param ISession $session
* @return ISession
*/
public function wrapSession(ISession $session) {
if (!($session instanceof CryptoSessionData)) {
return new CryptoSessionData($session, $this->crypto, $this->passphrase);
}

return $session;
}
}
1 change: 1 addition & 0 deletions tests/lib/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public function dataTestQuery() {
['ContactsManager', '\OCP\Contacts\IManager'],
['Crypto', '\OC\Security\Crypto'],
['Crypto', '\OCP\Security\ICrypto'],
['CryptoWrapper', '\OC\Session\CryptoWrapper'],

['DatabaseConnection', '\OC\DB\Connection'],
['DatabaseConnection', '\OCP\IDBConnection'],
Expand Down
Loading