server/tests/lib/appframework/middleware/security/CORSMiddlewareTest.php
Lukas Reschke 8313a3fcb3 Add mitigation against BREACH
While BREACH requires the following three factors to be effectively exploitable we should add another mitigation:

1. Application must support HTTP compression
2. Response most reflect user-controlled input
3. Response should contain sensitive data

Especially part 2 is with ownCloud not really given since user-input is usually only echoed if a CSRF token has been passed.

To reduce the risk even further it is however sensible to encrypt the CSRF token with a shared secret. Since this will change on every request an attack such as BREACH is not feasible anymore against the CSRF token at least.
2015-08-14 01:31:32 +02:00

245 lines
7.1 KiB
PHP

<?php
/**
* ownCloud - App Framework
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @copyright Bernhard Posselt 2014
*/
namespace OC\AppFramework\Middleware\Security;
use OC\AppFramework\Http\Request;
use OC\AppFramework\Utility\ControllerMethodReflector;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
class CORSMiddlewareTest extends \Test\TestCase {
private $reflector;
private $session;
protected function setUp() {
parent::setUp();
$this->reflector = new ControllerMethodReflector();
$this->session = $this->getMock('\OCP\IUserSession');
}
/**
* @CORS
*/
public function testSetCORSAPIHeader() {
$request = new Request(
[
'server' => [
'HTTP_ORIGIN' => 'test'
]
],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$this->reflector->reflect($this, __FUNCTION__);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$response = $middleware->afterController($this, __FUNCTION__, new Response());
$headers = $response->getHeaders();
$this->assertEquals('test', $headers['Access-Control-Allow-Origin']);
}
public function testNoAnnotationNoCORSHEADER() {
$request = new Request(
[
'server' => [
'HTTP_ORIGIN' => 'test'
]
],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$response = $middleware->afterController($this, __FUNCTION__, new Response());
$headers = $response->getHeaders();
$this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers));
}
/**
* @CORS
*/
public function testNoOriginHeaderNoCORSHEADER() {
$request = new Request(
[],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$this->reflector->reflect($this, __FUNCTION__);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$response = $middleware->afterController($this, __FUNCTION__, new Response());
$headers = $response->getHeaders();
$this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers));
}
/**
* @CORS
* @expectedException \OC\AppFramework\Middleware\Security\SecurityException
*/
public function testCorsIgnoredIfWithCredentialsHeaderPresent() {
$request = new Request(
[
'server' => [
'HTTP_ORIGIN' => 'test'
]
],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$this->reflector->reflect($this, __FUNCTION__);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$response = new Response();
$response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE');
$middleware->afterController($this, __FUNCTION__, $response);
}
/**
* @CORS
* @PublicPage
*/
public function testNoCORSShouldAllowCookieAuth() {
$request = new Request(
[],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$this->reflector->reflect($this, __FUNCTION__);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$this->session->expects($this->never())
->method('logout');
$this->session->expects($this->never())
->method('login')
->with($this->equalTo('user'), $this->equalTo('pass'))
->will($this->returnValue(true));
$this->reflector->reflect($this, __FUNCTION__);
$middleware->beforeController($this, __FUNCTION__, new Response());
}
/**
* @CORS
*/
public function testCORSShouldRelogin() {
$request = new Request(
['server' => [
'PHP_AUTH_USER' => 'user',
'PHP_AUTH_PW' => 'pass'
]],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$this->session->expects($this->once())
->method('logout');
$this->session->expects($this->once())
->method('login')
->with($this->equalTo('user'), $this->equalTo('pass'))
->will($this->returnValue(true));
$this->reflector->reflect($this, __FUNCTION__);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$middleware->beforeController($this, __FUNCTION__, new Response());
}
/**
* @CORS
* @expectedException \OC\AppFramework\Middleware\Security\SecurityException
*/
public function testCORSShouldNotAllowCookieAuth() {
$request = new Request(
['server' => [
'PHP_AUTH_USER' => 'user',
'PHP_AUTH_PW' => 'pass'
]],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$this->session->expects($this->once())
->method('logout');
$this->session->expects($this->once())
->method('login')
->with($this->equalTo('user'), $this->equalTo('pass'))
->will($this->returnValue(false));
$this->reflector->reflect($this, __FUNCTION__);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$middleware->beforeController($this, __FUNCTION__, new Response());
}
public function testAfterExceptionWithSecurityExceptionNoStatus() {
$request = new Request(
['server' => [
'PHP_AUTH_USER' => 'user',
'PHP_AUTH_PW' => 'pass'
]],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$response = $middleware->afterException($this, __FUNCTION__, new SecurityException('A security exception'));
$expected = new JSONResponse(['message' => 'A security exception'], 500);
$this->assertEquals($expected, $response);
}
public function testAfterExceptionWithSecurityExceptionWithStatus() {
$request = new Request(
['server' => [
'PHP_AUTH_USER' => 'user',
'PHP_AUTH_PW' => 'pass'
]],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$response = $middleware->afterException($this, __FUNCTION__, new SecurityException('A security exception', 501));
$expected = new JSONResponse(['message' => 'A security exception'], 501);
$this->assertEquals($expected, $response);
}
/**
* @expectedException \Exception
* @expectedExceptionMessage A regular exception
*/
public function testAfterExceptionWithRegularException() {
$request = new Request(
['server' => [
'PHP_AUTH_USER' => 'user',
'PHP_AUTH_PW' => 'pass'
]],
$this->getMock('\OCP\Security\ISecureRandom'),
$this->getMock('\OCP\Security\ICrypto'),
$this->getMock('\OCP\IConfig')
);
$middleware = new CORSMiddleware($request, $this->reflector, $this->session);
$middleware->afterException($this, __FUNCTION__, new \Exception('A regular exception'));
}
}