Move browserSupportsCspV3 to CSPNonceManager

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
Roeland Jago Douma 2016-10-25 21:36:17 +02:00
parent d5589a15d5
commit e351ba56f1
No known key found for this signature in database
GPG Key ID: 1E152838F164D13B
6 changed files with 53 additions and 28 deletions

View File

@ -380,7 +380,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$app->isLoggedIn(), $app->isLoggedIn(),
$app->isAdminUser(), $app->isAdminUser(),
$app->getServer()->getContentSecurityPolicyManager(), $app->getServer()->getContentSecurityPolicyManager(),
$app->getServer()->getCsrfTokenManager() $app->getServer()->getCsrfTokenManager(),
$app->getServer()->getContentSecurityPolicyNonceManager()
); );
}); });

View File

@ -36,6 +36,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException; use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException;
use OC\AppFramework\Utility\ControllerMethodReflector; use OC\AppFramework\Utility\ControllerMethodReflector;
use OC\Security\CSP\ContentSecurityPolicyManager; use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfTokenManager; use OC\Security\CSRF\CsrfTokenManager;
use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
@ -80,6 +81,8 @@ class SecurityMiddleware extends Middleware {
private $contentSecurityPolicyManager; private $contentSecurityPolicyManager;
/** @var CsrfTokenManager */ /** @var CsrfTokenManager */
private $csrfTokenManager; private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager */
private $cspNonceManager;
/** /**
* @param IRequest $request * @param IRequest $request
@ -92,6 +95,7 @@ class SecurityMiddleware extends Middleware {
* @param bool $isAdminUser * @param bool $isAdminUser
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager * @param ContentSecurityPolicyManager $contentSecurityPolicyManager
* @param CSRFTokenManager $csrfTokenManager * @param CSRFTokenManager $csrfTokenManager
* @param ContentSecurityPolicyNonceManager $cspNonceManager
*/ */
public function __construct(IRequest $request, public function __construct(IRequest $request,
ControllerMethodReflector $reflector, ControllerMethodReflector $reflector,
@ -102,7 +106,8 @@ class SecurityMiddleware extends Middleware {
$isLoggedIn, $isLoggedIn,
$isAdminUser, $isAdminUser,
ContentSecurityPolicyManager $contentSecurityPolicyManager, ContentSecurityPolicyManager $contentSecurityPolicyManager,
CsrfTokenManager $csrfTokenManager) { CsrfTokenManager $csrfTokenManager,
ContentSecurityPolicyNonceManager $cspNonceManager) {
$this->navigationManager = $navigationManager; $this->navigationManager = $navigationManager;
$this->request = $request; $this->request = $request;
$this->reflector = $reflector; $this->reflector = $reflector;
@ -113,6 +118,7 @@ class SecurityMiddleware extends Middleware {
$this->isAdminUser = $isAdminUser; $this->isAdminUser = $isAdminUser;
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager; $this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
$this->csrfTokenManager = $csrfTokenManager; $this->csrfTokenManager = $csrfTokenManager;
$this->cspNonceManager = $cspNonceManager;
} }
@ -177,23 +183,6 @@ class SecurityMiddleware extends Middleware {
} }
private function browserSupportsCspV3() {
$browserWhitelist = [
// Chrome 40+
'/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[4-9][0-9].[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+$/',
// Firefox 45+
'/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/',
// Safari 10+
'/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/1[0-9.]+ Safari\/[0-9.A-Z]+$/',
];
if($this->request->isUserAgent($browserWhitelist)) {
return true;
}
return false;
}
/** /**
* Performs the default CSP modifications that may be injected by other * Performs the default CSP modifications that may be injected by other
* applications * applications
@ -213,7 +202,7 @@ class SecurityMiddleware extends Middleware {
$defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy(); $defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy();
$defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy); $defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy);
if($this->browserSupportsCspV3()) { if($this->cspNonceManager->browserSupportsCspV3()) {
$defaultPolicy->useJsNonce($this->csrfTokenManager->getToken()->getEncryptedValue()); $defaultPolicy->useJsNonce($this->csrfTokenManager->getToken()->getEncryptedValue());
} }

View File

@ -22,6 +22,7 @@
namespace OC\Security\CSP; namespace OC\Security\CSP;
use OC\Security\CSRF\CsrfTokenManager; use OC\Security\CSRF\CsrfTokenManager;
use OCP\IRequest;
/** /**
* @package OC\Security\CSP * @package OC\Security\CSP
@ -29,14 +30,19 @@ use OC\Security\CSRF\CsrfTokenManager;
class ContentSecurityPolicyNonceManager { class ContentSecurityPolicyNonceManager {
/** @var CsrfTokenManager */ /** @var CsrfTokenManager */
private $csrfTokenManager; private $csrfTokenManager;
/** @var IRequest */
private $request;
/** @var string */ /** @var string */
private $nonce = ''; private $nonce = '';
/** /**
* @param CsrfTokenManager $csrfTokenManager * @param CsrfTokenManager $csrfTokenManager
* @param IRequest $request
*/ */
public function __construct(CsrfTokenManager $csrfTokenManager) { public function __construct(CsrfTokenManager $csrfTokenManager,
IRequest $request) {
$this->csrfTokenManager = $csrfTokenManager; $this->csrfTokenManager = $csrfTokenManager;
$this->request = $request;
} }
/** /**
@ -51,4 +57,25 @@ class ContentSecurityPolicyNonceManager {
return $this->nonce; return $this->nonce;
} }
/**
* Check if the browser supports CSP v3
* @return bool
*/
public function browserSupportsCspV3() {
$browserWhitelist = [
// Chrome 40+
'/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[4-9][0-9].[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+$/',
// Firefox 45+
'/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/',
// Safari 10+
'/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/1[0-9.]+ Safari\/[0-9.A-Z]+$/',
];
if($this->request->isUserAgent($browserWhitelist)) {
return true;
}
return false;
}
} }

