Преглед на файлове

feat(security): Allow to opt-out of ratelimit protection, e.g. for testing on CI

Signed-off-by: Joas Schilling <coding@schilljs.com>
tags/v27.0.0beta1
Joas Schilling преди 1 година
родител
ревизия
454281af03
No account linked to committer's email address

+ 10
- 1
config/config.sample.php Целия файл



/** /**
* The interval at which token activity should be updated. * The interval at which token activity should be updated.
* Increasing this value means that the last activty on the security page gets
* Increasing this value means that the last activity on the security page gets
* more outdated. * more outdated.
* *
* Tokens are still checked every 5 minutes for validity * Tokens are still checked every 5 minutes for validity
*/ */
'auth.bruteforce.protection.enabled' => true, 'auth.bruteforce.protection.enabled' => true,


/**
* Whether the rate limit protection shipped with Nextcloud should be enabled or not.
*
* Disabling this is discouraged for security reasons.
*
* Defaults to ``true``
*/
'ratelimit.protection.enabled' => true,

/** /**
* By default, WebAuthn is available, but it can be explicitly disabled by admins * By default, WebAuthn is available, but it can be explicitly disabled by admins
*/ */

+ 14
- 6
lib/private/Security/RateLimiting/Backend/DatabaseBackend.php Целия файл

declare(strict_types=1); declare(strict_types=1);


/** /**
* @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
* @copyright Copyright (c) 2021 Lukas Reschke <lukas@statuscode.ch> * @copyright Copyright (c) 2021 Lukas Reschke <lukas@statuscode.ch>
* *
* @author Joas Schilling <coding@schilljs.com>
* @author Lukas Reschke <lukas@statuscode.ch> * @author Lukas Reschke <lukas@statuscode.ch>
* *
* @license GNU AGPL version 3 or any later version * @license GNU AGPL version 3 or any later version


use OCP\AppFramework\Utility\ITimeFactory; use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection; use OCP\IDBConnection;


class DatabaseBackend implements IBackend { class DatabaseBackend implements IBackend {
private const TABLE_NAME = 'ratelimit_entries'; private const TABLE_NAME = 'ratelimit_entries';


/** @var IConfig */
private $config;
/** @var IDBConnection */ /** @var IDBConnection */
private $dbConnection; private $dbConnection;
/** @var ITimeFactory */ /** @var ITimeFactory */
private $timeFactory; private $timeFactory;


/**
* @param IDBConnection $dbConnection
* @param ITimeFactory $timeFactory
*/
public function __construct( public function __construct(
IConfig $config,
IDBConnection $dbConnection, IDBConnection $dbConnection,
ITimeFactory $timeFactory ITimeFactory $timeFactory
) { ) {
$this->config = $config;
$this->dbConnection = $dbConnection; $this->dbConnection = $dbConnection;
$this->timeFactory = $timeFactory; $this->timeFactory = $timeFactory;
} }
->values([ ->values([
'hash' => $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR), 'hash' => $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR),
'delete_after' => $qb->createNamedParameter($deleteAfter, IQueryBuilder::PARAM_DATE), 'delete_after' => $qb->createNamedParameter($deleteAfter, IQueryBuilder::PARAM_DATE),
])
->executeStatement();
]);

if (!$this->config->getSystemValueBool('ratelimit.protection.enabled', true)) {
return;
}

$qb->executeStatement();
} }
} }

+ 15
- 6
lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php Целия файл

declare(strict_types=1); declare(strict_types=1);


