diff --git a/AstraOS/astra_os.py b/AstraOS/astra_os.py index 6827ad5..daecdcf 100644 --- a/AstraOS/astra_os.py +++ b/AstraOS/astra_os.py @@ -20,7 +20,26 @@ import asyncio import discord from discord.ui import Button, View from dotenv import load_dotenv +import time as _time 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" app = Flask(__name__, template_folder='Frontend/HTML', @@ -53,7 +72,7 @@ class SuppressServerLoadFilter(logging.Filter): log = logging.getLogger('werkzeug') log.addFilter(SuppressServerLoadFilter()) 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_HTTPONLY'] = True app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' @@ -658,6 +677,11 @@ def login(): if maintenance_status and maintenance_status.get('enabled', False): return redirect(url_for('maintenance')) 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') password = request.form.get('password') if not username or not password: