Signed-off-by: Bjoern Schiessle <bjoern@schiessle.org>tags/v12.0.0beta1
@@ -43,8 +43,10 @@ use OC\AppFramework\Middleware\OCSMiddleware; | |||
use OC\AppFramework\Middleware\Security\SecurityMiddleware; | |||
use OC\AppFramework\Middleware\SessionMiddleware; | |||
use OC\AppFramework\Utility\SimpleContainer; | |||
use OC\AppFramework\Utility\TimeFactory; | |||
use OC\Core\Middleware\TwoFactorMiddleware; | |||
use OC\RichObjectStrings\Validator; | |||
use OC\Security\Bruteforce\Throttler; | |||
use OCP\AppFramework\IApi; | |||
use OCP\AppFramework\IAppContainer; | |||
use OCP\Files\IAppData; | |||
@@ -376,20 +378,25 @@ class DIContainer extends SimpleContainer implements IAppContainer { | |||
*/ | |||
$app = $this; | |||
$this->registerService('SecurityMiddleware', function($c) use ($app){ | |||
/** @var \OC\Server $server */ | |||
$server = $app->getServer(); | |||
return new SecurityMiddleware( | |||
$c['Request'], | |||
$c['ControllerMethodReflector'], | |||
$app->getServer()->getNavigationManager(), | |||
$app->getServer()->getURLGenerator(), | |||
$app->getServer()->getLogger(), | |||
$app->getServer()->getSession(), | |||
$server->getNavigationManager(), | |||
$server->getURLGenerator(), | |||
$server->getLogger(), | |||
$server->getSession(), | |||
$c['AppName'], | |||
$app->isLoggedIn(), | |||
$app->isAdminUser(), | |||
$app->getServer()->getContentSecurityPolicyManager(), | |||
$app->getServer()->getCsrfTokenManager(), | |||
$app->getServer()->getContentSecurityPolicyNonceManager() | |||
$server->getContentSecurityPolicyManager(), | |||
$server->getCsrfTokenManager(), | |||
$server->getContentSecurityPolicyNonceManager(), | |||
$server->getBruteForceThrottler() | |||
); | |||
}); | |||
$this->registerService('CORSMiddleware', function($c) { |
@@ -36,6 +36,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException; | |||
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; | |||
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException; | |||
use OC\AppFramework\Utility\ControllerMethodReflector; | |||
use OC\Security\Bruteforce\Throttler; | |||
use OC\Security\CSP\ContentSecurityPolicyManager; | |||
use OC\Security\CSP\ContentSecurityPolicyNonceManager; | |||
use OC\Security\CSRF\CsrfTokenManager; | |||
@@ -87,6 +88,8 @@ class SecurityMiddleware extends Middleware { | |||
private $csrfTokenManager; | |||
/** @var ContentSecurityPolicyNonceManager */ | |||
private $cspNonceManager; | |||
/** @var Throttler */ | |||
private $throttler; | |||
/** | |||
* @param IRequest $request | |||
@@ -101,6 +104,7 @@ class SecurityMiddleware extends Middleware { | |||
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager | |||
* @param CSRFTokenManager $csrfTokenManager | |||
* @param ContentSecurityPolicyNonceManager $cspNonceManager | |||
* @param Throttler $throttler | |||
*/ | |||
public function __construct(IRequest $request, | |||
ControllerMethodReflector $reflector, | |||
@@ -113,7 +117,8 @@ class SecurityMiddleware extends Middleware { | |||
$isAdminUser, | |||
ContentSecurityPolicyManager $contentSecurityPolicyManager, | |||
CsrfTokenManager $csrfTokenManager, | |||
ContentSecurityPolicyNonceManager $cspNonceManager) { | |||
ContentSecurityPolicyNonceManager $cspNonceManager, | |||
Throttler $throttler) { | |||
$this->navigationManager = $navigationManager; | |||
$this->request = $request; | |||
$this->reflector = $reflector; | |||
@@ -126,6 +131,7 @@ class SecurityMiddleware extends Middleware { | |||
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager; | |||
$this->csrfTokenManager = $csrfTokenManager; | |||
$this->cspNonceManager = $cspNonceManager; | |||
$this->throttler = $throttler; | |||
} | |||
@@ -185,6 +191,12 @@ class SecurityMiddleware extends Middleware { | |||
} | |||
} | |||
if($this->reflector->hasAnnotation('BruteForceProtection')) { | |||
$action = $this->request->getRequestUri(); | |||
$this->throttler->sleepDelay($this->request->getRemoteAddress(), $action); | |||
$this->throttler->registerAttempt($action, $this->request->getRemoteAddress()); | |||
} | |||
/** | |||
* FIXME: Use DI once available | |||
* Checks if app is enabled (also includes a check whether user is allowed to access the resource) |
@@ -189,9 +189,10 @@ class Throttler { | |||
* Get the throttling delay (in milliseconds) | |||
* | |||
* @param string $ip | |||
* @param string $action optionally filter by action | |||
* @return int | |||
*/ | |||
public function getDelay($ip) { | |||
public function getDelay($ip, $action = '') { | |||
$cutoffTime = (new \DateTime()) | |||
->sub($this->getCutoff(43200)) | |||
->getTimestamp(); | |||
@@ -201,6 +202,11 @@ class Throttler { | |||
->from('bruteforce_attempts') | |||
->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime))) | |||
->andWhere($qb->expr()->eq('subnet', $qb->createNamedParameter($this->getSubnet($ip)))); | |||
if ($action !== '') { | |||
$qb->andWhere($qb->expr()->eq('action', $qb->createNamedParameter($action))); | |||
} | |||
$attempts = count($qb->execute()->fetchAll()); | |||
if ($attempts === 0) { | |||
@@ -225,10 +231,11 @@ class Throttler { | |||
* Will sleep for the defined amount of time | |||
* | |||
* @param string $ip | |||
* @param string $action optionally filter by action | |||
* @return int the time spent sleeping | |||
*/ | |||
public function sleepDelay($ip) { | |||
$delay = $this->getDelay($ip); | |||
public function sleepDelay($ip, $action = '') { | |||
$delay = $this->getDelay($ip, $action); | |||
usleep($delay * 1000); | |||
return $delay; | |||
} |