diff options
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/appframework/dependencyinjection/dicontainer.php | 10 | ||||
-rw-r--r-- | lib/private/appframework/middleware/security/securitymiddleware.php | 37 | ||||
-rw-r--r-- | lib/private/security/csp/contentsecuritypolicy.php | 199 | ||||
-rw-r--r-- | lib/private/security/csp/contentsecuritypolicymanager.php | 73 | ||||
-rw-r--r-- | lib/private/server.php | 12 |
5 files changed, 325 insertions, 6 deletions
diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php index 61a04482431..ff9da88cd81 100644 --- a/lib/private/appframework/dependencyinjection/dicontainer.php +++ b/lib/private/appframework/dependencyinjection/dicontainer.php @@ -32,7 +32,6 @@ namespace OC\AppFramework\DependencyInjection; use OC; use OC\AppFramework\Http; -use OC\AppFramework\Http\Request; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Output; use OC\AppFramework\Core\API; @@ -43,8 +42,6 @@ use OC\AppFramework\Middleware\SessionMiddleware; use OC\AppFramework\Utility\SimpleContainer; use OCP\AppFramework\IApi; use OCP\AppFramework\IAppContainer; -use OCP\AppFramework\Middleware; -use OCP\IServerContainer; class DIContainer extends SimpleContainer implements IAppContainer { @@ -255,6 +252,10 @@ class DIContainer extends SimpleContainer implements IAppContainer { return $this->getServer()->getSession(); }); + $this->registerService('OCP\\Security\\IContentSecurityPolicyManager', function($c) { + return $this->getServer()->getContentSecurityPolicyManager(); + }); + $this->registerService('ServerContainer', function ($c) { return $this->getServer(); }); @@ -319,7 +320,8 @@ class DIContainer extends SimpleContainer implements IAppContainer { $app->getServer()->getLogger(), $c['AppName'], $app->isLoggedIn(), - $app->isAdminUser() + $app->isAdminUser(), + $app->getServer()->getContentSecurityPolicyManager() ); }); diff --git a/lib/private/appframework/middleware/security/securitymiddleware.php b/lib/private/appframework/middleware/security/securitymiddleware.php index 4ef043ad50f..f1ba98254f0 100644 --- a/lib/private/appframework/middleware/security/securitymiddleware.php +++ b/lib/private/appframework/middleware/security/securitymiddleware.php @@ -32,6 +32,8 @@ use OC\Appframework\Middleware\Security\Exceptions\CrossSiteRequestForgeryExcept use OC\Appframework\Middleware\Security\Exceptions\NotAdminException; use OC\Appframework\Middleware\Security\Exceptions\NotLoggedInException; use OC\AppFramework\Utility\ControllerMethodReflector; +use OC\Security\CSP\ContentSecurityPolicyManager; +use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Middleware; @@ -52,15 +54,24 @@ use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; * check fails */ class SecurityMiddleware extends Middleware { - + /** @var INavigationManager */ private $navigationManager; + /** @var IRequest */ private $request; + /** @var ControllerMethodReflector */ private $reflector; + /** @var string */ private $appName; + /** @var IURLGenerator */ private $urlGenerator; + /** @var ILogger */ private $logger; + /** @var bool */ private $isLoggedIn; + /** @var bool */ private $isAdminUser; + /** @var ContentSecurityPolicyManager */ + private $contentSecurityPolicyManager; /** * @param IRequest $request @@ -71,6 +82,7 @@ class SecurityMiddleware extends Middleware { * @param string $appName * @param bool $isLoggedIn * @param bool $isAdminUser + * @param ContentSecurityPolicyManager $contentSecurityPolicyManager */ public function __construct(IRequest $request, ControllerMethodReflector $reflector, @@ -79,7 +91,8 @@ class SecurityMiddleware extends Middleware { ILogger $logger, $appName, $isLoggedIn, - $isAdminUser) { + $isAdminUser, + ContentSecurityPolicyManager $contentSecurityPolicyManager) { $this->navigationManager = $navigationManager; $this->request = $request; $this->reflector = $reflector; @@ -88,6 +101,7 @@ class SecurityMiddleware extends Middleware { $this->logger = $logger; $this->isLoggedIn = $isLoggedIn; $this->isAdminUser = $isAdminUser; + $this->contentSecurityPolicyManager = $contentSecurityPolicyManager; } @@ -139,6 +153,25 @@ class SecurityMiddleware extends Middleware { } + /** + * Performs the default CSP modifications that may be injected by other + * applications + * + * @param Controller $controller + * @param string $methodName + * @param Response $response + * @return Response + */ + public function afterController($controller, $methodName, Response $response) { + $policy = !is_null($response->getContentSecurityPolicy()) ? $response->getContentSecurityPolicy() : new ContentSecurityPolicy(); + + $defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy(); + $defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy); + + $response->setContentSecurityPolicy($defaultPolicy); + + return $response; + } /** * If an SecurityException is being caught, ajax requests return a JSON error diff --git a/lib/private/security/csp/contentsecuritypolicy.php b/lib/private/security/csp/contentsecuritypolicy.php new file mode 100644 index 00000000000..25eacfab1d6 --- /dev/null +++ b/lib/private/security/csp/contentsecuritypolicy.php @@ -0,0 +1,199 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Security\CSP; + +/** + * Class ContentSecurityPolicy extends the public class and adds getter and setters. + * This is necessary since we don't want to expose the setters and getters to the + * public API. + * + * @package OC\Security\CSP + */ +class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy { + /** + * @return boolean + */ + public function isInlineScriptAllowed() { + return $this->inlineScriptAllowed; + } + + /** + * @param boolean $inlineScriptAllowed + */ + public function setInlineScriptAllowed($inlineScriptAllowed) { + $this->inlineScriptAllowed = $inlineScriptAllowed; + } + + /** + * @return boolean + */ + public function isEvalScriptAllowed() { + return $this->evalScriptAllowed; + } + + /** + * @param boolean $evalScriptAllowed + */ + public function setEvalScriptAllowed($evalScriptAllowed) { + $this->evalScriptAllowed = $evalScriptAllowed; + } + + /** + * @return array + */ + public function getAllowedScriptDomains() { + return $this->allowedScriptDomains; + } + + /** + * @param array $allowedScriptDomains + */ + public function setAllowedScriptDomains($allowedScriptDomains) { + $this->allowedScriptDomains = $allowedScriptDomains; + } + + /** + * @return boolean + */ + public function isInlineStyleAllowed() { + return $this->inlineStyleAllowed; + } + + /** + * @param boolean $inlineStyleAllowed + */ + public function setInlineStyleAllowed($inlineStyleAllowed) { + $this->inlineStyleAllowed = $inlineStyleAllowed; + } + + /** + * @return array + */ + public function getAllowedStyleDomains() { + return $this->allowedStyleDomains; + } + + /** + * @param array $allowedStyleDomains + */ + public function setAllowedStyleDomains($allowedStyleDomains) { + $this->allowedStyleDomains = $allowedStyleDomains; + } + + /** + * @return array + */ + public function getAllowedImageDomains() { + return $this->allowedImageDomains; + } + + /** + * @param array $allowedImageDomains + */ + public function setAllowedImageDomains($allowedImageDomains) { + $this->allowedImageDomains = $allowedImageDomains; + } + + /** + * @return array + */ + public function getAllowedConnectDomains() { + return $this->allowedConnectDomains; + } + + /** + * @param array $allowedConnectDomains + */ + public function setAllowedConnectDomains($allowedConnectDomains) { + $this->allowedConnectDomains = $allowedConnectDomains; + } + + /** + * @return array + */ + public function getAllowedMediaDomains() { + return $this->allowedMediaDomains; + } + + /** + * @param array $allowedMediaDomains + */ + public function setAllowedMediaDomains($allowedMediaDomains) { + $this->allowedMediaDomains = $allowedMediaDomains; + } + + /** + * @return array + */ + public function getAllowedObjectDomains() { + return $this->allowedObjectDomains; + } + + /** + * @param array $allowedObjectDomains + */ + public function setAllowedObjectDomains($allowedObjectDomains) { + $this->allowedObjectDomains = $allowedObjectDomains; + } + + /** + * @return array + */ + public function getAllowedFrameDomains() { + return $this->allowedFrameDomains; + } + + /** + * @param array $allowedFrameDomains + */ + public function setAllowedFrameDomains($allowedFrameDomains) { + $this->allowedFrameDomains = $allowedFrameDomains; + } + + /** + * @return array + */ + public function getAllowedFontDomains() { + return $this->allowedFontDomains; + } + + /** + * @param array $allowedFontDomains + */ + public function setAllowedFontDomains($allowedFontDomains) { + $this->allowedFontDomains = $allowedFontDomains; + } + + /** + * @return array + */ + public function getAllowedChildSrcDomains() { + return $this->allowedChildSrcDomains; + } + + /** + * @param array $allowedChildSrcDomains + */ + public function setAllowedChildSrcDomains($allowedChildSrcDomains) { + $this->allowedChildSrcDomains = $allowedChildSrcDomains; + } + +} diff --git a/lib/private/security/csp/contentsecuritypolicymanager.php b/lib/private/security/csp/contentsecuritypolicymanager.php new file mode 100644 index 00000000000..760cd36e56b --- /dev/null +++ b/lib/private/security/csp/contentsecuritypolicymanager.php @@ -0,0 +1,73 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2016, 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\Security\CSP; + +use OCP\AppFramework\Http\ContentSecurityPolicy; +use OCP\AppFramework\Http\EmptyContentSecurityPolicy; +use OCP\Security\IContentSecurityPolicyManager; + +class ContentSecurityPolicyManager implements IContentSecurityPolicyManager { + /** @var ContentSecurityPolicy[] */ + private $policies = []; + + /** {@inheritdoc} */ + public function addDefaultPolicy(EmptyContentSecurityPolicy $policy) { + $this->policies[] = $policy; + } + + /** + * Get the configured default policy. This is not in the public namespace + * as it is only supposed to be used by core itself. + * + * @return ContentSecurityPolicy + */ + public function getDefaultPolicy() { + $defaultPolicy = new \OC\Security\CSP\ContentSecurityPolicy(); + foreach($this->policies as $policy) { + $defaultPolicy = $this->mergePolicies($defaultPolicy, $policy); + } + return $defaultPolicy; + } + + /** + * Merges the first given policy with the second one + * + * @param ContentSecurityPolicy $defaultPolicy + * @param EmptyContentSecurityPolicy $originalPolicy + * @return ContentSecurityPolicy + */ + public function mergePolicies(ContentSecurityPolicy $defaultPolicy, + EmptyContentSecurityPolicy $originalPolicy) { + foreach((object)(array)$originalPolicy as $name => $value) { + $setter = 'set'.ucfirst($name); + if(is_array($value)) { + $getter = 'get'.ucfirst($name); + $currentValues = is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : []; + $defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value)))); + } elseif (is_bool($value)) { + $defaultPolicy->$setter($value); + } + } + + return $defaultPolicy; + } +} diff --git a/lib/private/server.php b/lib/private/server.php index d453a42d3a0..d3dbcba86ba 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -63,6 +63,7 @@ use OC\Lock\NoopLockingProvider; use OC\Mail\Mailer; use OC\Notification\Manager; use OC\Security\CertificateManager; +use OC\Security\CSP\ContentSecurityPolicyManager; use OC\Security\Crypto; use OC\Security\CSRF\CsrfTokenGenerator; use OC\Security\CSRF\CsrfTokenManager; @@ -74,6 +75,7 @@ use OC\Security\TrustedDomainHelper; use OC\Session\CryptoWrapper; use OC\Tagging\TagMapper; use OCP\IServerContainer; +use OCP\Security\IContentSecurityPolicyManager; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -598,6 +600,9 @@ class Server extends ServerContainer implements IServerContainer { $sessionStorage ); }); + $this->registerService('ContentSecurityPolicyManager', function (Server $c) { + return new ContentSecurityPolicyManager(); + }); $this->registerService('ShareManager', function(Server $c) { $config = $c->getConfig(); $factoryClass = $config->getSystemValue('sharing.managerFactory', '\OC\Share20\ProviderFactory'); @@ -1221,6 +1226,13 @@ class Server extends ServerContainer implements IServerContainer { } /** + * @return IContentSecurityPolicyManager + */ + public function getContentSecurityPolicyManager() { + return $this->query('ContentSecurityPolicyManager'); + } + + /** * Not a public API as of 8.2, wait for 9.0 * * @return \OCA\Files_External\Service\BackendService |