Session 2026-04-23: Security, Content-Schutz, Wiki-Temperament-Migration

Security (9 Fixes):
- JWT_SECRET Pflicht-Check beim Start (Production)
- Rate-Limit: Login (10/5min), Register (5/h), KI-Training (10/h), Giftköder (3/h)
- KI-Training-Endpoint: Auth-Pflicht hinzugefügt
- Private Profile aus Freunde-Suche gefiltert
- OG-Tags XSS mit html.escape() gesichert
- Globales File-Upload-Limit 20 MB (Middleware)
- E-Mail-Maskierung für Moderatoren im Admin-Panel
- IP-Blocklist in ratelimit.py

Content-Schutz (4 Schichten):
- robots.txt: /api/ komplett Disallow, SSR-Seiten Allow
- Rate-Limit auf /api/wiki/rassen (60/min) + Detail (30/min)
- Honeypot /api/wiki/trap + unsichtbarer Link in index.html
- Wasserzeichen in KI-Enricher-Prompt

Wiki Temperament-Migration:
- 60-Wort Übersetzungsmap EN→DE
- Datenmüll-Filter (hunderasse, dog breed etc.)
- translate_existing_temperaments() + Admin-Button
- SW by-v318, APP_VER 306
This commit is contained in:
rene 2026-04-23 18:34:05 +02:00
parent 0f5f1c4c30
commit 15f854d96c
15 changed files with 284 additions and 53 deletions

View file

@ -5,7 +5,7 @@ import secrets
import string
from typing import Optional
from fastapi import APIRouter, HTTPException, Response, Depends
from fastapi import APIRouter, HTTPException, Request, Response, Depends
from pydantic import BaseModel, EmailStr
from database import db
from auth import (
@ -13,6 +13,7 @@ from auth import (
get_current_user
)
from username_blocklist import is_username_blocked
from ratelimit import check as rl_check
router = APIRouter()
COOKIE_NAME = "by_token"
@ -43,7 +44,8 @@ def _set_cookie(response: Response, token: str):
@router.post("/register")
async def register(data: RegisterRequest, response: Response):
async def register(data: RegisterRequest, response: Response, request: Request):
rl_check(request, max_requests=5, window_seconds=3600, key="register")
name = data.name.strip()
if len(name) < 2:
raise HTTPException(400, "Benutzername muss mindestens 2 Zeichen lang sein.")
@ -90,7 +92,8 @@ async def register(data: RegisterRequest, response: Response):
@router.post("/login")
async def login(data: LoginRequest, response: Response):
async def login(data: LoginRequest, response: Response, request: Request):
rl_check(request, max_requests=10, window_seconds=300, key="login")
with db() as conn:
user = conn.execute(
"SELECT id, pw_hash, name, rolle, is_premium FROM users WHERE email=?",