Laden...

App.config verschlüsseln

Erstellt von RayYago vor 4 Jahren Letzter Beitrag vor 4 Jahren 3.387 Views
R
RayYago Themenstarter:in
19 Beiträge seit 2019
vor 4 Jahren
App.config verschlüsseln

Hallo liebe User,

zu diesem Thema gibt es bereits ähnliche Beiträge, aber ich habe hier ein kleines Problem, dass ich einfach nicht lösen kann.

Ausgangssituation:
Ich habe eine kleine Anwendung geschrieben, die ein paar Cliene IO Aufgaben erledigt und dann Logs in eine Datenbanktabelle schreibt.
Die IO Aufgaben spielen hierbei keine Rolle.
Es ist wirklich nur eine sehr simple Anwendung die am Ende Informationen nach erledigten Aufgaben, in die Datenbank schreibt.
Den ConnectionString habe ich in der Datei App.config hinterlegt, als plain text.

Ein ConnectionString in plain text ist generell sehr schlecht, also habe ich mich etwas schlau gemacht und herausgefunden das man mit dem ConfigurationManager (System.Configuration), Teile der Config verschlüsseln kann.

Das funktioniert eigentlich auch sehr gut, nur wenn ich den Code ausführe der das Ver/Entschlüsseln macht, erstellt mir ConfigurationManager immer eine neue Datei die er verschlüsselt, die dann z.B. unter "bin\Debug\netcoreapp3.0" liegt und nicht im Hauptverzeichnis.

Das macht auch Sinn, da dort ja alle Dateien liegen die zum Client übertragen werden müssen.

Aber die eigentliche App.Config bleibt unverschlüsselt. Der Client hat zwar die verschlüsselte Version, aber in meinen Verzeichnissen ist die unverschlüsselte Datei zu finden.

Kann ich die App.Config nicht auch irgendwie verschlüsseln?

Beispiel:
erstellen -> C:\Project\Application\App.Config (plain text)
mit ConfigurationManager SectionInformation.ProtectSection verschlüsseln.
Neue Datei wurde unter C:\Project\Application\bin\Debug\Applicaion.config erstellt.
App.Config ist aber immer noch unverschlüsselt und für jeden Sichtbar, der Zugriff auf das Entwicklerlaufwerk hat.

Hier der Code:

private bool EncryptConnectionString(bool encrypt, string fileName)
        {
            bool success = true;
            Configuration configuration = null;
            try
            {
                configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                ConnectionStringsSection configSection = (ConnectionStringsSection)configuration.GetSection("connectionStrings");
                if (!configSection.ElementInformation.IsLocked && !configSection.SectionInformation.IsLocked)
                {
                    if (encrypt && !configSection.SectionInformation.IsProtected)
                    {
                        configSection.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
                    }

                    if (!encrypt && configSection.SectionInformation.IsProtected) 
                    {
                        configSection.SectionInformation.UnprotectSection();
                    }
                    configSection.SectionInformation.ForceSave = true;
                    configuration.Save();
                    success = true;
                }
            }
            catch (Exception ex)
            {
                success = false;
            }
            return success;
        }
16.806 Beiträge seit 2008
vor 4 Jahren

Warum hast Du mit .NET Core 3 eine App.Config, und keine AppSettings.json?
In eine Settings-Datei sollten überhaupt keine sensiblen Informationen platziert werden.

Was ist denn Deine Umgebung? Webanwendung? IIS? Cloud?

R
RayYago Themenstarter:in
19 Beiträge seit 2019
vor 4 Jahren

Ich habe eine .NET Core Konsolen-Anwendung. Standardmäßig ist weder appConfig noch AppSettings vorhanden. Ich dachte das die App.Config Standard ist und habe dann einfach selber eine erstellt und dem Project hinzugefügt über Visual Studio 2019 😕

Wo sollte ich sonst sowas speichern? Ich wollte einen großen Aufwand vermeiden für ein paar so kleine Aufgaben wie die Anwendung durchführt.

Ich hatte noch gelesen das der beste Weg wäre, einen Service (ich glaub so wurde das genannt) zu erstellen, der zwischen Client und Datenbank arbeitet. Aber das wäre, meiner Meinung nach ein overkill für ein paar Inserts die die Anwendung macht...

