introduce brute force protection for api calls

Signed-off-by: Bjoern Schiessle <bjoern@schiessle.org>
This commit is contained in:
Bjoern Schiessle 2017-01-17 11:51:10 +01:00
parent 4bbd52b3f9
commit df296249d6
No known key found for this signature in database
GPG Key ID: 2378A753E2BF04F6
3 changed files with 37 additions and 11 deletions

View File

@ -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) {

View File

@ -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)

View File

@ -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;
}