introduce brute force protection for api calls
Signed-off-by: Bjoern Schiessle <bjoern@schiessle.org>
This commit is contained in:
parent
4bbd52b3f9
commit
df296249d6
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue