Browse Source

Add a public interface for the bruteforce throttler and register for injection

Signed-off-by: Joas Schilling <coding@schilljs.com>
tags/v25.0.0beta1
Joas Schilling 2 years ago
parent
commit
c0f47af2d0
No account linked to committer's email address

+ 1
- 0
lib/composer/composer/autoload_classmap.php View File

@@ -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',

+ 1
- 0
lib/composer/composer/autoload_static.php View File

@@ -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',

+ 3
- 5
lib/private/Security/Bruteforce/Throttler.php View File

@@ -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();

+ 2
- 0
lib/private/Server.php View File

@@ -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.

+ 126
- 0
lib/public/Security/Bruteforce/IThrottler.php View File

@@ -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;
}

Loading…
Cancel
Save