import random import logging from datetime import datetime, timedelta from zoneinfo import ZoneInfo from flask import request from db import execute_query, get_db_connection, get_db_cursor from config import FAIL_MESSAGES, MAX_LOGIN_ATTEMPTS, BLOCK_DURATION_HOURS, EMAIL_ENABLED from utils.audit import log_audit from utils.network import get_client_ip logger = logging.getLogger(__name__) def check_ip_blocked(ip_address): """Check if an IP address is blocked""" result = execute_query( """ SELECT blocked_until FROM login_attempts WHERE ip_address = %s AND blocked_until IS NOT NULL """, (ip_address,), fetch_one=True ) if result and result[0]: if result[0] > datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None): return True, result[0] return False, None def record_failed_attempt(ip_address, username): """Record a failed login attempt""" # Random error message error_message = random.choice(FAIL_MESSAGES) with get_db_connection() as conn: with get_db_cursor(conn) as cur: try: # Check if IP already exists cur.execute(""" SELECT attempt_count FROM login_attempts WHERE ip_address = %s """, (ip_address,)) result = cur.fetchone() if result: # Update existing entry new_count = result[0] + 1 blocked_until = None if new_count >= MAX_LOGIN_ATTEMPTS: blocked_until = datetime.now(ZoneInfo("Europe/Berlin")).replace(tzinfo=None) + timedelta(hours=BLOCK_DURATION_HOURS) # Email notification (if enabled) if EMAIL_ENABLED: send_security_alert_email(ip_address, username, new_count) cur.execute(""" UPDATE login_attempts SET attempt_count = %s, last_attempt = CURRENT_TIMESTAMP, blocked_until = %s, last_username_tried = %s, last_error_message = %s WHERE ip_address = %s """, (new_count, blocked_until, username, error_message, ip_address)) else: # Create new entry cur.execute(""" INSERT INTO login_attempts (ip_address, attempt_count, last_username_tried, last_error_message) VALUES (%s, 1, %s, %s) """, (ip_address, username, error_message)) conn.commit() # Audit log log_audit('LOGIN_FAILED', 'user', additional_info=f"IP: {ip_address}, User: {username}, Message: {error_message}") except Exception as e: logger.error(f"Rate limiting error: {e}") conn.rollback() return error_message def reset_login_attempts(ip_address): """Reset login attempts for an IP""" execute_query( "DELETE FROM login_attempts WHERE ip_address = %s", (ip_address,) ) def get_login_attempts(ip_address): """Get the number of login attempts for an IP""" result = execute_query( "SELECT attempt_count FROM login_attempts WHERE ip_address = %s", (ip_address,), fetch_one=True ) return result[0] if result else 0 def send_security_alert_email(ip_address, username, attempt_count): """Send a security alert email""" subject = f"⚠️ SICHERHEITSWARNUNG: {attempt_count} fehlgeschlagene Login-Versuche" body = f""" WARNUNG: Mehrere fehlgeschlagene Login-Versuche erkannt! IP-Adresse: {ip_address} Versuchter Benutzername: {username} Anzahl Versuche: {attempt_count} Zeit: {datetime.now(ZoneInfo("Europe/Berlin")).strftime('%Y-%m-%d %H:%M:%S')} Die IP-Adresse wurde für 24 Stunden gesperrt. Dies ist eine automatische Nachricht vom v2-Docker Admin Panel. """ # TODO: Email sending implementation when SMTP is configured logger.warning(f"Sicherheitswarnung: {attempt_count} fehlgeschlagene Versuche von IP {ip_address}") print(f"E-Mail würde gesendet: {subject}")