Login Rate Limit Reactivation Limit Persistent Sessions
This commit is contained in:
parent
dc180a1e8a
commit
bcaf8ae706
|
|
@ -20,7 +20,26 @@ import asyncio
|
||||||
import discord
|
import discord
|
||||||
from discord.ui import Button, View
|
from discord.ui import Button, View
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
import time as _time
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
# ---- Simple login rate limiter (per IP, 0.5s min interval) ----
|
||||||
|
_login_last_attempt: dict = {} # {ip: timestamp}
|
||||||
|
_LOGIN_RATE_LIMIT_SECONDS = 0.5
|
||||||
|
|
||||||
|
def _check_login_rate_limit(ip: str) -> bool:
|
||||||
|
"""Returns True if the request is allowed, False if rate-limited."""
|
||||||
|
now = _time.time()
|
||||||
|
last = _login_last_attempt.get(ip, 0)
|
||||||
|
if now - last < _LOGIN_RATE_LIMIT_SECONDS:
|
||||||
|
return False
|
||||||
|
_login_last_attempt[ip] = now
|
||||||
|
# Cleanup old entries to prevent unbounded growth
|
||||||
|
if len(_login_last_attempt) > 10000:
|
||||||
|
cutoff = now - 60
|
||||||
|
for k in [k for k, v in list(_login_last_attempt.items()) if v < cutoff]:
|
||||||
|
_login_last_attempt.pop(k, None)
|
||||||
|
return True
|
||||||
async_mode = "eventlet"
|
async_mode = "eventlet"
|
||||||
app = Flask(__name__,
|
app = Flask(__name__,
|
||||||
template_folder='Frontend/HTML',
|
template_folder='Frontend/HTML',
|
||||||
|
|
@ -53,7 +72,7 @@ class SuppressServerLoadFilter(logging.Filter):
|
||||||
log = logging.getLogger('werkzeug')
|
log = logging.getLogger('werkzeug')
|
||||||
log.addFilter(SuppressServerLoadFilter())
|
log.addFilter(SuppressServerLoadFilter())
|
||||||
app.secret_key = os.environ.get('SECRET_KEY', os.urandom(32))
|
app.secret_key = os.environ.get('SECRET_KEY', os.urandom(32))
|
||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=8)
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=30)
|
||||||
app.config['SESSION_COOKIE_SECURE'] = True
|
app.config['SESSION_COOKIE_SECURE'] = True
|
||||||
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||||||
|
|
@ -658,6 +677,11 @@ def login():
|
||||||
if maintenance_status and maintenance_status.get('enabled', False):
|
if maintenance_status and maintenance_status.get('enabled', False):
|
||||||
return redirect(url_for('maintenance'))
|
return redirect(url_for('maintenance'))
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# Rate limit: max 1 attempt per 0.5s per IP
|
||||||
|
_client_ip = request.headers.get('X-Forwarded-For', request.remote_addr or '').split(',')[0].strip()
|
||||||
|
if not _check_login_rate_limit(_client_ip):
|
||||||
|
flash("Zu viele Anmeldeversuche. Bitte warte einen Moment.", "warning")
|
||||||
|
return render_template('login.html')
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue