Rate limit failed web logins to 5 in 5 minutes

This commit is contained in:
Taylor Helsper 2020-05-17 17:01:53 -05:00
parent d107e86b20
commit 9c856a3aa8
2 changed files with 40 additions and 2 deletions

View File

@ -696,7 +696,7 @@ def page_login():
if login(pw): if login(pw):
return redirect("/") return redirect("/")
else: else:
flash("Invalid Password", category="error") flash(get_login_error_message(), category="error")
return redirect("/login") return redirect("/login")
@app.route("/logout") @app.route("/logout")

View File

@ -1,9 +1,15 @@
from flask import Blueprint, render_template, session, abort, Markup, request, redirect, send_from_directory, url_for, flash from flask import Blueprint, render_template, session, abort, Markup, request, redirect, send_from_directory, url_for, flash
from config import * from config import *
import time
import pam import pam
import os import os
import subprocess import subprocess
# Globals
recent_invalid_login_attempt = 0
recent_invalid_login_time_marker = 0
login_error_message = ""
# Exceptions # Exceptions
class LoginError(Exception): class LoginError(Exception):
"""Raised when a user is not logged in""" """Raised when a user is not logged in"""
@ -22,8 +28,16 @@ def is_logged_in():
return False return False
def login(password): def login(password):
global login_error_message
if get_recent_invalid_login_attempts() >= 5:
login_error_message = "Too Many Invalid Attempts - Wait 5 minutes"
return False
p = pam.pam() p = pam.pam()
if password == None or p.authenticate("admin", password) == False: if password == None or p.authenticate("admin", password) == False:
login_error_message = "Invalid Password"
increase_recent_invalid_login_attempts()
return False return False
else: else:
session["logged_in"] = True session["logged_in"] = True
@ -35,3 +49,27 @@ def logout():
def handle_login_exception(e): def handle_login_exception(e):
return redirect("/login") return redirect("/login")
def get_recent_invalid_login_attempts():
global recent_invalid_login_attempts
global recent_invalid_login_time_marker
# Reset count if it's been 5 minutes since first bad login
if time.time() > recent_invalid_login_time_marker + 300:
recent_invalid_login_attempts = 0
return recent_invalid_login_attempts
def increase_recent_invalid_login_attempts():
global recent_invalid_login_attempts
global recent_invalid_login_time_marker
# Mark time of first invalid login so we know when we would need to reset
if recent_invalid_login_attempts == 0:
recent_invalid_login_time_marker = time.time()
recent_invalid_login_attempts += 1
def get_login_error_message():
global login_error_message
return login_error_message