aboutsummaryrefslogtreecommitdiff
path: root/ihatemoney/web.py
diff options
context:
space:
mode:
author0livd <github@destras.fr>2017-08-20 12:37:12 +0200
committerAlexis Metaireau <alexis@notmyidea.org>2017-08-20 12:37:12 +0200
commitec4a099f182629d86a7421af7d4899a655be684e (patch)
treef5b72f89a3fca31f5a74b393a508d0153344a0fc /ihatemoney/web.py
parent68e411473540c136dfdb269af888ceddbd0d403b (diff)
downloadihatemoney-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.py22
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)