Vielleicht bin ich auch einfach schlecht informiert. Die meisten Beiträge zu diesem Thema sind aus 2005 - 2012 und behandeln genau den Ansatz mit der App.Config, den ich hier gezeigt habe.

16.806 Beiträge seit 2008
vor 4 Jahren

Nein, prinzipiell ist AppSettings die aktuelle Variante.
Das neue Tooling unterstützt auch App.Configs gar nicht mehr. Auch die .NET Core Settings Dokumentation behandelt ausschließlich AppSettings.json 😃

Prinzipiell solltest Du bei der Grundstruktur der Konsolenanwendungen folgendes machen:
Bau Dir einen ConsoleHost, der die Konfiguration und die Dependency Injection übernimmt.
Siehe Sample hier: https://github.com/BenjaminAbt/Hangfire.ConsoleHost/blob/master/sample/Server/Program.cs

Damit hast Du die Grundstruktur.

Bleibt das Thema Settings: Settings-Dateien, egal ob AppConfig oder AppSettings.json, die sensible Informationen haben, haben auf gemeinsamen Laufwerken nichts, aber auch gar nichts zu suchen.
Da hilft Dir prinzipiell die beste Verschlüsselung nichts, weil man sie eben auch wieder in der Anwendung entschlüsseln kann - dann kannst es gleich lassen.

Du kannst problemlos Settings aus mehreren Quellen laden

  • Dateien, an verschiedenen Orten
  • Umgebungsvariablen
  • Datenbanken...

Speicher die sensible Daten bei Desktopanwendungen im Benutzerverzeichnis (AppData) oder in de Umgebungsvariablen des Nutzers.
Aber bitte nicht auf Shared Folders.

Alternativ: einfach die Connection Strings gar nicht speichern und den Benutzer beim Start eingeben lassen.
Die Variante verwende ich recht häufig - eben um nirgends irgendwelche Settings zu hinterlassen.

R
RayYago Themenstarter:in
19 Beiträge seit 2019
vor 4 Jahren

Bau Dir einen ConsoleHost, der die Konfiguration und die Dependency Injection übernimmt.

Ich weiß nicht genau was ein ConsoleHost ist, aber meinst du damit das dieser dann, wie in deinem Beispiel auf GitHub, die AppSettings etc. läd und aufbereitet, damit die Informationen daraus verwendet werden können?

Speicher die sensible Daten bei Desktopanwendungen im Benutzerverzeichnis (ProgramData) oder in de Umgebungsvariablen des Nutzers.

Diese Verzeichnise sind doch auch einsehbar? Umgebungsvariablen sind doch auch als plain text zu sehen. Und eine Datei mit dem ConnectionString einfach unter ProgramData abzulegen ist wahrscheinlich auch nicht besonders sicher oder? Die müsste ich dann verschlüsseln, aber den Key für das entschlüsseln müsste ich ja dann wieder irgendwo ablegen, wo es nicht einfach gelesen werden kann? ^^

Was das Thema Sicherheit angeht bin ich wirklich nicht fit, die Anwendung selbst war kein Problem. Ich verwende bereits den gesamten Winterurlaub darauf, mehr in den Stoff zu kommen und danke dir für deine Bemühungen Abt.

16.806 Beiträge seit 2008
vor 4 Jahren

Ich weiß nicht genau was ein ConsoleHost ist

Wenn Du etwas nicht weißt, dann hilft meist ein Blick in die Dokumentation.

Der Host ist ein Objekt, das alle Ressourcen der App kapselt, z. B.:
Abhängigkeitsinjektion

Protokollierung

Konfiguration

IHostedService-Implementierungen

Es sorgt dafür, dass Du ein Rahmen hast, der Dir gewisse Verantwortungen abnimmt - egal ob in ASP.NET Anwendungen, wo es bereits eingebaut ist, oder Konsolenanwendungen (in denen man es leider selbst hinzugefügen muss) oder Desktop Anwendungen (in denen man das meiste noch hinzufügen muss).
Ein Code Beispiel für Konsolenanwendungen siehst Du via dem GitHub Link.
Die eigentliche Anwendung ist dann:


                    // Startup
                    services.AddHostedService<MyHangfireApp>();

als Start und als Implementierung:


public class MyHangfireApp : IHostedService, IDisposable

