CAPTCHA (Noch kein Key)
Dieser Commit ist enthalten in:
97
JOURNAL.md
97
JOURNAL.md
@@ -801,4 +801,99 @@ Die Session-Daten werden erst gefüllt, wenn der License Server API implementier
|
|||||||
**Status:**
|
**Status:**
|
||||||
- ✅ Session-Timeout-Mechanismus vollständig implementiert
|
- ✅ Session-Timeout-Mechanismus vollständig implementiert
|
||||||
- ✅ Debug-Logging für Session-Überwachung aktiv
|
- ✅ Debug-Logging für Session-Überwachung aktiv
|
||||||
- ✅ Cookie-Sicherheitseinstellungen optimiert
|
- ✅ Cookie-Sicherheitseinstellungen optimiert
|
||||||
|
|
||||||
|
### 2025-06-07 - CAPTCHA Backend-Validierung implementiert
|
||||||
|
- Google reCAPTCHA v2 Backend-Verifizierung hinzugefügt
|
||||||
|
|
||||||
|
**Implementierte Features:**
|
||||||
|
1. **verify_recaptcha() Funktion (app.py):**
|
||||||
|
- Validiert CAPTCHA-Response mit Google API
|
||||||
|
- Fallback: Wenn RECAPTCHA_SECRET_KEY nicht konfiguriert, wird CAPTCHA übersprungen (für PoC)
|
||||||
|
- Timeout von 5 Sekunden für API-Request
|
||||||
|
- Error-Handling für Netzwerkfehler
|
||||||
|
- Logging für Debugging und Fehleranalyse
|
||||||
|
|
||||||
|
2. **Login-Route Erweiterungen:**
|
||||||
|
- CAPTCHA wird nach 2 Fehlversuchen angezeigt
|
||||||
|
- Prüfung ob CAPTCHA-Response vorhanden
|
||||||
|
- Validierung der CAPTCHA-Response gegen Google API
|
||||||
|
- Unterschiedliche Fehlermeldungen für fehlende/ungültige CAPTCHA
|
||||||
|
- Site Key wird aus Environment-Variable an Template übergeben
|
||||||
|
|
||||||
|
3. **Environment-Konfiguration (.env):**
|
||||||
|
- RECAPTCHA_SITE_KEY (für Frontend)
|
||||||
|
- RECAPTCHA_SECRET_KEY (für Backend-Validierung)
|
||||||
|
- Beide auskommentiert für PoC-Phase
|
||||||
|
|
||||||
|
4. **Dependencies:**
|
||||||
|
- requests Library zu requirements.txt hinzugefügt
|
||||||
|
|
||||||
|
**Sicherheitsaspekte:**
|
||||||
|
- CAPTCHA verhindert automatisierte Brute-Force-Angriffe
|
||||||
|
- Timing-Attack-Schutz bleibt auch bei CAPTCHA-Prüfung aktiv
|
||||||
|
- Bei Netzwerkfehlern wird CAPTCHA als bestanden gewertet (Verfügbarkeit vor Sicherheit)
|
||||||
|
- Secret Key wird niemals im Frontend exponiert
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
1. Google reCAPTCHA v2 Keys erstellen: https://www.google.com/recaptcha/admin
|
||||||
|
2. Keys in .env eintragen:
|
||||||
|
```
|
||||||
|
RECAPTCHA_SITE_KEY=your-site-key
|
||||||
|
RECAPTCHA_SECRET_KEY=your-secret-key
|
||||||
|
```
|
||||||
|
3. Container neu starten
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- ✅ CAPTCHA-Frontend bereits vorhanden (login.html)
|
||||||
|
- ✅ Backend-Validierung vollständig implementiert
|
||||||
|
- ✅ Fallback für PoC-Betrieb ohne Google-Keys
|
||||||
|
- ✅ Integration in Rate-Limiting-System
|
||||||
|
- ⚠️ CAPTCHA-Keys noch nicht konfiguriert (für PoC deaktiviert)
|
||||||
|
|
||||||
|
**Anleitung für Google reCAPTCHA Keys:**
|
||||||
|
|
||||||
|
1. **Registrierung bei Google reCAPTCHA:**
|
||||||
|
- Gehe zu: https://www.google.com/recaptcha/admin/create
|
||||||
|
- Melde dich mit Google-Konto an
|
||||||
|
- Label eingeben: "v2-Docker Admin Panel"
|
||||||
|
- Typ wählen: "reCAPTCHA v2" → "Ich bin kein Roboter"-Kästchen
|
||||||
|
- Domains hinzufügen:
|
||||||
|
```
|
||||||
|
admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com
|
||||||
|
localhost
|
||||||
|
```
|
||||||
|
- Nutzungsbedingungen akzeptieren
|
||||||
|
- Senden klicken
|
||||||
|
|
||||||
|
2. **Keys erhalten:**
|
||||||
|
- Site Key (öffentlich für Frontend)
|
||||||
|
- Secret Key (geheim für Backend-Validierung)
|
||||||
|
|
||||||
|
3. **Keys in .env eintragen:**
|
||||||
|
```bash
|
||||||
|
RECAPTCHA_SITE_KEY=6Ld...
|
||||||
|
RECAPTCHA_SECRET_KEY=6Ld...
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Container neu starten:**
|
||||||
|
```bash
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**Kosten:**
|
||||||
|
- Kostenlos bis 1 Million Anfragen pro Monat
|
||||||
|
- Danach: $1.00 pro 1000 zusätzliche Anfragen
|
||||||
|
- Für dieses Projekt reicht die kostenlose Version vollkommen aus
|
||||||
|
|
||||||
|
**Test-Keys für Entwicklung:**
|
||||||
|
- Site Key: `6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI`
|
||||||
|
- Secret Key: `6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe`
|
||||||
|
- ⚠️ Diese Keys nur für lokale Tests verwenden, niemals produktiv!
|
||||||
|
|
||||||
|
**Aktueller Status:**
|
||||||
|
- Code ist vollständig implementiert und getestet
|
||||||
|
- CAPTCHA wird nach 2 Fehlversuchen angezeigt
|
||||||
|
- Ohne konfigurierte Keys wird CAPTCHA-Prüfung übersprungen
|
||||||
|
- Für Produktion müssen nur die Keys in .env eingetragen werden
|
||||||
7
v2/.env
7
v2/.env
@@ -47,3 +47,10 @@ EMAIL_ENABLED=false
|
|||||||
|
|
||||||
# Backup-Verschlüsselung (optional, wird automatisch generiert wenn leer)
|
# Backup-Verschlüsselung (optional, wird automatisch generiert wenn leer)
|
||||||
# BACKUP_ENCRYPTION_KEY=
|
# BACKUP_ENCRYPTION_KEY=
|
||||||
|
|
||||||
|
# ===================== CAPTCHA KONFIGURATION =====================
|
||||||
|
|
||||||
|
# Google reCAPTCHA v2 Keys (https://www.google.com/recaptcha/admin)
|
||||||
|
# Für PoC-Phase auskommentiert - CAPTCHA wird übersprungen wenn Keys fehlen
|
||||||
|
# RECAPTCHA_SITE_KEY=your-site-key-here
|
||||||
|
# RECAPTCHA_SECRET_KEY=your-secret-key-here
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from apscheduler.schedulers.background import BackgroundScheduler
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import requests
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@@ -512,6 +513,41 @@ def send_security_alert_email(ip_address, username, attempt_count):
|
|||||||
logging.warning(f"Sicherheitswarnung: {attempt_count} fehlgeschlagene Versuche von IP {ip_address}")
|
logging.warning(f"Sicherheitswarnung: {attempt_count} fehlgeschlagene Versuche von IP {ip_address}")
|
||||||
print(f"E-Mail würde gesendet: {subject}")
|
print(f"E-Mail würde gesendet: {subject}")
|
||||||
|
|
||||||
|
def verify_recaptcha(response):
|
||||||
|
"""Verifiziert die reCAPTCHA v2 Response mit Google"""
|
||||||
|
secret_key = os.getenv('RECAPTCHA_SECRET_KEY')
|
||||||
|
|
||||||
|
# Wenn kein Secret Key konfiguriert ist, CAPTCHA als bestanden werten (für PoC)
|
||||||
|
if not secret_key:
|
||||||
|
logging.warning("RECAPTCHA_SECRET_KEY nicht konfiguriert - CAPTCHA wird übersprungen")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Verifizierung bei Google
|
||||||
|
try:
|
||||||
|
verify_url = 'https://www.google.com/recaptcha/api/siteverify'
|
||||||
|
data = {
|
||||||
|
'secret': secret_key,
|
||||||
|
'response': response
|
||||||
|
}
|
||||||
|
|
||||||
|
# Timeout für Request setzen
|
||||||
|
r = requests.post(verify_url, data=data, timeout=5)
|
||||||
|
result = r.json()
|
||||||
|
|
||||||
|
# Log für Debugging
|
||||||
|
if not result.get('success'):
|
||||||
|
logging.warning(f"reCAPTCHA Validierung fehlgeschlagen: {result.get('error-codes', [])}")
|
||||||
|
|
||||||
|
return result.get('success', False)
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.error(f"reCAPTCHA Verifizierung fehlgeschlagen: {str(e)}")
|
||||||
|
# Bei Netzwerkfehlern CAPTCHA als bestanden werten
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Unerwarteter Fehler bei reCAPTCHA: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
@app.route("/login", methods=["GET", "POST"])
|
@app.route("/login", methods=["GET", "POST"])
|
||||||
def login():
|
def login():
|
||||||
# Timing-Attack Schutz - Start Zeit merken
|
# Timing-Attack Schutz - Start Zeit merken
|
||||||
@@ -545,7 +581,22 @@ def login():
|
|||||||
return render_template("login.html",
|
return render_template("login.html",
|
||||||
error="CAPTCHA ERFORDERLICH!",
|
error="CAPTCHA ERFORDERLICH!",
|
||||||
show_captcha=True,
|
show_captcha=True,
|
||||||
error_type="captcha")
|
error_type="captcha",
|
||||||
|
attempts_left=max(0, MAX_LOGIN_ATTEMPTS - attempt_count),
|
||||||
|
recaptcha_site_key=os.getenv('RECAPTCHA_SITE_KEY'))
|
||||||
|
|
||||||
|
# CAPTCHA validieren
|
||||||
|
if not verify_recaptcha(captcha_response):
|
||||||
|
# Timing-Attack Schutz
|
||||||
|
elapsed = time.time() - start_time
|
||||||
|
if elapsed < 1.0:
|
||||||
|
time.sleep(1.0 - elapsed)
|
||||||
|
return render_template("login.html",
|
||||||
|
error="CAPTCHA UNGÜLTIG! Bitte erneut versuchen.",
|
||||||
|
show_captcha=True,
|
||||||
|
error_type="captcha",
|
||||||
|
attempts_left=max(0, MAX_LOGIN_ATTEMPTS - attempt_count),
|
||||||
|
recaptcha_site_key=os.getenv('RECAPTCHA_SITE_KEY'))
|
||||||
|
|
||||||
# Check gegen beide Admin-Accounts aus .env
|
# Check gegen beide Admin-Accounts aus .env
|
||||||
admin1_user = os.getenv("ADMIN1_USERNAME")
|
admin1_user = os.getenv("ADMIN1_USERNAME")
|
||||||
@@ -589,12 +640,14 @@ def login():
|
|||||||
error=error_message,
|
error=error_message,
|
||||||
show_captcha=(new_attempt_count >= CAPTCHA_AFTER_ATTEMPTS),
|
show_captcha=(new_attempt_count >= CAPTCHA_AFTER_ATTEMPTS),
|
||||||
error_type="failed",
|
error_type="failed",
|
||||||
attempts_left=max(0, MAX_LOGIN_ATTEMPTS - new_attempt_count))
|
attempts_left=max(0, MAX_LOGIN_ATTEMPTS - new_attempt_count),
|
||||||
|
recaptcha_site_key=os.getenv('RECAPTCHA_SITE_KEY'))
|
||||||
|
|
||||||
# GET Request
|
# GET Request
|
||||||
return render_template("login.html",
|
return render_template("login.html",
|
||||||
show_captcha=(attempt_count >= CAPTCHA_AFTER_ATTEMPTS),
|
show_captcha=(attempt_count >= CAPTCHA_AFTER_ATTEMPTS),
|
||||||
attempts_left=max(0, MAX_LOGIN_ATTEMPTS - attempt_count))
|
attempts_left=max(0, MAX_LOGIN_ATTEMPTS - attempt_count),
|
||||||
|
recaptcha_site_key=os.getenv('RECAPTCHA_SITE_KEY'))
|
||||||
|
|
||||||
@app.route("/logout")
|
@app.route("/logout")
|
||||||
def logout():
|
def logout():
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ pandas
|
|||||||
openpyxl
|
openpyxl
|
||||||
cryptography
|
cryptography
|
||||||
apscheduler
|
apscheduler
|
||||||
|
requests
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren