Security F1: Wallet-Keys/API-Secrets at-rest verschluesselt (AES-256-GCM, portabler Master-Key)
Behebt den kritischsten Befund (Klartext-Private-Keys in remote-gehosteter MySQL): - SecretProtection (Core/Security): AES-256-GCM, authenticated. Master-Key AUSSERHALB der DB (env POLYTRADER_MASTER_KEY, sonst gitignorierte master.key). Format enc:v1:base64(nonce|tag|ct). Alt-Klartext (ohne Praefix) wird gelesen und beim Speichern verschluesselt (selbstheilend). Ohne Master-Key: Passthrough + deutliche Startwarnung (kein stiller Sicherheitsverlust). - EncryptedStringConverter (EF ValueConverter) auf core_accounts.PrivateKey/ApiSecret/ApiPassphrase; Spalten 256->512 verbreitert (Migration EncryptAccountSecretsWidenColumns, offline generiert). - Program.cs: Master-Key vor der Hydration laden; nach Start einmalige/idempotente Re-Encryption vorhandener Klartext-Credentials. Auch in --smoke-ui verdrahtet. - CoreDbContextFactory nutzt jetzt fixe Server-Version (offline-Migrationsgenerierung, kein DB-Zugriff). 13 neue Krypto-Tests (Round-Trip, Nonce-Frische, Manipulations-/Falscher-Key-Erkennung, Passthrough, Key-Formate). Build 0 Fehler, 324 Tests gruen, --smoke-ui ok (Warnung ohne Key wie erwartet). AKTIVIERUNG (im Zielland): POLYTRADER_MASTER_KEY setzen (zufaelliger 32-Byte-Base64-Key, SEPARAT sichern!) + Migration anwenden (dotnet ef database update --context CoreDbContext). Danach Alchemy-/Mullvad-Secrets aus F3 rotieren. WICHTIG: Master-Key-Verlust = Kein Zugriff auf die Keys mehr. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
32fda4e70f
commit
f3ed63cf9d
+58
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Channels;
|
||||
using PolyTrader.Core.Persistence;
|
||||
using PolyTrader.Core.Security;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -131,6 +134,10 @@ internal static class Program
|
||||
services.AddTransient<PolyTraderSharp.Ui.LauncherForm>();
|
||||
}).Build();
|
||||
|
||||
// F1 (Sicherheit): Master-Key VOR jeder Credential-Entschlüsselung (Hydration) laden.
|
||||
// Quelle: Umgebungsvariable POLYTRADER_MASTER_KEY, sonst gitignorierte master.key im App-Ordner.
|
||||
ConfigureSecretProtection(AppHost.Services.GetRequiredService<TerminalLogger>());
|
||||
|
||||
try
|
||||
{
|
||||
// Trade-Nummerierung fortsetzen: höchste bestehende TradeId serverseitig lesen
|
||||
@@ -150,6 +157,9 @@ internal static class Program
|
||||
|
||||
AppHost.Start();
|
||||
|
||||
// F1: einmalige, idempotente Verschlüsselung evtl. vorhandener Klartext-Credentials in der DB.
|
||||
ReencryptAccountCredentials(AppHost.Services);
|
||||
|
||||
// Shell-Views registrieren (Core-App-Views; Module folgen via module.RegisterUi).
|
||||
var uiHost = AppHost.Services.GetRequiredService<PolyTraderSharp.Ui.ShellUiHost>();
|
||||
var viewServices = AppHost.Services;
|
||||
@@ -292,6 +302,51 @@ internal static class Program
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// F1: Lädt den Master-Key (POLYTRADER_MASTER_KEY, sonst gitignorierte master.key) und aktiviert die
|
||||
/// at-rest-Verschlüsselung. Ohne Key läuft die App wie bisher mit Klartext – mit deutlicher Warnung
|
||||
/// (kein stiller Sicherheitsverlust). Muss VOR jeder Credential-Entschlüsselung laufen.
|
||||
/// </summary>
|
||||
private static void ConfigureSecretProtection(TerminalLogger logger)
|
||||
{
|
||||
string? masterKey = Environment.GetEnvironmentVariable("POLYTRADER_MASTER_KEY");
|
||||
if (string.IsNullOrWhiteSpace(masterKey))
|
||||
{
|
||||
string keyFile = Path.Combine(AppContext.BaseDirectory, "master.key");
|
||||
if (File.Exists(keyFile)) masterKey = File.ReadAllText(keyFile).Trim();
|
||||
}
|
||||
SecretProtection.Configure(masterKey);
|
||||
|
||||
if (SecretProtection.IsConfigured)
|
||||
logger.Info("🔐 Secret-Verschlüsselung aktiv – Account-Credentials werden at-rest verschlüsselt (AES-256-GCM).");
|
||||
else
|
||||
logger.Warning("⚠️ SICHERHEIT: Kein POLYTRADER_MASTER_KEY gesetzt – Account-Credentials liegen UNVERSCHLÜSSELT in der DB. " +
|
||||
"Master-Key setzen (env POLYTRADER_MASTER_KEY oder master.key), siehe docs/sicherheit.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// F1: Verschlüsselt einmalig/idempotent evtl. vorhandene Klartext-Credentials in der DB (Alt-Bestand
|
||||
/// wird beim Re-Save durch den EF-Converter verschlüsselt). Nur wenn ein Master-Key gesetzt ist.
|
||||
/// Fehler blockieren den Start nicht (Log); künftige Saves verschlüsseln ohnehin.
|
||||
/// </summary>
|
||||
private static void ReencryptAccountCredentials(IServiceProvider services)
|
||||
{
|
||||
if (!SecretProtection.IsConfigured) return;
|
||||
var logger = services.GetRequiredService<TerminalLogger>();
|
||||
try
|
||||
{
|
||||
var accountRepo = services.GetRequiredService<IAccountRepository>();
|
||||
var accounts = accountRepo.GetAll(); // Converter entschlüsselt/passt Klartext durch
|
||||
foreach (var acc in accounts) accountRepo.Upsert(acc); // Re-Save → Converter verschlüsselt
|
||||
logger.Info($"🔐 Account-Credentials at-rest gesichert ({accounts.Count} Account(s)).");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"Re-Encryption der Account-Credentials fehlgeschlagen: {ex.Message}. " +
|
||||
"Stimmt der Master-Key mit den bereits verschlüsselten Daten überein?");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Headless-Smoke-Test: baut einen minimalen Host (Persistenz + State + Modul-Registrierung
|
||||
/// + Shell/Launcher), hydriert den State aus MySQL und konstruiert jede registrierte View
|
||||
@@ -330,6 +385,9 @@ internal static class Program
|
||||
services.AddTransient<PolyTraderSharp.Ui.LauncherForm>();
|
||||
}).Build();
|
||||
|
||||
// F1: Master-Key laden, damit die Hydration verschlüsselte Credentials entschlüsseln kann.
|
||||
ConfigureSecretProtection(host.Services.GetRequiredService<TerminalLogger>());
|
||||
|
||||
// State aus MySQL laden (Accounts/Trader/Settings), ohne die BackgroundServices zu starten.
|
||||
try
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user