Daher hab ich Dir den Link gegeben.

Diese Verzeichnise sind doch auch einsehbar?

Hab ProgramData in AppData bereits korrigiert gehabt.

Aber ja, diese Verzeichnisse sind immer lesbar, sobald der PC komprommitiert ist.
Während er Entwicklung gibt es auch die dotnet user secrets, die manche LEute auch für den produktiven Einsatz nehmen, die auf Environment Variables basieren, aber für den produtiven Einsatz nicht empfohlen sind.

Umgebungsvariablen werden im Allgemeinen in unverschlüsseltem, unverschlüsseltem Text gespeichert. Wenn der Computer oder der Prozess kompromittiert ist, kann von nicht vertrauenswürdigen Parteien auf Umgebungsvariablen zugegriffen werden. Möglicherweise sind zusätzliche Maßnahmen erforderlich, um eine Offenlegung von Benutzer Geheimnissen zu verhindern.

Umgebungsvariablen sind doch auch als plain text zu sehen. Und eine Datei mit dem ConnectionString einfach unter ProgramData abzulegen ist wahrscheinlich auch nicht besonders sicher oder?

Egal wie man ein Passwort ablegt: sobald man ein Passwort irgendwo ablegt, ist es nicht sicher.
Sicher sind nur integrierte Lösungen wie zB. Azure Active Directory oder zB. Azure Key Vault (für Applikationen in der Azure Umgebung).

Alles, was Du irgendwie auf Deinem PC ablegst, kann auf eine gewisse Art und Weise ausgelesen werden.
Alles.

Eine Verschlüsselung von etwas, das Du nachher wieder im Urzustand brauchst (wie ein ConnectionString oder ein Passwort, also kein Hashing) ist prinzipiell auch nur eine Verschleierung.
Spätestens im Speicher kann es ausgelesen werden.

Daher auch den Tipp mit der Alternative.

Der Zustand

Aber die eigentliche App.Config bleibt unverschlüsselt. Der Client hat zwar die verschlüsselte Version, aber in meinen Verzeichnissen ist die unverschlüsselte Datei zu finden.

ist prinzipiell korrekt so.
Du hast während der Entwicklung immer die originale Datei; und wenn diese Teil des Publish Prozesses ist, dann wird diese natürlich zusätzlich mitgliefert.

Im Prinzip ist das alles eher "ein Problem Deines Workflows"; das so kein Tooling abdeckt.

R
RayYago Themenstarter:in
19 Beiträge seit 2019
vor 4 Jahren

Danke noch einmal für deine Hilfe Abt.

Ich habe jetzt einwenig damit herumgespielt und denke mache dass erstmal so:

COnnectionString wird irgendwo verschlüsselt gespeichert wie z.B. unter AppData, und die Application muss dann über die Arugumente mit dem Passwort gestartet werden.

Denn eine andere Software soll dieses Programm starten.

Aber ich hätte mal zweikleine Fragen zu deinem Beispiel:

Prinzipiell solltest Du bei der Grundstruktur der Konsolenanwendungen folgendes machen:
Bau Dir einen ConsoleHost, der die Konfiguration und die Dependency Injection übernimmt.
Siehe Sample hier:
>

Ich habe mal eine abgespeckte version erstellt um meine Frage deutlicher zu machen. Das Beispiel hat keine DependencyInjection.

HostBuilder:

static async Task Main(string[] args)
        {
            IHostBuilder builder = new HostBuilder()
                    .ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        config.SetBasePath(Directory.GetCurrentDirectory());
                        config.AddJsonFile("appsettings.json", optional: true);
                        config.AddJsonFile($"appsettings.{Environment.MachineName}.json", optional: true);
                    })
                    .ConfigureServices((hostingContext, services) =>
                    {
                        services.AddOptions();

                        // Startup
                        services.AddHostedService<MyApp>();
                    });
            await builder.RunConsoleAsync();
        }

Frage 1:
Ich habe bzw. brauche keine Hintergrundprozesse. Meine Anwendung soll linear oder eher gesagt synchron ablaufen.
Kann ich dann in der Methode

public Task StartAsync(CancellationToken cancellationToken)

enfach meine Aufgaben die ich abarbeiten will ausführen oder wäre das eher unsauber programmiert dann?
So in etwa:

