Signed-off-by: Joas Schilling <coding@schilljs.com>tags/v25.0.0beta1
@@ -510,6 +510,7 @@ return array( | |||
'OCP\\Search\\Result' => $baseDir . '/lib/public/Search/Result.php', | |||
'OCP\\Search\\SearchResult' => $baseDir . '/lib/public/Search/SearchResult.php', | |||
'OCP\\Search\\SearchResultEntry' => $baseDir . '/lib/public/Search/SearchResultEntry.php', | |||
'OCP\\Security\\Bruteforce\\IThrottler' => $baseDir . '/lib/public/Security/Bruteforce/IThrottler.php', | |||
'OCP\\Security\\Bruteforce\\MaxDelayReached' => $baseDir . '/lib/public/Security/Bruteforce/MaxDelayReached.php', | |||
'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => $baseDir . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php', | |||
'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => $baseDir . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php', |
@@ -543,6 +543,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 | |||
'OCP\\Search\\Result' => __DIR__ . '/../../..' . '/lib/public/Search/Result.php', | |||
'OCP\\Search\\SearchResult' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResult.php', | |||
'OCP\\Search\\SearchResultEntry' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResultEntry.php', | |||
'OCP\\Security\\Bruteforce\\IThrottler' => __DIR__ . '/../../..' . '/lib/public/Security/Bruteforce/IThrottler.php', | |||
'OCP\\Security\\Bruteforce\\MaxDelayReached' => __DIR__ . '/../../..' . '/lib/public/Security/Bruteforce/MaxDelayReached.php', | |||
'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => __DIR__ . '/../../..' . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php', | |||
'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => __DIR__ . '/../../..' . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php', |
@@ -36,6 +36,7 @@ use OC\Security\Normalizer\IpAddress; | |||
use OCP\AppFramework\Utility\ITimeFactory; | |||
use OCP\IConfig; | |||
use OCP\IDBConnection; | |||
use OCP\Security\Bruteforce\IThrottler; | |||
use OCP\Security\Bruteforce\MaxDelayReached; | |||
use Psr\Log\LoggerInterface; | |||
@@ -52,11 +53,8 @@ use Psr\Log\LoggerInterface; | |||
* | |||
* @package OC\Security\Bruteforce | |||
*/ | |||
class Throttler { | |||
class Throttler implements IThrottler { | |||
public const LOGIN_ACTION = 'login'; | |||
public const MAX_DELAY = 25; | |||
public const MAX_DELAY_MS = 25000; // in milliseconds | |||
public const MAX_ATTEMPTS = 10; | |||
/** @var IDBConnection */ | |||
private $db; | |||
@@ -311,7 +309,7 @@ class Throttler { | |||
* | |||
* @param string $ip | |||
*/ | |||
public function resetDelayForIP($ip) { | |||
public function resetDelayForIP(string $ip): void { | |||
$cutoffTime = $this->getCutoffTimestamp(); | |||
$qb = $this->db->getQueryBuilder(); |
@@ -231,6 +231,7 @@ use OCP\Remote\Api\IApiFactory; | |||
use OCP\Remote\IInstanceFactory; | |||
use OCP\RichObjectStrings\IValidator; | |||
use OCP\Route\IRouter; | |||
use OCP\Security\Bruteforce\IThrottler; | |||
use OCP\Security\IContentSecurityPolicyManager; | |||
use OCP\Security\ICredentialsManager; | |||
use OCP\Security\ICrypto; | |||
@@ -1002,6 +1003,7 @@ class Server extends ServerContainer implements IServerContainer { | |||
$this->registerAlias(ITrustedDomainHelper::class, TrustedDomainHelper::class); | |||
/** @deprecated 19.0.0 */ | |||
$this->registerDeprecatedAlias('Throttler', Throttler::class); | |||
$this->registerAlias(IThrottler::class, Throttler::class); | |||
$this->registerService('IntegrityCodeChecker', function (ContainerInterface $c) { | |||
// IConfig and IAppManager requires a working database. This code | |||
// might however be called when ownCloud is not yet setup. |
@@ -0,0 +1,126 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\Security\Bruteforce; | |||
/** | |||
* Class Throttler implements the bruteforce protection for security actions in | |||
* Nextcloud. | |||
* | |||
* It is working by logging invalid login attempts to the database and slowing | |||
* down all login attempts from the same subnet. The max delay is 30 seconds and | |||
* the starting delay are 200 milliseconds. (after the first failed login) | |||
* | |||
* This is based on Paragonie's AirBrake for Airship CMS. You can find the original | |||
* code at https://github.com/paragonie/airship/blob/7e5bad7e3c0fbbf324c11f963fd1f80e59762606/src/Engine/Security/AirBrake.php | |||
* | |||
* @package OC\Security\Bruteforce | |||
* @since 25.0.0 | |||
*/ | |||
interface IThrottler { | |||
/** | |||
* @since 25.0.0 | |||
*/ | |||
public const MAX_DELAY = 25; | |||
/** | |||
* @since 25.0.0 | |||
*/ | |||
public const MAX_DELAY_MS = 25000; // in milliseconds | |||
/** | |||
* @since 25.0.0 | |||
*/ | |||
public const MAX_ATTEMPTS = 10; | |||
/** | |||
* Register a failed attempt to bruteforce a security control | |||
* | |||
* @param string $action | |||
* @param string $ip | |||
* @param array $metadata Optional metadata logged to the database | |||
* @since 25.0.0 | |||
*/ | |||
public function registerAttempt(string $action, string $ip, array $metadata = []): void; | |||
/** | |||
* Get the throttling delay (in milliseconds) | |||
* | |||
* @param string $ip | |||
* @param string $action optionally filter by action | |||
* @param float $maxAgeHours | |||
* @return int | |||
* @since 25.0.0 | |||
*/ | |||
public function getAttempts(string $ip, string $action = '', float $maxAgeHours = 12): int; | |||
/** | |||
* Get the throttling delay (in milliseconds) | |||
* | |||
* @param string $ip | |||
* @param string $action optionally filter by action | |||
* @return int | |||
* @since 25.0.0 | |||
*/ | |||
public function getDelay(string $ip, string $action = ''): int; | |||
/** | |||
* Reset the throttling delay for an IP address, action and metadata | |||
* | |||
* @param string $ip | |||
* @param string $action | |||
* @param array $metadata | |||
* @since 25.0.0 | |||
*/ | |||
public function resetDelay(string $ip, string $action, array $metadata): void; | |||
/** | |||
* Reset the throttling delay for an IP address | |||
* | |||
* @param string $ip | |||
* @since 25.0.0 | |||
*/ | |||
public function resetDelayForIP(string $ip): void; | |||
/** | |||
* Will sleep for the defined amount of time | |||
* | |||
* @param string $ip | |||
* @param string $action optionally filter by action | |||
* @return int the time spent sleeping | |||
* @since 25.0.0 | |||
*/ | |||
public function sleepDelay(string $ip, string $action = ''): int; | |||
/** | |||
* Will sleep for the defined amount of time unless maximum was reached in the last 30 minutes | |||
* In this case a "429 Too Many Request" exception is thrown | |||
* | |||
* @param string $ip | |||
* @param string $action optionally filter by action | |||
* @return int the time spent sleeping | |||
* @throws MaxDelayReached when reached the maximum | |||
* @since 25.0.0 | |||
*/ | |||
public function sleepDelayOrThrowOnMax(string $ip, string $action = ''): int; | |||
} |