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->isAdminUser(),
$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\Utility\ControllerMethodReflector;
use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfTokenManager;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
@ -80,6 +81,8 @@ class SecurityMiddleware extends Middleware {
private $contentSecurityPolicyManager;
/** @var CsrfTokenManager */
private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager */
private $cspNonceManager;
/**
* @param IRequest $request
@ -92,6 +95,7 @@ class SecurityMiddleware extends Middleware {
* @param bool $isAdminUser
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager
* @param CSRFTokenManager $csrfTokenManager
* @param ContentSecurityPolicyNonceManager $cspNonceManager
*/
public function __construct(IRequest $request,
ControllerMethodReflector $reflector,
@ -102,7 +106,8 @@ class SecurityMiddleware extends Middleware {
$isLoggedIn,
$isAdminUser,
ContentSecurityPolicyManager $contentSecurityPolicyManager,
CsrfTokenManager $csrfTokenManager) {
CsrfTokenManager $csrfTokenManager,
ContentSecurityPolicyNonceManager $cspNonceManager) {
$this->navigationManager = $navigationManager;
$this->request = $request;
$this->reflector = $reflector;
@ -113,6 +118,7 @@ class SecurityMiddleware extends Middleware {
$this->isAdminUser = $isAdminUser;
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
$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
* applications
@ -213,7 +202,7 @@ class SecurityMiddleware extends Middleware {
$defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy();
$defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy);
if($this->browserSupportsCspV3()) {
if($this->cspNonceManager->browserSupportsCspV3()) {
$defaultPolicy->useJsNonce($this->csrfTokenManager->getToken()->getEncryptedValue());
}

View File

@ -22,6 +22,7 @@
namespace OC\Security\CSP;
use OC\Security\CSRF\CsrfTokenManager;
use OCP\IRequest;
/**
* @package OC\Security\CSP
@ -29,14 +30,19 @@ use OC\Security\CSRF\CsrfTokenManager;
class ContentSecurityPolicyNonceManager {
/** @var CsrfTokenManager */
private $csrfTokenManager;
/** @var IRequest */
private $request;
/** @var string */
private $nonce = '';
/**
* @param CsrfTokenManager $csrfTokenManager
* @param IRequest $request
*/
public function __construct(CsrfTokenManager $csrfTokenManager) {
public function __construct(CsrfTokenManager $csrfTokenManager,
IRequest $request) {
$this->csrfTokenManager = $csrfTokenManager;
$this->request = $request;
}
/**
@ -51,4 +57,25 @@ class ContentSecurityPolicyNonceManager {
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) {
return new ContentSecurityPolicyNonceManager(
$c->getCsrfTokenManager()
$c->getCsrfTokenManager(),
$c->getRequest()
);
});
$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\Security\CSP\ContentSecurityPolicy;
use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfToken;
use OC\Security\CSRF\CsrfTokenManager;
use OCP\AppFramework\Controller;
@ -76,6 +77,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
private $contentSecurityPolicyManager;
/** @var CsrfTokenManager|\PHPUnit_Framework_MockObject_MockObject */
private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager|\PHPUnit_Framework_MockObject_MockObject */
private $cspNonceManager;
protected function setUp() {
parent::setUp();
@ -88,6 +91,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->request = $this->createMock(IRequest::class);
$this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class);
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
$this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class);
$this->middleware = $this->getMiddleware(true, true);
$this->secException = new SecurityException('hey', false);
$this->secAjaxException = new SecurityException('hey', true);
@ -109,7 +113,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$isLoggedIn,
$isAdminUser,
$this->contentSecurityPolicyManager,
$this->csrfTokenManager
$this->csrfTokenManager,
$this->cspNonceManager
);
}
@ -559,9 +564,9 @@ class SecurityMiddlewareTest extends \Test\TestCase {
}
public function testAfterController() {
$this->request
$this->cspNonceManager
->expects($this->once())
->method('isUserAgent')
->method('browserSupportsCspV3')
->willReturn(false);
$response = $this->createMock(Response::class);
$defaultPolicy = new ContentSecurityPolicy();
@ -603,9 +608,9 @@ class SecurityMiddlewareTest extends \Test\TestCase {
}
public function testAfterControllerWithContentSecurityPolicy3Support() {
$this->request
$this->cspNonceManager
->expects($this->once())
->method('isUserAgent')
->method('browserSupportsCspV3')
->willReturn(true);
$token = $this->createMock(CsrfToken::class);
$token

View File

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