View File

@ -711,7 +711,8 @@ class Server extends ServerContainer implements IServerContainer {
}); });
$this->registerService('ContentSecurityPolicyNonceManager', function(Server $c) { $this->registerService('ContentSecurityPolicyNonceManager', function(Server $c) {
return new ContentSecurityPolicyNonceManager( return new ContentSecurityPolicyNonceManager(
$c->getCsrfTokenManager() $c->getCsrfTokenManager(),
$c->getRequest()
); );
}); });
$this->registerService('ShareManager', function(Server $c) { $this->registerService('ShareManager', function(Server $c) {

View File

@ -36,6 +36,7 @@ use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Utility\ControllerMethodReflector; use OC\AppFramework\Utility\ControllerMethodReflector;
use OC\Security\CSP\ContentSecurityPolicy; use OC\Security\CSP\ContentSecurityPolicy;
use OC\Security\CSP\ContentSecurityPolicyManager; use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfToken; use OC\Security\CSRF\CsrfToken;
use OC\Security\CSRF\CsrfTokenManager; use OC\Security\CSRF\CsrfTokenManager;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
@ -76,6 +77,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
private $contentSecurityPolicyManager; private $contentSecurityPolicyManager;
/** @var CsrfTokenManager|\PHPUnit_Framework_MockObject_MockObject */ /** @var CsrfTokenManager|\PHPUnit_Framework_MockObject_MockObject */
private $csrfTokenManager; private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager|\PHPUnit_Framework_MockObject_MockObject */
private $cspNonceManager;
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
@ -88,6 +91,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->request = $this->createMock(IRequest::class); $this->request = $this->createMock(IRequest::class);
$this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class); $this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class);
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class); $this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
$this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class);
$this->middleware = $this->getMiddleware(true, true); $this->middleware = $this->getMiddleware(true, true);
$this->secException = new SecurityException('hey', false); $this->secException = new SecurityException('hey', false);
$this->secAjaxException = new SecurityException('hey', true); $this->secAjaxException = new SecurityException('hey', true);
@ -109,7 +113,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$isLoggedIn, $isLoggedIn,
$isAdminUser, $isAdminUser,
$this->contentSecurityPolicyManager, $this->contentSecurityPolicyManager,
$this->csrfTokenManager $this->csrfTokenManager,
$this->cspNonceManager
); );
} }
@ -559,9 +564,9 @@ class SecurityMiddlewareTest extends \Test\TestCase {
} }
public function testAfterController() { public function testAfterController() {
$this->request $this->cspNonceManager
->expects($this->once()) ->expects($this->once())
->method('isUserAgent') ->method('browserSupportsCspV3')
->willReturn(false); ->willReturn(false);
$response = $this->createMock(Response::class); $response = $this->createMock(Response::class);
$defaultPolicy = new ContentSecurityPolicy(); $defaultPolicy = new ContentSecurityPolicy();
@ -603,9 +608,9 @@ class SecurityMiddlewareTest extends \Test\TestCase {
} }
public function testAfterControllerWithContentSecurityPolicy3Support() { public function testAfterControllerWithContentSecurityPolicy3Support() {
$this->request $this->cspNonceManager
->expects($this->once()) ->expects($this->once())
->method('isUserAgent') ->method('browserSupportsCspV3')
->willReturn(true); ->willReturn(true);
$token = $this->createMock(CsrfToken::class); $token = $this->createMock(CsrfToken::class);
$token $token

View File

@ -24,6 +24,7 @@ namespace Test\Security\CSP;
use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfToken; use OC\Security\CSRF\CsrfToken;
use OC\Security\CSRF\CsrfTokenManager; use OC\Security\CSRF\CsrfTokenManager;
use OCP\IRequest;
use Test\TestCase; use Test\TestCase;
class ContentSecurityPolicyNonceManagerTest extends TestCase { class ContentSecurityPolicyNonceManagerTest extends TestCase {
@ -35,7 +36,8 @@ class ContentSecurityPolicyNonceManagerTest extends TestCase {
public function setUp() { public function setUp() {
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class); $this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
$this->nonceManager = new ContentSecurityPolicyNonceManager( $this->nonceManager = new ContentSecurityPolicyNonceManager(
$this->csrfTokenManager $this->csrfTokenManager,
$this->createMock(IRequest::class)
); );
} }