From df60e2d88b3fdcab4b1183a4e5e16b26d89c5dc2 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Fri, 29 Jul 2016 13:41:30 +0200 Subject: [PATCH] Dark hackery to not always disable CSRF for OCS controllers --- .../Security/SecurityMiddleware.php | 7 ++- .../Security/SecurityMiddlewareTest.php | 55 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php index daac36606f248..7468d00c0beee 100644 --- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php @@ -42,6 +42,7 @@ use OCP\AppFramework\Middleware; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\OCSController; use OCP\INavigationManager; use OCP\IURLGenerator; use OCP\IRequest; @@ -112,7 +113,7 @@ public function __construct(IRequest $request, * This runs all the security checks before a method call. The * security checks are determined by inspecting the controller method * annotations - * @param string $controller the controllername or string + * @param Controller $controller the controller * @param string $methodName the name of the method * @throws SecurityException when a security check fails */ @@ -145,7 +146,9 @@ public function beforeController($controller, $methodName) { // CSRF check - also registers the CSRF token since the session may be closed later Util::callRegister(); if(!$this->reflector->hasAnnotation('NoCSRFRequired')) { - if(!$this->request->passesCSRFCheck()) { + if(!$this->request->passesCSRFCheck() && !( + $controller instanceof OCSController && + $this->request->getHeader('OCS_APIREQUEST') === true)) { throw new CrossSiteRequestForgeryException(); } } diff --git a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php index 487b83c0bef13..6f6759321352c 100644 --- a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php @@ -35,22 +35,38 @@ use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Security\CSP\ContentSecurityPolicy; +use OC\Security\CSP\ContentSecurityPolicyManager; +use OCP\AppFramework\Controller; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\ILogger; +use OCP\INavigationManager; +use OCP\IRequest; +use OCP\IURLGenerator; class SecurityMiddlewareTest extends \Test\TestCase { + /** @var SecurityMiddleware|\PHPUnit_Framework_MockObject_MockObject */ private $middleware; + /** @var Controller|\PHPUnit_Framework_MockObject_MockObject */ private $controller; + /** @var SecurityException */ private $secException; + /** @var SecurityException */ private $secAjaxException; + /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ private $request; + /** @var ControllerMethodReflector */ private $reader; + /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ private $logger; + /** @var INavigationManager|\PHPUnit_Framework_MockObject_MockObject */ private $navigationManager; + /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ private $urlGenerator; + /** @var ContentSecurityPolicyManager|\PHPUnit_Framework_MockObject_MockObject */ private $contentSecurityPolicyManager; protected function setUp() { @@ -354,6 +370,45 @@ public function testPassesStrictCookieRequiredCheck() { $this->middleware->beforeController(__CLASS__, __FUNCTION__); } + public function dataCsrfOcsController() { + $controller = $this->getMockBuilder('OCP\AppFramework\Controller') + ->disableOriginalConstructor() + ->getMock(); + $ocsController = $this->getMockBuilder('OCP\AppFramework\OCSController') + ->disableOriginalConstructor() + ->getMock(); + + return [ + [$controller, false, true], + [$controller, true, true], + + [$ocsController, false, true], + [$ocsController, true, true], + ]; + } + + /** + * @dataProvider dataCsrfOcsController + * @param Controller $controller + * @param bool $hasOcsApiHeader + * @param bool $exception + */ + public function testCsrfOcsController(Controller $controller, $hasOcsApiHeader, $exception) { + $this->request + ->method('getHeader') + ->willReturn($hasOcsApiHeader ? 'true' : null); + $this->request->expects($this->once()) + ->method('passesStrictCookieCheck') + ->willReturn(true); + + try { + $this->middleware->beforeController($controller, 'foo'); + $this->assertFalse($exception); + } catch (CrossSiteRequestForgeryException $e) { + $this->assertTrue($exception); + } + } + /** * @NoCSRFRequired * @NoAdminRequired