[Kimchi-devel] [PATCH][Wok] Bug fix #147: Block authentication request after too many failures

Ramon Medeiros ramonn at linux.vnet.ibm.com
Mon Jan 16 19:17:11 UTC 2017


To prevent brute force attack, creates a mechanism to allow 3 tries
first. After that, a timeout will start and will be added 30 seconds for
each failed try in a row.

Signed-off-by: Ramon Medeiros <ramonn at linux.vnet.ibm.com>
---
 src/wok/i18n.py          |  3 ++-
 src/wok/root.py          | 37 ++++++++++++++++++++++++++++++++++---
 ui/js/src/wok.login.js   | 11 ++++++++++-
 ui/pages/login.html.tmpl |  3 ++-
 4 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index cbc37cf..c594ae1 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -1,7 +1,7 @@
 #
 # Project Wok
 #
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
 #
 # Code derived from Project Kimchi
 #
@@ -41,6 +41,7 @@ messages = {
     "WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"),
     "WOKAUTH0002E": _("You are not authorized to access Kimchi"),
     "WOKAUTH0003E": _("Specify %(item)s to login into Kimchi"),
+    "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."),
     "WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"),
 
     "WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"),
diff --git a/src/wok/root.py b/src/wok/root.py
index 080b7f0..44e7adb 100644
--- a/src/wok/root.py
+++ b/src/wok/root.py
@@ -1,7 +1,7 @@
 #
 # Project Wok
 #
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
 #
 # Code derived from Project Kimchi
 #
@@ -22,6 +22,7 @@
 import cherrypy
 import json
 import os
+import time
 from distutils.version import LooseVersion
 
 from wok import auth
@@ -31,7 +32,7 @@ from wok.config import paths as wok_paths
 from wok.control import sub_nodes
 from wok.control.base import Resource
 from wok.control.utils import parse_request
-from wok.exception import MissingParameter
+from wok.exception import MissingParameter, UnauthorizedError
 from wok.reqlogger import log_request
 
 
@@ -48,7 +49,8 @@ class Root(Resource):
         super(Root, self).__init__(model)
         self._handled_error = ['error_page.400', 'error_page.404',
                                'error_page.405', 'error_page.406',
-                               'error_page.415', 'error_page.500']
+                               'error_page.415', 'error_page.500',
+                               'error_page.403']
 
         if not dev_env:
             self._cp_config = dict([(key, self.error_production_handler)
@@ -146,6 +148,8 @@ class WokRoot(Root):
         self.domain = 'wok'
         self.messages = messages
         self.extends = None
+        self.failed_logins = []
+        self.fail_timeout = 30
 
         # set user log messages and make sure all parameters are present
         self.log_map = ROOT_REQUESTS
@@ -153,6 +157,12 @@ class WokRoot(Root):
 
     @cherrypy.expose
     def login(self, *args):
+        def _raise_timeout():
+            details = e = UnauthorizedError("WOKAUTH0004E",
+                                           {"seconds": self.fail_timeout})
+            log_request(code, params, details, method, 403)
+            raise cherrypy.HTTPError(403, e.message)
+            
         details = None
         method = 'POST'
         code = self.getRequestMessage(method, 'login')
@@ -166,10 +176,31 @@ class WokRoot(Root):
             log_request(code, params, details, method, 400)
             raise cherrypy.HTTPError(400, e.message)
 
+        # check for repetly
+        l = len(self.failed_logins)
+        if l >= 3:
+
+            # verify if timeout is still valid
+            last_try = self.failed_logins[l - 1]
+            if time.time() < (last_try["time"] + self.fail_timeout):
+                _raise_timeout()
         try:
             status = 200
             user_info = auth.login(username, password)
+
+            # user logged sucessfuly: reset counters
+            self.failed_logins = []
+            self.timeout = 30
         except cherrypy.HTTPError, e:
+            # store time and prevent too much tries
+            self.failed_logins.append({"user": username,
+                                       "time": time.time()})
+
+            # more than 3 fails: raise error
+            if len(self.failed_logins) > 3:
+                self.fail_timeout += (len(self.failed_logins) - 3) * 30
+                _raise_timeout()
+
             status = e.status
             raise
         finally:
diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js
index fa2a98a..ba9ac23 100644
--- a/ui/js/src/wok.login.js
+++ b/ui/js/src/wok.login.js
@@ -1,7 +1,7 @@
 /*
  * Project Wok
  *
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
  *
  * Code derived from Project Kimchi
  *
@@ -71,6 +71,7 @@ wok.login_main = function() {
         wok.login(settings, function(data) {
             var query = window.location.search;
             var next  = /.*next=(.*?)(&|$)/g.exec(query);
+
             if (next) {
                 var next_url = decodeURIComponent(next[1]);
             }
@@ -84,9 +85,17 @@ wok.login_main = function() {
             if (jqXHR.responseText == "") {
                 $("#messUserPass").hide();
                 $("#missServer").show();
+                $("#timeoutError").hide();
+            } else if ((jqXHR.responseJSON != undefined) &&
+                       ! (jqXHR.responseJSON["reason"] == undefined)) {
+                $("#messUserPass").hide();
+                $("#missServer").hide();
+                $("#timeoutError").html(jqXHR.responseJSON["reason"]);
+                $("#timeoutError").show();
             } else {
                 $("#missServer").hide();
                 $("#messUserPass").show();
+                $("#timeoutError").hide();
             }
             $("#messSession").hide();
             $("#logging").hide();
diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl
index f5a4b2d..d25910c 100644
--- a/ui/pages/login.html.tmpl
+++ b/ui/pages/login.html.tmpl
@@ -1,7 +1,7 @@
 #*
  * Project Wok
  *
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
  *
  * Code derived from Project Kimchi
  *
@@ -107,6 +107,7 @@
                     <div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div>
                     <div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div>
                     <div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div>
+                    <div id="timeoutError" class="alert alert-danger" style="display: none;">$_("Timeout error")</div>
                 </div>
                 <form id="form-login" class="form-horizontal" method="post">
                     <div class="form-group">
-- 
2.7.4



More information about the Kimchi-devel mailing list