public Task StartAsync(CancellationToken cancellationToken)
        {
            StartDownload();
            MoveFile();
            LogInformation;
            //Fehler bei den Aufgaben aufgetreten
            //App soll mit einem return code geschlossen werden
            Environment.Exit(5);
            return Task.CompletedTask;
        }

Frage 2:
Wenn jezt ein Fehler in der Application auftritt, möchte ich die APP mit einem Returncode beenden:

Environment.Exit(5);

Das funktioniert allerdings nicht weil ich glaube, dass das nur den Prozess beendet der aktuell läuft und nicht die gesamte Application.

Den ReturnCode brauche ich bzw. die Anwendung die dieses Programm aufruft.

Wie könnte ich denn die gesamte Anwendung in dem Fall mit einem return code schließen?

P
441 Beiträge seit 2014
vor 4 Jahren

Ein wenig google hätte dich zu diesem Beitrag geführt:
https://stackoverflow.com/questions/54912012/how-to-stop-exit-terminate-dotnet-core-hostbuilder-console-application-programma

--> indem du dir über DI die Implementierung von IApplicationLifetime holst.

Edit: Hier noch die Dokumentation dazu: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.1#ihostapplicationlifetime

R
RayYago Themenstarter:in
19 Beiträge seit 2019
vor 4 Jahren

@Papst

Das hatte ich gesehen, was ich aber meinte ist das icht die Application mit einem return code beenden möchte. Aber wenn ich den HostBuilder verwende und in meiner eigentlichen Anwendung z.B.

_lifeTime.StopApplication();

gelange ich am Ende in die StopAsync Methode. Aber ich kann hier weder

Environment.Exit();

aufrufen, noch kann ich einen INT oder dergleichen zurückgeben, damit ich in der Main Methode z.B.

return 5;

zurück geben kann.
Das war die eigentliche Frage dabei. Wie ich die App mit einem return code beenden kann ohne das ich globale variablen verwenden muss.

Ich habe sowohl Doku als auch mit google alles durchforstet, bereits versucht die Methoden zu ändern damit die einen Wert zurück geben können, aber die sind ja alle durch den IHostService Interface vorgegeben. builder.RunConsoleAsync(); kann ebenfalls keine Werte zurück geben.

Ich finde einfach nicht raus wie ich das hinbekommen soll.

Ich dachte dass man vielleicht Properties setzten kann, aber ich konnte noch nicht herausfinden wie und ob das überhaupt geht.

P
441 Beiträge seit 2014
vor 4 Jahren

Den Exit Code musst du weiterhin aus der Main method zurückgeben.

Eine "globale Variable" ist nicht notwendig, hierfür kannst du das gleiche Konstrukt verwenden, wie Microsoft für den IApplicationLifetime verwendet: Ein im Dependency Injection registriertes Singleton, dass du allerdings vorab instanziierst und zur Kommunikation zwischen deinem HostedService und der Main Methode verwendest.


static async Task<int> Main(string[] args)
        {
            MyLifetimeHandler myLifetime = new MyLifetimeHandler();
            IHostBuilder builder = new HostBuilder()
                    .ConfigureAppConfiguration((hostingContext, config) =>
                    {
                        config.SetBasePath(Directory.GetCurrentDirectory());
                        config.AddJsonFile("appsettings.json", optional: true);
                        config.AddJsonFile($"appsettings.{Environment.MachineName}.json", optional: true);
                    })
                    .ConfigureServices((hostingContext, services) =>
                    {
                        services.AddOptions();
                        services.AddSingleton(myLifetime);
                        // Startup
                        services.AddHostedService<MyApp>();
                    });
            await builder.RunConsoleAsync();
            return myLifetime.ExitCode;
        }



class MyApp: IHostedService
{
  public MyApp(...., MyLifetimeHandler lifetime) {...}
  [...]
}


class MyLifetimeHandler
{
  private readonly IApplicationLifetime _lifetime;

  public int ExitCode { get; private set; }

  MyLifetimeHandler(IApplicationLifetime lifetime)
  {
     _lifetime = lifetime;
  }

  void Exit(int exitCode)
  {
    ExitCode = exitCode;
    _lifetime.StopApplication();
  }
}

Edit: vielleicht kann einer der Mods das mal vom Thema trennen.