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

Ramon Medeiros ramonn at linux.vnet.ibm.com
Tue Jan 17 12:59:56 UTC 2017



On 01/17/2017 10:12 AM, Lucio Correia wrote:
> On 16/01/2017 17:29, Ramon Medeiros wrote:
>> 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>
>> ---
>> Changes:
>>
>> v2:
>> fix pep8 issues
>>
>>  src/wok/i18n.py          |  1 +
>>  src/wok/root.py          | 37 ++++++++++++++++++++++++++++++++++---
>>  ui/js/src/wok.login.js   | 11 ++++++++++-
>>  ui/pages/login.html.tmpl |  3 ++-
>>  4 files changed, 47 insertions(+), 5 deletions(-)
>>
>> diff --git a/src/wok/i18n.py b/src/wok/i18n.py
>> index e454e31..21cc4ea 100644
>> --- a/src/wok/i18n.py
>> +++ b/src/wok/i18n.py
>> @@ -41,6 +41,7 @@ messages = {
>>      "WOKAUTH0001E": _("Authentication failed for user 
>> '%(username)s'. [Error code: %(code)s]"),
>>      "WOKAUTH0002E": _("You are not authorized to access Wok. Please, 
>> login first."),
>>      "WOKAUTH0003E": _("Specify %(item)s to login into Wok."),
>> +    "WOKAUTH0004E": _("You have failed to login in too much 
>> attempts. Please, wait for %(seconds)s seconds to try again."),
>
> Suggestion: Too many login attempts failed. Wait %(seconds)s 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..65226fb 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 repestly
>> +        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()
>> +
>
> I was thinking it would be controlled by username i.e. 3 failed 
> attempts for the same username would block that username only.
>
you are right, Just forget it
>
>>              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">
>>
>
>

-- 

Ramon Nunes Medeiros
Kimchi Developer
Linux Technology Center Brazil
IBM Systems & Technology Group
Phone : +55 19 2132 7878
ramonn at br.ibm.com

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ovirt.org/pipermail/kimchi-devel/attachments/20170117/8800d072/attachment.html>


More information about the Kimchi-devel mailing list