fix(api-keys): gültige Schlüssel nie durch die Fehlversuchs-Sperre abweisen

Befunde aus dem Sicherheitsreview des Branches:

- MEDIUM: Die Sperre lief vor der Schlüsselsuche und zählte auch falsch
  aufgebaute Tokens. Wer die gemeinsame Adresse (Cluster-Ausgang, Proxy)
  nutzt oder per X-Forwarded-For vortäuscht, hätte ERPNext und den Shop
  15 Minuten aussperren können. Jetzt: erst Suche über den Hash; ein
  gültiger, nicht widerrufener, nicht abgelaufener Schlüssel kommt immer
  durch; 429 nur für unbekannte, widerrufene und abgelaufene Schlüssel;
  falsch aufgebaute Tokens sofort 401 ohne Zählung. Beim Start warnt
  ApiCore, wenn TRUST_PROXY in Produktion fehlt (im Cluster gesetzt).
- LOW: Bei vollem Speicher verdrängt der Limiter zuerst nicht gesperrte
  Einträge, gesperrte erst zuletzt.
- LOW: Bewegungs-Cursor mit Jahr außerhalb 2000-2100 ergab 500, jetzt 400.
- LOW/INFO: Vertrag legt fest, dass ERPNext Produkte und Chargen bei jedem
  Lauf vollständig neu liest (Voll-Spiegel) und Bewegungen inkrementell;
  remainingQuantity ist live, Bewegungen kommen 5 Minuten verzögert.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-12 12:00:03 +02:00
1 parent e28cc8e0f3
commit fe7b61c3c1
11 files changed
+300 -81

No files matched your search

+1 -1
View File
@@ -143,7 +143,7 @@ Grundlage: OWASP API Security Top 10 (2023) und OWASP ASVS Level 2. Umsetzung un
| Sitzungen | Refresh-Rotation atomar, Diebstahlerkennung, max. 7 Tage; Einmal-Tokens für SSO-Abschluss und MFA-Schritt |
| Step-up | Backup-Import, MFA ein/aus verlangen aktuelles Passwort (+ TOTP) |
| Autorisierung | fail-closed `PermissionsGuard`; keine Rechteausweitung über Benutzer-/Rollenpflege; Suche nur über lesbare Objektarten |
| Maschinen-Zugriff | API-Schlüssel nur als Hash (Suche über den Hash, kein Vergleich von Geheimnissen), Scope-Allowlist (`shop-sync:read`, `shop-sync:write`, `erp-inventory:read`), IP-Allowlist, widerrufbar, befristbar; Ersteller kann nur eigene Rechte vergeben; nach 20 abgelehnten Schlüsseln in 15 min je IP (IPv6: je /64) `429`; Logs nur mit Schlüssel-Präfix |
| Maschinen-Zugriff | API-Schlüssel nur als Hash (Suche über den Hash, kein Vergleich von Geheimnissen), Scope-Allowlist (`shop-sync:read`, `shop-sync:write`, `erp-inventory:read`), IP-Allowlist, widerrufbar, befristbar; Ersteller kann nur eigene Rechte vergeben; nach 20 ungültigen Schlüsseln in 15 min je IP (IPv6: je /64) `429` für weitere ungültige – gültige Schlüssel nie, falsch aufgebaute Tokens zählen nicht; Warnung beim Start, wenn `TRUST_PROXY` in Produktion fehlt; Logs nur mit Schlüssel-Präfix |
| Ausgehende Aufrufe | Webhooks: Ziel-IP beim Verbindungsaufbau geprüft (kein DNS-Rebinding), nur Port 80/443, https in Produktion, 5 s Timeout, Signatur über Zeitstempel+ID+Body |
| Transport/Antworten | `Cache-Control: no-store` (außer öffentliche Chargen-Seite), CSP `default-src 'none'`, `frame-ancestors 'none'`, keine ETags; Produktion startet nur mit https-`FRONTEND_ORIGIN` und sicheren Cookies |
| Rate-Limits | Throttler vor der Authentifizierung (100/min, Login/MFA/Refresh/Logout 10/min, öffentliche Chargen 30/min); Client-IP über `TRUST_PROXY` |