diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php index b0b87c66ca638..c99c5bcd83399 100644 --- a/apps/dav/appinfo/v1/publicwebdav.php +++ b/apps/dav/appinfo/v1/publicwebdav.php @@ -41,7 +41,8 @@ $authBackend = new OCA\DAV\Connector\PublicAuth( \OC::$server->getRequest(), \OC::$server->getShareManager(), - \OC::$server->getSession() + \OC::$server->getSession(), + \OC::$server->getBruteForceThrottler() ); $authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend); diff --git a/apps/dav/lib/Connector/PublicAuth.php b/apps/dav/lib/Connector/PublicAuth.php index 55d2de72228ee..426cbf871d71c 100644 --- a/apps/dav/lib/Connector/PublicAuth.php +++ b/apps/dav/lib/Connector/PublicAuth.php @@ -29,6 +29,7 @@ */ namespace OCA\DAV\Connector; +use OC\Security\Bruteforce\Throttler; use OCP\IRequest; use OCP\ISession; use OCP\Share\Exceptions\ShareNotFound; @@ -42,6 +43,7 @@ * @package OCA\DAV\Connector */ class PublicAuth extends AbstractBasic { + private const BRUTEFORCE_ACTION = 'public_webdav_auth'; /** @var \OCP\Share\IShare */ private $share; @@ -55,17 +57,23 @@ class PublicAuth extends AbstractBasic { /** @var IRequest */ private $request; + /** @var Throttler */ + private $throttler; + /** * @param IRequest $request * @param IManager $shareManager * @param ISession $session + * @param Throttler $throttler */ public function __construct(IRequest $request, IManager $shareManager, - ISession $session) { + ISession $session, + Throttler $throttler) { $this->request = $request; $this->shareManager = $shareManager; $this->session = $session; + $this->throttler = $throttler; // setup realm $defaults = new \OCP\Defaults(); @@ -85,9 +93,12 @@ public function __construct(IRequest $request, * @throws \Sabre\DAV\Exception\NotAuthenticated */ protected function validateUserPass($username, $password) { + $this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION); + try { $share = $this->shareManager->getShareByToken($username); } catch (ShareNotFound $e) { + $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress()); return false; } @@ -112,11 +123,14 @@ protected function validateUserPass($username, $password) { header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"'); throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); } + + $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress()); return false; } } elseif ($share->getShareType() === IShare::TYPE_REMOTE) { return true; } else { + $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress()); return false; } } else { diff --git a/apps/dav/tests/unit/Connector/PublicAuthTest.php b/apps/dav/tests/unit/Connector/PublicAuthTest.php index cdf269deb33f2..89068c0e6effd 100644 --- a/apps/dav/tests/unit/Connector/PublicAuthTest.php +++ b/apps/dav/tests/unit/Connector/PublicAuthTest.php @@ -26,6 +26,7 @@ */ namespace OCA\DAV\Tests\unit\Connector; +use OC\Security\Bruteforce\Throttler; use OCP\IRequest; use OCP\ISession; use OCP\Share\Exceptions\ShareNotFound; @@ -49,6 +50,8 @@ class PublicAuthTest extends \Test\TestCase { private $shareManager; /** @var \OCA\DAV\Connector\PublicAuth */ private $auth; + /** @var Throttler|\PHPUnit\Framework\MockObject\MockObject */ + private $throttler; /** @var string */ private $oldUser; @@ -65,11 +68,15 @@ protected function setUp(): void { $this->shareManager = $this->getMockBuilder(IManager::class) ->disableOriginalConstructor() ->getMock(); + $this->throttler = $this->getMockBuilder(Throttler::class) + ->disableOriginalConstructor() + ->getMock(); $this->auth = new \OCA\DAV\Connector\PublicAuth( $this->request, $this->shareManager, - $this->session + $this->session, + $this->throttler ); // Store current user