/** /**
* @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
* *
* @author Christoph Wurst <christoph@winzerhof-wurst.at> * @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Lukas Reschke <lukas@statuscode.ch> * @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de> * @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl> * @author Roeland Jago Douma <roeland@famdouma.nl>
use OCP\AppFramework\Utility\ITimeFactory; use OCP\AppFramework\Utility\ITimeFactory;
use OCP\ICache; use OCP\ICache;
use OCP\ICacheFactory; use OCP\ICacheFactory;
use OCP\IConfig;


/** /**
* Class MemoryCacheBackend uses the configured distributed memory cache for storing * Class MemoryCacheBackend uses the configured distributed memory cache for storing
* @package OC\Security\RateLimiting\Backend * @package OC\Security\RateLimiting\Backend
*/ */
class MemoryCacheBackend implements IBackend { class MemoryCacheBackend implements IBackend {
/** @var IConfig */
private $config;
/** @var ICache */ /** @var ICache */
private $cache; private $cache;
/** @var ITimeFactory */ /** @var ITimeFactory */
private $timeFactory; private $timeFactory;


/**
* @param ICacheFactory $cacheFactory
* @param ITimeFactory $timeFactory
*/
public function __construct(ICacheFactory $cacheFactory,
ITimeFactory $timeFactory) {
public function __construct(
IConfig $config,
ICacheFactory $cacheFactory,
ITimeFactory $timeFactory) {
$this->config = $config;
$this->cache = $cacheFactory->createDistributed(__CLASS__); $this->cache = $cacheFactory->createDistributed(__CLASS__);
$this->timeFactory = $timeFactory; $this->timeFactory = $timeFactory;
} }


// Store the new attempt // Store the new attempt
$existingAttempts[] = (string)($currentTime + $period); $existingAttempts[] = (string)($currentTime + $period);

if (!$this->config->getSystemValueBool('ratelimit.protection.enabled', true)) {
return;
}

$this->cache->set($identifier, json_encode($existingAttempts)); $this->cache->set($identifier, json_encode($existingAttempts));
} }
} }

+ 2
- 0
lib/private/Server.php Целия файл

$cacheFactory = $c->get(ICacheFactory::class); $cacheFactory = $c->get(ICacheFactory::class);
if ($cacheFactory->isAvailable()) { if ($cacheFactory->isAvailable()) {
$backend = new \OC\Security\RateLimiting\Backend\MemoryCacheBackend( $backend = new \OC\Security\RateLimiting\Backend\MemoryCacheBackend(
$c->get(AllConfig::class),
$this->get(ICacheFactory::class), $this->get(ICacheFactory::class),
new \OC\AppFramework\Utility\TimeFactory() new \OC\AppFramework\Utility\TimeFactory()
); );
} else { } else {
$backend = new \OC\Security\RateLimiting\Backend\DatabaseBackend( $backend = new \OC\Security\RateLimiting\Backend\DatabaseBackend(
$c->get(AllConfig::class),
$c->get(IDBConnection::class), $c->get(IDBConnection::class),
new \OC\AppFramework\Utility\TimeFactory() new \OC\AppFramework\Utility\TimeFactory()
); );

+ 9
- 0
tests/lib/Security/RateLimiting/Backend/MemoryCacheBackendTest.php Целия файл

use OCP\AppFramework\Utility\ITimeFactory; use OCP\AppFramework\Utility\ITimeFactory;
use OCP\ICache; use OCP\ICache;
use OCP\ICacheFactory; use OCP\ICacheFactory;
use OCP\IConfig;
use Test\TestCase; use Test\TestCase;


class MemoryCacheBackendTest extends TestCase { class MemoryCacheBackendTest extends TestCase {
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $config;
/** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
private $cacheFactory; private $cacheFactory;
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();


$this->config = $this->createMock(IConfig::class);
$this->cacheFactory = $this->createMock(ICacheFactory::class); $this->cacheFactory = $this->createMock(ICacheFactory::class);
$this->timeFactory = $this->createMock(ITimeFactory::class); $this->timeFactory = $this->createMock(ITimeFactory::class);
$this->cache = $this->createMock(ICache::class); $this->cache = $this->createMock(ICache::class);
->with('OC\Security\RateLimiting\Backend\MemoryCacheBackend') ->with('OC\Security\RateLimiting\Backend\MemoryCacheBackend')
->willReturn($this->cache); ->willReturn($this->cache);


$this->config->method('getSystemValueBool')
->with('ratelimit.protection.enabled')
->willReturn(true);

$this->memoryCache = new MemoryCacheBackend( $this->memoryCache = new MemoryCacheBackend(
$this->config,
$this->cacheFactory, $this->cacheFactory,
$this->timeFactory $this->timeFactory
); );

Loading…
Отказ
Запис