diff options
| author | 0livd <github@destras.fr> | 2017-08-20 12:37:12 +0200 |
|---|---|---|
| committer | Alexis Metaireau <alexis@notmyidea.org> | 2017-08-20 12:37:12 +0200 |
| commit | ec4a099f182629d86a7421af7d4899a655be684e (patch) | |
| tree | f5b72f89a3fca31f5a74b393a508d0153344a0fc /ihatemoney/web.py | |
| parent | 68e411473540c136dfdb269af888ceddbd0d403b (diff) | |
| download | ihatemoney-mirror-ec4a099f182629d86a7421af7d4899a655be684e.zip ihatemoney-mirror-ec4a099f182629d86a7421af7d4899a655be684e.tar.gz ihatemoney-mirror-ec4a099f182629d86a7421af7d4899a655be684e.tar.bz2 | |
Protect admin endpoints against brute force attacks (#249)
* Protect admin endpoints against brute force attacks
Add a throttling mechanism to prevent a client brute
forcing the authentication form, based on its ip address
Closes #245
* Reset attempt counters if they get memory hungry
Diffstat (limited to 'ihatemoney/web.py')
| -rw-r--r-- | ihatemoney/web.py | 22 |
1 files changed, 17 insertions, 5 deletions
diff --git a/ihatemoney/web.py b/ihatemoney/web.py index 65c0ed6..cc2eeac 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -27,10 +27,12 @@ from ihatemoney.forms import ( InviteForm, MemberForm, PasswordReminder, ProjectForm, get_billform_for, ExportForm ) -from ihatemoney.utils import Redirect303, list_of_dicts2json, list_of_dicts2csv +from ihatemoney.utils import Redirect303, list_of_dicts2json, list_of_dicts2csv, LoginThrottler main = Blueprint("main", __name__) +login_throttler = LoginThrottler(max_attempts=3, delay=1) + def requires_admin(f): """Require admin permissions for @requires_admin decorated endpoints. @@ -89,14 +91,24 @@ def admin(): form = AdminAuthenticationForm() goto = request.args.get('goto', url_for('.home')) if request.method == "POST": + client_ip = request.remote_addr + if not login_throttler.is_login_allowed(client_ip): + msg = _("Too many failed login attempts, please retry later.") + form.errors['admin_password'] = [msg] + return render_template("authenticate.html", form=form, admin_auth=True) if form.validate(): - if check_password_hash(current_app.config['ADMIN_PASSWORD'], form.admin_password.data): + # Valid password + if (check_password_hash(current_app.config['ADMIN_PASSWORD'], + form.admin_password.data)): session['is_admin'] = True session.update() + login_throttler.reset(client_ip) return redirect(goto) - else: - msg = _("This admin password is not the right one") - form.errors['admin_password'] = [msg] + # Invalid password + login_throttler.increment_attempts_counter(client_ip) + msg = _("This admin password is not the right one. Only %(num)d attempts left.", + num=login_throttler.get_remaining_attempts(client_ip)) + form.errors['admin_password'] = [msg] return render_template("authenticate.html", form=form, admin_auth=True) |
