Konsolidierte Funde aus postgres-/sql-/jwt-/owasp-top10-expert-Review: - Race Conditions gefixt: doppelte aktive Kontrolle (SAVEPOINT + partieller Unique-Index), doppelte Mindermengen-Genehmigung (FOR UPDATE + Unique-Index), Lost-Update bei Nachfüllung (FOR UPDATE auf Fehlbestand/Objektposition). - Migration 0007: partielle Unique-Indizes als DB-Sicherheitsnetz + fehlende FK-Indizes (fehlbestand.material_id, kontrolle(objekt_id,status), zustaendigkeit, benutzer_rolle.rolle, objektposition.ablaufdatum u.a.). - Connection-Pool explizit begrenzt (pool_size=5, max_overflow=5) - ohne das könnte jeder uvicorn-Worker den Postgres max_connections-Wert sprengen. - Timing-Angriff bei Login-Enumeration gefixt (konstante Antwortzeit über Dummy-Hash), JWT-Decode verlangt jetzt exp/sub-Claims. - App-seitiges Rate-Limiting (slowapi, 5/min) auf /auth/login als Verteidigung in der Tiefe zusätzlich zum nginx-Limit. - nginx: TLS mit selbstsigniertem Zertifikat (LAN-Betrieb, keine Domain), HSTS, Content-Security-Policy, Permissions-Policy ergänzt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
64 lines
2.7 KiB
Plaintext
64 lines
2.7 KiB
Plaintext
# limit_req_zone gehört in den http-Kontext (hier: via sites-enabled-Include-Kette
|
|
# bereits innerhalb von http{}, aber außerhalb des server{}-Blocks nötig).
|
|
# Grober Schutz gegen Brute-Force auf Login, ergänzt (nicht ersetzt) die
|
|
# App-seitige Rate-Begrenzung (slowapi, siehe backend/app/core/rate_limit.py).
|
|
limit_req_zone $binary_remote_addr zone=mabea_login:10m rate=5r/m;
|
|
|
|
# HTTP -> HTTPS Redirect. Selbstsigniertes Zertifikat (Karte owasp-Review: LAN-
|
|
# Betrieb ohne öffentliche Domain, kein Let's Encrypt möglich) - Browser zeigen
|
|
# beim ersten Zugriff eine Warnung, bis das Zertifikat manuell akzeptiert wird.
|
|
server {
|
|
listen 80;
|
|
server_name __APP_DOMAIN__;
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
server_name __APP_DOMAIN__;
|
|
|
|
ssl_certificate /etc/nginx/ssl/mabea.crt;
|
|
ssl_certificate_key /etc/nginx/ssl/mabea.key;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
ssl_prefer_server_ciphers on;
|
|
|
|
# Basis-Hardening-Header (Review-Fund owasp-top10-expert).
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header X-Content-Type-Options nosniff always;
|
|
add_header X-Frame-Options DENY always;
|
|
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
|
# CSP erlaubt nur same-origin (PWA lädt keine externen Skripte/Fonts) plus
|
|
# Kamera-Zugriff für den Barcode-Scanner (Karte 10). connect-src 'self'
|
|
# deckt auch API-Aufrufe ab, da Frontend/API dieselbe Origin teilen (siehe
|
|
# location-Blöcke unten). Owasp-Review-Fund: CSP fehlte komplett.
|
|
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self'; frame-ancestors 'none'" always;
|
|
add_header Permissions-Policy "camera=(self)" always;
|
|
|
|
location /api/v1/auth/login {
|
|
limit_req zone=mabea_login burst=5 nodelay;
|
|
proxy_pass http://127.0.0.1:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
|
|
# React-PWA (Sprint 7): statische Build-Ausgabe von frontend/dist.
|
|
# try_files statt 404 bei Client-Routen, da react-router-dom im Browser routet.
|
|
root /var/www/mabea;
|
|
index index.html;
|
|
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
}
|