Merge pull request #35057 from nextcloud/enh/noid/rate-limit-public-share-endpoints

Add brute force protection on all methods wrapped by PublicShareMiddleware
pull/35653/head
Julien Veyssier 1 year ago committed by GitHub
commit 1357cf6191
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -304,7 +304,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
new OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware( new OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware(
$c->get(IRequest::class), $c->get(IRequest::class),
$c->get(ISession::class), $c->get(ISession::class),
$c->get(\OCP\IConfig::class) $c->get(\OCP\IConfig::class),
$c->get(OC\Security\Bruteforce\Throttler::class)
) )
); );
$dispatcher->registerMiddleware( $dispatcher->registerMiddleware(

@ -24,6 +24,7 @@
namespace OC\AppFramework\Middleware\PublicShare; namespace OC\AppFramework\Middleware\PublicShare;
use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException; use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException;
use OC\Security\Bruteforce\Throttler;
use OCP\AppFramework\AuthPublicShareController; use OCP\AppFramework\AuthPublicShareController;
use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Middleware; use OCP\AppFramework\Middleware;
@ -34,6 +35,7 @@ use OCP\IRequest;
use OCP\ISession; use OCP\ISession;
class PublicShareMiddleware extends Middleware { class PublicShareMiddleware extends Middleware {
/** @var IRequest */ /** @var IRequest */
private $request; private $request;
@ -43,10 +45,14 @@ class PublicShareMiddleware extends Middleware {
/** @var IConfig */ /** @var IConfig */
private $config; private $config;
public function __construct(IRequest $request, ISession $session, IConfig $config) { /** @var Throttler */
private $throttler;
public function __construct(IRequest $request, ISession $session, IConfig $config, Throttler $throttler) {
$this->request = $request; $this->request = $request;
$this->session = $session; $this->session = $session;
$this->config = $config; $this->config = $config;
$this->throttler = $throttler;
} }
public function beforeController($controller, $methodName) { public function beforeController($controller, $methodName) {
@ -54,6 +60,11 @@ class PublicShareMiddleware extends Middleware {
return; return;
} }
$controllerClassPath = explode('\\', get_class($controller));
$controllerShortClass = end($controllerClassPath);
$bruteforceProtectionAction = $controllerShortClass . '::' . $methodName;
$this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), $bruteforceProtectionAction);
if (!$this->isLinkSharingEnabled()) { if (!$this->isLinkSharingEnabled()) {
throw new NotFoundException('Link sharing is disabled'); throw new NotFoundException('Link sharing is disabled');
} }
@ -68,6 +79,8 @@ class PublicShareMiddleware extends Middleware {
$controller->setToken($token); $controller->setToken($token);
if (!$controller->isValidToken()) { if (!$controller->isValidToken()) {
$this->throttle($bruteforceProtectionAction, $token);
$controller->shareNotFound(); $controller->shareNotFound();
throw new NotFoundException(); throw new NotFoundException();
} }
@ -88,6 +101,7 @@ class PublicShareMiddleware extends Middleware {
throw new NeedAuthenticationException(); throw new NeedAuthenticationException();
} }
$this->throttle($bruteforceProtectionAction, $token);
throw new NotFoundException(); throw new NotFoundException();
} }
@ -128,4 +142,10 @@ class PublicShareMiddleware extends Middleware {
return true; return true;
} }
private function throttle($bruteforceProtectionAction, $token): void {
$ip = $this->request->getRemoteAddress();
$this->throttler->sleepDelay($ip, $bruteforceProtectionAction);
$this->throttler->registerAttempt($bruteforceProtectionAction, $ip, ['token' => $token]);
}
} }

@ -25,6 +25,7 @@ namespace Test\AppFramework\Middleware\PublicShare;
use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException; use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException;
use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware; use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware;
use OC\Security\Bruteforce\Throttler;
use OCP\AppFramework\AuthPublicShareController; use OCP\AppFramework\AuthPublicShareController;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\NotFoundResponse;
@ -44,6 +45,8 @@ class PublicShareMiddlewareTest extends \Test\TestCase {
private $session; private $session;
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $config; private $config;
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $throttler;
/** @var PublicShareMiddleware */ /** @var PublicShareMiddleware */
private $middleware; private $middleware;
@ -55,11 +58,13 @@ class PublicShareMiddlewareTest extends \Test\TestCase {
$this->request = $this->createMock(IRequest::class); $this->request = $this->createMock(IRequest::class);
$this->session = $this->createMock(ISession::class); $this->session = $this->createMock(ISession::class);
$this->config = $this->createMock(IConfig::class); $this->config = $this->createMock(IConfig::class);
$this->throttler = $this->createMock(Throttler::class);
$this->middleware = new PublicShareMiddleware( $this->middleware = new PublicShareMiddleware(
$this->request, $this->request,
$this->session, $this->session,
$this->config $this->config,
$this->throttler
); );
} }

Loading…
Cancel
Save