Desktop: Passwortabfrage vor Konten, Geheimnissen und WebUI-Zugang
CI / Build & Tests (Linux) (push) Canceled after 0s
CI / Plattformneutralität & Hygiene (push) Canceled after 0s

Umsetzung von #567.

Entscheidung Richard, 13.09.2026: Geheimnisse verwalten wir ausschliesslich im
Desktop, und selbst dort erst nach zusaetzlicher Passwortabfrage.

Bisher sah und aenderte jeder, der die Desktop-Oberflaeche offen vor sich
hatte:
- Polymarket-Konten mit ApiKey, ApiSecret, ApiPassphrase und PrivateKey
- Master-Key, OpenRouter-Key, Watchdog-Token und Lizenzschluessel
- Mullvad-Account und Polygon-RPC-URL mit Alchemy-Key, offen im Editor
- das WebUI-Token, per "Anzeigen" lesbar

Jetzt gesperrt, bis entsperrt:
- Tab "Polymarket Accounts": Gesperrt stehen die Konten gar nicht im
  visuellen Baum. Das Raster bleibt leer, der Editor mit den Private Keys
  ebenfalls; nichts ist nur verdeckt.
- Master-Key erzeugen, OpenRouter-, Watchdog- und Lizenzschluessel setzen.
- Mullvad-Account und Polygon-RPC-URL: mit [SensitiveSetting] markiert und im
  Editor ausgeblendet.
- WebUI-Zugang: Gesperrt steht das Token nicht einmal maskiert im Feld.
  Speichern ist mitgesperrt, weil es den Feldinhalt als Token schreibt;
  gesperrt und leer haette es das Token geloescht.
- Frei bleiben Lizenzpruefung und Test-Heartbeat, weil sie keinen Schluessel
  zeigen.

Kern im Core (PolyTrader.Core.Security), weil nur dort testbar:
- SettingsPasswordStore: PBKDF2-HMAC-SHA256, 600 000 Iterationen, Salt, eigene
  Datei settings-password.json neben master.key, atomar geschrieben und nur fuer
  den Besitzer lesbar. Nicht in server_settings.xml, die seit #561 das WebUI
  mitschreibt. Ein vorhandenes Passwort wird nie ueberschrieben.
- SettingsLock:
  - erstes Festlegen mit Wiederholung und mindestens 10 Zeichen
  - zwei freie Fehlversuche, danach 5 s bis 5 min Wartezeit, in der auch das
    richtige Passwort nicht geprueft wird
  - Sperre nach 5 Minuten ohne geschuetzte Aktion und beim Verlassen der
    Einstellungen
  - eine unlesbare Passwortdatei entsperrt nie und gilt auch nicht still als
    "kein Passwort"
  - Protokoll ohne eingegebene Werte

Oberflaeche: PasswordDialog deklarativ in .axaml, Sperr-Leiste ueber den Tabs.
Die PBKDF2-Pruefung dauert etwa eine halbe Sekunde und laeuft nicht im
UI-Thread.

Grenzen:
- Die Sperre schuetzt gegen jemanden an einer entsperrten Desktop-Sitzung. Wer
  Dateizugriff hat, liest master.key selbst. Deshalb gibt es kein "Passwort
  vergessen": Wer settings-password.json loeschen kann, braucht keins.
- Die Wartezeit nach Fehlversuchen beginnt nach einem Neustart von vorn.
- "Datenbankeinstellungen": Eine Ansicht dafuer gibt es im Desktop nicht; die
  Verbindungszeichenfolge steht nur in appsettings.Local.json. Entsteht eine,
  gehoert sie hinter dieselbe Sperre.

Tests: 20 fuer Passwortdatei und Sperre (Festlegen, Fehlversuche, Wartezeit,
Leerlauf, unlesbare Datei, Aendern, Iterationszahl aus der Datei, Dateirechte)
und 5 fuer die Markierung der Geheimnisfelder. Die Oberflaeche selbst wird auf
der VM abgenommen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-13 14:34:32 +02:00
co-authored by Claude Opus 5
parent 1a09d99a63
commit 86b497bdbf
14 changed files with 913 additions and 24 deletions
@@ -14,6 +14,15 @@
<TextBlock Name="lblStatus" Text="Bereit." />
</Border>
<!-- #567: Sperrzustand der geschuetzten Einstellungen (Konten, Geheimnisse, WebUI-Zugang). -->
<Border Classes="toolbar" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Name="btnLockToggle" Content="🔒 Entsperren" />
<TextBlock Name="lblLockState" VerticalAlignment="Center" Classes="muted"
Text="Konten, Geheimnisse und WebUI-Zugang sind gesperrt." />
</StackPanel>
</Border>
<TabControl>
<TabItem Header="Allgemeine Einstellungen">
@@ -44,7 +53,18 @@
</TabItem>
<TabItem Header="Polymarket Accounts">
<DockPanel>
<Panel>
<!-- #567: Gesperrt stehen die Konten gar nicht im visuellen Baum - das Raster bleibt leer,
nicht nur verdeckt. -->
<Border Name="pnlAccountsLocked" Padding="32" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Spacing="12" MaxWidth="480">
<TextBlock Text="🔒 Polymarket-Konten sind gesperrt" FontSize="16" FontWeight="SemiBold" />
<TextBlock TextWrapping="Wrap" Classes="muted"
Text="Die Konten enthalten Private Keys und API-Schlüssel – also Zugriff auf Geld. Sie sind erst nach Eingabe des Einstellungs-Passworts sichtbar und sperren sich nach 5 Minuten ohne geschützte Aktion wieder." />
<Button Name="btnAccUnlock" Content="Entsperren …" HorizontalAlignment="Left" Classes="primary" />
</StackPanel>
</Border>
<DockPanel Name="pnlAccounts" IsVisible="False">
<Border Classes="toolbar" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal">
<Button Name="btnAccNew" Content="Neuer Account" />
@@ -68,6 +88,7 @@
<controls:SettingsEditor Name="editorAccount" Grid.Column="2" />
</Grid>
</DockPanel>
</Panel>
</TabItem>
<TabItem Header="WebUI">