@app.route('/login', methods=['GET', 'POST']) # Mantenemos el limitador general por si acaso (DDoS), pero más relajado @limiter.limit("30 per minute") def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] conn = get_db_connection() user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone() # 1. SI EL USUARIO NO EXISTE if user is None: conn.close() # ⚠️ Truco de seguridad: No digas "usuario no existe", di "datos incorrectos" # para que el hacker no sepa qué usuarios son reales. flash('Usuario o contraseña incorrectos', 'error') return render_template('login.html') # 2. VERIFICAR SI YA ESTÁ BANEADO if user['baneado'] == 1: conn.close() flash('🚫 ESTA CUENTA HA SIDO BLOQUEADA PERMANENTEMENTE POR SEGURIDAD.', 'error') return render_template('login.html') # 3. VERIFICAR CONTRASEÑA if check_password_hash(user['password_hash'], password): # ✅ ÉXITO: Reseteamos el contador a 0 y entramos conn.execute('UPDATE users SET intentos_fallidos = 0 WHERE id = ?', (user['id'],)) conn.commit() conn.close() user_obj = User(user['id'], user['username'], user['password_hash']) login_user(user_obj) return redirect(url_for('index')) else: # ❌ FALLO: Aumentamos el contador nuevos_fallos = user['intentos_fallidos'] + 1 if nuevos_fallos >= 10: # ⛔ SE ACABARON LOS INTENTOS -> BANEO conn.execute('UPDATE users SET intentos_fallidos = ?, baneado = 1 WHERE id = ?', (nuevos_fallos, user['id'])) conn.commit() conn.close() flash('🚫 HAS SIDO BANEADO. Contacta al administrador.', 'error') else: # ⚠️ AVISO DE ADVERTENCIA conn.execute('UPDATE users SET intentos_fallidos = ? WHERE id = ?', (nuevos_fallos, user['id'])) conn.commit() conn.close() intentos_restantes = 10 - nuevos_fallos flash(f'Contraseña incorrecta. Te quedan {intentos_restantes} intentos antes del bloqueo.', 'error') return render_template('login.html') return render_template('login.html')