From 0c38569c83466b52013b7890432bcb6ae74df883 Mon Sep 17 00:00:00 2001 From: Johannes Riedel Date: Tue, 17 Mar 2020 17:06:52 +0100 Subject: [PATCH] Implement occ command security:bruteforceattemps:reset-for-ip Signed-off-by: Johannes Riedel --- .../Security/ResetBruteforceAttempts.php | 62 +++++++++++++++++++ core/register_command.php | 1 + lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/Security/Bruteforce/Throttler.php | 35 +++++++++-- 5 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 core/Command/Security/ResetBruteforceAttempts.php diff --git a/core/Command/Security/ResetBruteforceAttempts.php b/core/Command/Security/ResetBruteforceAttempts.php new file mode 100644 index 0000000000..dcb827f8dd --- /dev/null +++ b/core/Command/Security/ResetBruteforceAttempts.php @@ -0,0 +1,62 @@ +. + * + */ + + +namespace OC\Core\Command\Security; + +use OC\Core\Command\Base; +use OC\Security\Bruteforce\Throttler; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class ResetBruteforceAttempts extends Base { + + /** @var Throttler */ + protected $throttler; + + public function __construct(Throttler $throttler) { + $this->throttler = $throttler; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('security:bruteforce:reset') + ->setDescription('resets bruteforce attemps for given IP address') + ->addArgument( + 'ipaddress', + InputArgument::REQUIRED, + 'IP address for which the attempts are to be reset' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $ip = $input->getArgument('ipaddress'); + + if (!filter_var($ip, FILTER_VALIDATE_IP)) { + $output->writeln('"' . $ip . '" is not a valid IP address'); + return 1; + } + + $this->throttler->resetDelayForIP($ip); + } +} diff --git a/core/register_command.php b/core/register_command.php index efa3146c49..bad27b25ec 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -174,6 +174,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\Security\ListCertificates(\OC::$server->getCertificateManager(null), \OC::$server->getL10N('core'))); $application->add(new OC\Core\Command\Security\ImportCertificate(\OC::$server->getCertificateManager(null))); $application->add(new OC\Core\Command\Security\RemoveCertificate(\OC::$server->getCertificateManager(null))); + $application->add(new OC\Core\Command\Security\ResetBruteforceAttempts(\OC::$server->getBruteForceThrottler())); } else { $application->add(new OC\Core\Command\Maintenance\Install(\OC::$server->getSystemConfig())); } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 0595abb735..303b3fba89 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -770,6 +770,7 @@ return array( 'OC\\Core\\Command\\Security\\ImportCertificate' => $baseDir . '/core/Command/Security/ImportCertificate.php', 'OC\\Core\\Command\\Security\\ListCertificates' => $baseDir . '/core/Command/Security/ListCertificates.php', 'OC\\Core\\Command\\Security\\RemoveCertificate' => $baseDir . '/core/Command/Security/RemoveCertificate.php', + 'OC\\Core\\Command\\Security\\ResetBruteforceAttempts' => $baseDir . '/core/Command/Security/ResetBruteforceAttempts.php', 'OC\\Core\\Command\\Status' => $baseDir . '/core/Command/Status.php', 'OC\\Core\\Command\\TwoFactorAuth\\Base' => $baseDir . '/core/Command/TwoFactorAuth/Base.php', 'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => $baseDir . '/core/Command/TwoFactorAuth/Cleanup.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index d9739745d8..b4a36679cc 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -799,6 +799,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\Security\\ImportCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/ImportCertificate.php', 'OC\\Core\\Command\\Security\\ListCertificates' => __DIR__ . '/../../..' . '/core/Command/Security/ListCertificates.php', 'OC\\Core\\Command\\Security\\RemoveCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/RemoveCertificate.php', + 'OC\\Core\\Command\\Security\\ResetBruteforceAttempts' => __DIR__ . '/../../..' . '/core/Command/Security/ResetBruteforceAttempts.php', 'OC\\Core\\Command\\Status' => __DIR__ . '/../../..' . '/core/Command/Status.php', 'OC\\Core\\Command\\TwoFactorAuth\\Base' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Base.php', 'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Cleanup.php', diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php index b5a4dfbfaf..e53c3c66d3 100644 --- a/lib/private/Security/Bruteforce/Throttler.php +++ b/lib/private/Security/Bruteforce/Throttler.php @@ -88,6 +88,17 @@ class Throttler { return $d2->diff($d1); } + /** + * Calculate the cut off timestamp + * + * @return int + */ + private function getCutoffTimestamp(): int { + return (new \DateTime()) + ->sub($this->getCutoff(43200)) + ->getTimestamp(); + } + /** * Register a failed attempt to bruteforce a security control * @@ -212,9 +223,7 @@ class Throttler { return 0; } - $cutoffTime = (new \DateTime()) - ->sub($this->getCutoff(43200)) - ->getTimestamp(); + $cutoffTime = $this->getCutoffTimestamp(); $qb = $this->db->getQueryBuilder(); $qb->select('*') @@ -259,9 +268,7 @@ class Throttler { return; } - $cutoffTime = (new \DateTime()) - ->sub($this->getCutoff(43200)) - ->getTimestamp(); + $cutoffTime = $this->getCutoffTimestamp(); $qb = $this->db->getQueryBuilder(); $qb->delete('bruteforce_attempts') @@ -273,6 +280,22 @@ class Throttler { $qb->execute(); } + /** + * Reset the throttling delay for an IP address + * + * @param string $ip + */ + public function resetDelayForIP($ip){ + $cutoffTime = $this->getCutoffTimestamp(); + + $qb = $this->db->getQueryBuilder(); + $qb->delete('bruteforce_attempts') + ->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime))) + ->andWhere($qb->expr()->eq('ip', $qb->createNamedParameter($ip))); + + $qb->execute(); + } + /** * Will sleep for the defined amount of time *