Skip to content

Commit c5339fa

Browse files
Merge pull request #37542 from nextcloud/bugfix/noid/allow-to-opt-out-of-ratelimit-for-testing
feat(security): Allow to opt-out of ratelimit protection, e.g. for te…
2 parents 1000b46 + 454281a commit c5339fa

File tree

5 files changed

+50
-13
lines changed

5 files changed

+50
-13
lines changed

config/config.sample.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@
302302

303303
/**
304304
* The interval at which token activity should be updated.
305-
* Increasing this value means that the last activty on the security page gets
305+
* Increasing this value means that the last activity on the security page gets
306306
* more outdated.
307307
*
308308
* Tokens are still checked every 5 minutes for validity
@@ -321,6 +321,15 @@
321321
*/
322322
'auth.bruteforce.protection.enabled' => true,
323323

324+
/**
325+
* Whether the rate limit protection shipped with Nextcloud should be enabled or not.
326+
*
327+
* Disabling this is discouraged for security reasons.
328+
*
329+
* Defaults to ``true``
330+
*/
331+
'ratelimit.protection.enabled' => true,
332+
324333
/**
325334
* By default, WebAuthn is available, but it can be explicitly disabled by admins
326335
*/

lib/private/Security/RateLimiting/Backend/DatabaseBackend.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
declare(strict_types=1);
44

55
/**
6+
* @copyright Copyright (c) 2023 Joas Schilling <[email protected]>
67
* @copyright Copyright (c) 2021 Lukas Reschke <[email protected]>
78
*
9+
* @author Joas Schilling <[email protected]>
810
* @author Lukas Reschke <[email protected]>
911
*
1012
* @license GNU AGPL version 3 or any later version
@@ -27,24 +29,25 @@
2729

2830
use OCP\AppFramework\Utility\ITimeFactory;
2931
use OCP\DB\QueryBuilder\IQueryBuilder;
32+
use OCP\IConfig;
3033
use OCP\IDBConnection;
3134

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

38+
/** @var IConfig */
39+
private $config;
3540
/** @var IDBConnection */
3641
private $dbConnection;
3742
/** @var ITimeFactory */
3843
private $timeFactory;
3944

40-
/**
41-
* @param IDBConnection $dbConnection
42-
* @param ITimeFactory $timeFactory
43-
*/
4445
public function __construct(
46+
IConfig $config,
4547
IDBConnection $dbConnection,
4648
ITimeFactory $timeFactory
4749
) {
50+
$this->config = $config;
4851
$this->dbConnection = $dbConnection;
4952
$this->timeFactory = $timeFactory;
5053
}
@@ -115,7 +118,12 @@ public function registerAttempt(string $methodIdentifier,
115118
->values([
116119
'hash' => $qb->createNamedParameter($identifier, IQueryBuilder::PARAM_STR),
117120
'delete_after' => $qb->createNamedParameter($deleteAfter, IQueryBuilder::PARAM_DATE),
118-
])
119-
->executeStatement();
121+
]);
122+
123+
if (!$this->config->getSystemValueBool('ratelimit.protection.enabled', true)) {
124+
return;
125+
}
126+
127+
$qb->executeStatement();
120128
}
121129
}

lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
declare(strict_types=1);
44

55
/**
6+
* @copyright Copyright (c) 2023 Joas Schilling <[email protected]>
67
* @copyright Copyright (c) 2017 Lukas Reschke <[email protected]>
78
*
89
* @author Christoph Wurst <[email protected]>
10+
* @author Joas Schilling <[email protected]>
911
* @author Lukas Reschke <[email protected]>
1012
* @author Morris Jobke <[email protected]>
1113
* @author Roeland Jago Douma <[email protected]>
@@ -31,6 +33,7 @@
3133
use OCP\AppFramework\Utility\ITimeFactory;
3234
use OCP\ICache;
3335
use OCP\ICacheFactory;
36+
use OCP\IConfig;
3437

3538
/**
3639
* Class MemoryCacheBackend uses the configured distributed memory cache for storing
@@ -39,17 +42,18 @@
3942
* @package OC\Security\RateLimiting\Backend
4043
*/
4144
class MemoryCacheBackend implements IBackend {
45+
/** @var IConfig */
46+
private $config;
4247
/** @var ICache */
4348
private $cache;
4449
/** @var ITimeFactory */
4550
private $timeFactory;
4651

47-
/**
48-
* @param ICacheFactory $cacheFactory
49-
* @param ITimeFactory $timeFactory
50-
*/
51-
public function __construct(ICacheFactory $cacheFactory,
52-
ITimeFactory $timeFactory) {
52+
public function __construct(
53+
IConfig $config,
54+
ICacheFactory $cacheFactory,
55+
ITimeFactory $timeFactory) {
56+
$this->config = $config;
5357
$this->cache = $cacheFactory->createDistributed(__CLASS__);
5458
$this->timeFactory = $timeFactory;
5559
}
@@ -121,6 +125,11 @@ public function registerAttempt(string $methodIdentifier,
121125

122126
// Store the new attempt
123127
$existingAttempts[] = (string)($currentTime + $period);
128+
129+
if (!$this->config->getSystemValueBool('ratelimit.protection.enabled', true)) {
130+
return;
131+
}
132+
124133
$this->cache->set($identifier, json_encode($existingAttempts));
125134
}
126135
}

lib/private/Server.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,11 +846,13 @@ public function __construct($webRoot, \OC\Config $config) {
846846
$cacheFactory = $c->get(ICacheFactory::class);
847847
if ($cacheFactory->isAvailable()) {
848848
$backend = new \OC\Security\RateLimiting\Backend\MemoryCacheBackend(
849+
$c->get(AllConfig::class),
849850
$this->get(ICacheFactory::class),
850851
new \OC\AppFramework\Utility\TimeFactory()
851852
);
852853
} else {
853854
$backend = new \OC\Security\RateLimiting\Backend\DatabaseBackend(
855+
$c->get(AllConfig::class),
854856
$c->get(IDBConnection::class),
855857
new \OC\AppFramework\Utility\TimeFactory()
856858
);

tests/lib/Security/RateLimiting/Backend/MemoryCacheBackendTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@
2828
use OCP\AppFramework\Utility\ITimeFactory;
2929
use OCP\ICache;
3030
use OCP\ICacheFactory;
31+
use OCP\IConfig;
3132
use Test\TestCase;
3233

3334
class MemoryCacheBackendTest extends TestCase {
35+
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
36+
private $config;
3437
/** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
3538
private $cacheFactory;
3639
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
@@ -43,6 +46,7 @@ class MemoryCacheBackendTest extends TestCase {
4346
protected function setUp(): void {
4447
parent::setUp();
4548

49+
$this->config = $this->createMock(IConfig::class);
4650
$this->cacheFactory = $this->createMock(ICacheFactory::class);
4751
$this->timeFactory = $this->createMock(ITimeFactory::class);
4852
$this->cache = $this->createMock(ICache::class);
@@ -53,7 +57,12 @@ protected function setUp(): void {
5357
->with('OC\Security\RateLimiting\Backend\MemoryCacheBackend')
5458
->willReturn($this->cache);
5559

60+
$this->config->method('getSystemValueBool')
61+
->with('ratelimit.protection.enabled')
62+
->willReturn(true);
63+
5664
$this->memoryCache = new MemoryCacheBackend(
65+
$this->config,
5766
$this->cacheFactory,
5867
$this->timeFactory
5968
);

0 commit comments

Comments
 (0)