Zum Inhalt

← Zurück zur Übersicht

Bedrohungsmodell

Das vollständige Dokument: was geschützt wird, vor wem, womit und wo der Schutz endet. Wo etwas nicht überprüft oder nicht zugesichert ist, sagen wir es.

Referenzdokument für Version 0.1.0 (Vorabversion). Es wurde nicht von Dritten überprüft.

Sicherheitsprobleme bitte melden an security@shelleasy.app.

Dieses Dokument legt dar, vor wem ShellEasy die gespeicherten Daten schützt, mit welchen Mitteln und wo der Schutz endet. Jede Aussage wurde am Code überprüft; wo etwas nicht überprüft oder nicht garantiert ist, wird das ausdrücklich gesagt.

Letzte Prüfung: Version 0.1.0 (Pre-Release). Ändern sich die Kryptografie, die Vertrauensgrenzen oder das Dateiformat, ist dieses Dokument in derselben Änderung zu aktualisieren.

1. Was geschützt wird

SchutzgutSpeicherortWert
Private SSH-Schlüssel, Passphrasen, ServerpasswörterSpalten identities.* der DatenbankAm höchsten: Sie gewähren Zugriff auf die Server
Liste der Hosts, Benutzernamen, Ordner, Tags, Snippets, TunnelregelnDatenbankHoch: Sie verraten die Infrastruktur und die Gewohnheiten
Master-Passwortnur im Kopf des Nutzers (optional im Schlüsselbund, hinter der Biometrie)Der einzige Schlüssel zu allem
Bereits akzeptierte Serverschlüssel (known_hosts)DatenbankSie verhindern den Man-in-the-Middle-Angriff
Exportdateien .shelleasydort, wo der Nutzer sie speichertSie enthalten alles, einschließlich der Geheimnisse
Inhalt der Sitzungen und der übertragenen DateienArbeitsspeicher, Netzwerk, ZieldatenträgerAußerhalb des Schutzbereichs des Tresors (siehe §7)

2. Wer der Angreifer ist

Berücksichtigt:

  1. Wer ohne das Master-Passwort an die Dateien der App gelangt — gestohlener oder verlorener Rechner, kopiertes Backup (Time Machine, Cloud, externe Festplatte), anderer Benutzer desselben Rechners, Schadsoftware, die Dateien liest, aber das Passwort noch nicht hat. Das ist das Hauptszenario, für das der Tresor existiert.
  2. Wer die Dateien lesen und ändern kann, während die App geschlossen ist — dieselbe Kategorie, aber aktiv: Er kann Dateien im Datenordner verändern oder ersetzen.
  3. Ein feindlicher oder kompromittierter SSH-/SFTP-Server, zu dem der Nutzer eine Verbindung aufbaut: Er kann bösartige Datei- und Ordnernamen, Terminalausgaben mit Sonderzeichenfolgen und geänderte Schlüssel senden.
  4. Eine gezielt präparierte .shelleasy-Datei oder ~/.ssh/config, die dem Nutzer zum Import untergeschoben wird.
  5. Ein anderer Rechner im Netzwerk, der versucht, einen offen gelassenen Tunnel oder SOCKS-Proxy zu nutzen.
  6. Wer über die Schulter mitschaut oder einen entsperrten, für wenige Minuten unbeaufsichtigten Rechner an sich nimmt — nur durch die automatische Sperre abgemildert (§5.4).

Nicht berücksichtigt (außerhalb des Schutzbereichs):

  • Ein Angreifer mit entsperrtem Rechner und geöffneter App oder mit Code, der als derselbe Benutzer läuft, während der Tresor entsperrt ist: Er kann den Arbeitsspeicher des Prozesses lesen, die Tastatur aufzeichnen und die App zum Exportieren auffordern. Kein lokaler Tresor hält dem stand.
  • Ein kompromittiertes Betriebssystem oder eine kompromittierte Firmware.
  • Die Analyse von Seitenkanälen (Zeiten, Verbrauch, Caches) auf einem Rechner, der mit Personen geteilt wird, die diese beobachten können.
  • Ein Angreifer, der ein schwaches Master-Passwort mit einem aufwendigen, aber nicht unmöglichen Offline-Angriff errät (§5.1): Die Stärke des Schutzes hängt vom gewählten Passwort ab.
  • Die Sicherheit der Server, zu denen eine Verbindung besteht, und der SSH-Bibliothek von Go (golang.org/x/crypto/ssh), der vertraut wird.

3. Wie die Verschlüsselung funktioniert (in Kürze)

Master-Passwort --Argon2id(salt, t=3, 64 MiB, p=4)--> KEK
KEK --AES-256-GCM, AAD "easyshell/vault/dek/v1"--> verpackte DEK   (in vault.json, AUSSERHALB der Datenbank)
DEK --HKDF-SHA256, info "easyshell/db/v1"--> Datenbankschlüssel
Datenbankschlüssel --Adiantum, 4-KiB-Blöcke--> gesamte SQLite-Datei
DEK --AES-256-GCM, AAD "tabella|colonna|id"--> einzelne Geheimnisse (zweite Ebene, innerhalb der Datenbank)
  • vault.json enthält nur Version, Salt, Argon2id-Parameter und die verpackte DEK. Die Datei liegt außerhalb der Datenbank, weil sie zu deren Öffnung gebraucht wird. Sie wird atomar geschrieben, Berechtigungen 0600.
  • Gesamte Datenbank verschlüsselt mit Adiantum (Bibliothek ncruces/go-sqlite3): Hostnamen, Benutzernamen, Ordner, Tags, Tabellenstruktur, Indizes, Journaldateien von SQLite. Temporäre Dateien verwenden Zufallsschlüssel, und temp_store=memory vermeidet, dass solche Dateien überhaupt geschrieben werden.
  • Zweite Ebene für die Geheimnisse: Jeder private Schlüssel, jede Passphrase und jedes Passwort ist mit AES-256-GCM verschlüsselt und durch die AAD tabella|colonna|id an den eigenen Datensatz gebunden. Ein in einen anderen Datensatz kopierter Blob lässt sich nicht öffnen (in den Tests überprüft). Jede Stelle im Code, die ein Geheimnis ver- oder entschlüsselt, läuft über vault.FieldAAD mit der id der Zeile (überprüft: data.go, sessions.go, sync.go).
  • Die Oberfläche erhält nie die Geheimnisse. Die Identitäten gelangen nur mit Kennzeichen (hasKey, hasPassphrase, hasPassword) zur Oberfläche; der private Schlüssel wird über einen im nativen Dialog gewählten Pfad angegeben und läuft nicht durch die Webview. Überprüft in internal/core/data.go und in den Bindings von app.go.
  • .shelleasy-Datei: Kopfdaten (Magic, Version, Argon2id-Parameter, Salt, Nonce), als AAD authentifiziert, dann AES-256-GCM über das komprimierte JSON. Die Klartextdaten nach der Dekomprimierung sind auf 64 MiB begrenzt, die gelesene Datei auf 128 MiB.

4. Was im Klartext bleibt (Metadaten)

Wer die Dateien, aber nicht das Passwort hat, liest die Daten nicht, kann aber Folgendes sehen:

  • Dass ein Datenordner von ShellEasy existiert, die Namen der Dateien (vault.json, die Datenbank, backups/) sowie deren Größe und Änderungsdaten. Die Größe der Datenbank gibt eine ungefähre Vorstellung davon, wie viele Daten sie enthält; die Daten zeigen, wann die App benutzt und wann Änderungen gespeichert wurden.
  • Die Parameter des Tresors: Salt, Kosten von Argon2id, Version des Formats (vault.json). Sie sind keine Geheimnisse, verraten aber, wie viel ein Versuch kostet, das Passwort zu erraten.
  • Die ungefähre Länge der Geheimnisse innerhalb der Datenbank ist verborgen (verschlüsselt in 4-KiB-Blöcken), die Gesamtgröße jedoch nicht.
  • Bei den .shelleasy-Dateien: die Größe (≈ Datenmenge nach der Komprimierung) und die Parameter der Kopfdaten.
  • Außerhalb des Tresors, je nach System: zuletzt verwendete Ordner, die zuletzt verwendeten Objekte des Betriebssystems, der Verlauf der lokalen Shell, die temporären Dateien der „Remote-Bearbeitung“, solange sie geöffnet sind (temporärer Ordner des Benutzers 0700, Dateien 0600, beim Schließen entfernt).
  • Die Einstellungen der Oberfläche, gespeichert im localStorage der Webview (Schlüssel es.*): Sie enthalten keine Serverdaten.

Der Schlüsselbund des Systems enthält, wenn das Entsperren per Fingerabdruck (Touch ID) aktiviert wird, das Master-Passwort (§5.5).

5. Grenzen und Entscheidungen, eine nach der anderen

5.1 Das Master-Passwort ist die einzige Barriere

Der Angreifer aus §2 (1) kann Passwörter offline durchprobieren. Jeder Versuch kostet ein Argon2id mit 64 MiB, 3 Durchgängen und 4 Threads: Das macht den Angriff langsam, aber ein schwaches Passwort bleibt schwach. Die verlangte Mindestlänge beträgt 8 Zeichen; ein Satz aus 4–5 Wörtern ist deutlich besser als ein Wort mit Sonderzeichen. Eine Wiederherstellung gibt es nicht: Geht das Passwort verloren, sind die Daten verloren (Projektentscheidung, „Zero-Knowledge“). Deshalb wird ein .shelleasy-Export empfohlen.

Die Parameter dürfen das Minimum (3 Durchgänge, 64 MiB, 4 Threads) weder unterschreiten noch eine Obergrenze (16 Durchgänge, 1 GiB, 16 Threads) überschreiten: siehe §6.1.

5.2 Verschlüsselung der Datenbank: was Adiantum leistet und was nicht

Adiantum ist eine Festplattenverschlüsselung (die Datei wird in 4-KiB-Blöcke geteilt, jeder Block wird vollständig verschlüsselt, wobei derselbe Klartext an derselben Stelle denselben Chiffretext ergibt). Ihr Bedrohungsmodell lautet: Der Angreifer sieht mehrere Kopien des Datenträgers und erfährt ausschließlich, welche Blöcke sich geändert haben. Daraus folgen drei Grenzen, die auch für ShellEasy gelten:

  1. Sie ist deterministisch. Wer zwei Kopien der Datenbank hat (zum Beispiel zwei Backups von verschiedenen Tagen), sieht, welche 4-KiB-Blöcke sich geändert haben, welche nicht und welche zu einem früheren Wert zurückgekehrt sind, aber nicht, was darin steht. Ein VACUUM baut die Datei neu auf und löscht einen großen Teil dieser Information; ShellEasy führt es nicht von sich aus aus.
  2. Sie authentifiziert die Seiten nicht. Ein veränderter Block wird zu Zufallsdaten entschlüsselt (der ganze Block, nicht ein einzelnes Bit), was SQLite in der Regel als Beschädigung erkennt, aber das ist keine Garantie: Adiantum erklärt keinen Schutz vor Manipulation. Ein Angreifer, der die Datei verändert, kann daher im schlimmsten Fall Daten beschädigen oder verlieren lassen oder echte, früher bereits vorhandene Blöcke oder ganze Dateien wieder einspielen (siehe Punkt 3); ohne den Schlüssel kann er keine Inhalte nach eigener Wahl konstruieren.
  3. Sie schützt nicht vor dem Wiedereinspielen einer alten Kopie. Wer die Datenbank durch eine eigene frühere Version (gleicher Schlüssel) ersetzt, erhält eine gültige, aber alte Datenbank: Hosts und Identitäten, die nach diesem Datum gelöscht oder geändert wurden, erscheinen wieder so, wie sie waren. Die App bemerkt das nicht.

Was die Integrität der Geheimnisse dennoch schützt. Die Zugangsdaten hängen für ihre Authentizität nicht von Adiantum ab: Sie sind AES-GCM-Blobs mit an den Datensatz gebundener AAD. Ein manipulierter Block oder ein verschobener Blob lässt das Öffnen des Geheimnisses scheitern (Fehler, keine falschen Daten). Ein alter, aber echter Blob desselben Datensatzes (Rollback eines einzelnen Geheimnisses oder der ganzen Datei) lässt sich dagegen öffnen: Das ist die Grenze aus Punkt 3.

Praktische Folgen für den Nutzer:

  • Eine Kopie der Datenbank ist kein sicheres Backup, das dort aufbewahrt werden darf, wo andere es beschreiben können: Ein Backup dient der Wiederherstellung, nicht dem Vertrauen. Die Backups der App (backups/, die letzten 10 Kopien, die vor jedem Import angelegt werden) sind verschlüsselte Kopien mit den Berechtigungen 0700/0600.
  • Die Datenbank wird nie automatisch aus einer ungeprüften Kopie neu aufgebaut oder ersetzt.
  • Das robusteste Wiederherstellungsmittel ist der .shelleasy-Export (AES-GCM, vollständig authentifiziert).

5.3 vault.json: MAC und Versionszähler — geprüft und nicht übernommen

Es wurde eine Integritätsprüfung der Datei erwogen, zum Beispiel ein MAC auf vault.json plus ein Versionszähler. Ergebnis:

  • Ein MAC auf vault.json bringt nichts. Die Datei ist für das, was zählt, bereits authentifiziert: Die verpackte DEK ist ein AES-GCM-Blob mit AAD; jede Änderung an Salt, Parametern oder Blob verändert den abgeleiteten Schlüssel oder bricht die Authentifizierung, und das Entsperren schlägt fehl (ErrWrongPassword), ohne je einen falschen Schlüssel zu erzeugen. Der Angreifer aus §2 (2) kann das Entsperren verhindern (die Datei löschen oder beschädigen), aber nicht umlenken.
  • Ein Versionszähler trägt nicht ohne einen Fixpunkt außerhalb der Reichweite des Angreifers. Liegt der Zähler im selben Ordner, stellt, wer eine alte Datei wiederherstellen kann, auch den Zähler wieder her. Nötig wäre ein Anker im Schlüsselbund des Systems (der nur dort existiert, wo der Schlüsselbund verfügbar und an die Biometrie gebunden ist, heute nur unter macOS), und er bliebe nur gegenüber jemandem gültig, der nicht auch diesen verändern kann. Er wird jetzt nicht übernommen; er bleibt als Idee im Follow-up-Issue vermerkt (§9).
  • Das Wiedereinspielen eines alten vault.json hat jedoch eine reale Wirkung: Die DEK ändert sich nicht, wenn das Master-Passwort geändert wird (sie wird nur neu verpackt, Projektentscheidung, um nicht alles neu zu verschlüsseln). Wer also das alte Passwort kennt und eine alte Kopie von vault.json (zum Beispiel in einem Backup) sowie eine Kopie der Datenbank hat, kann die Datenbank weiterhin öffnen, auch nachdem der Nutzer das Passwort wegen des Verdachts einer Kompromittierung geändert hat. Besteht der Verdacht, das Passwort sei kompromittiert, genügt es nicht, es zu ändern: Es ist ein neuer Tresor mit einem neuen Passwort anzulegen und ein .shelleasy-Export zu importieren. Eine Rotation der DEK beim Passwortwechsel ist im Follow-up vermerkt (§9).

5.4 Sperre, offene Sitzungen und Arbeitsspeicher

  • Automatische Sperre nach 15 Minuten Inaktivität der Oberfläche (fester Wert, noch nicht konfigurierbar) und auf Anforderung. Die Sperre schließt die Datenbank und überschreibt die DEK mit Nullen. Bereits authentifizierte SSH-Sitzungen und Tunnel bleiben nach der Sperre aktiv (Entscheidung D3, um laufende Arbeit nicht zu unterbrechen); zum Öffnen neuer Sitzungen ist Entsperren nötig. Um alles zu beenden, sind die Sitzungen oder die App zu schließen.
  • Überschreiben mit Nullen nicht garantiert. Die App hält die Geheimnisse als []byte und überschreibt sie nach Gebrauch mit Nullen (vault.Zero; die Sperre überschreibt die DEK), aber Go stellt nicht sicher, dass der Garbage Collector oder das Wachstum des Stacks keine Kopien hinterlassen, und nicht alle Daten laufen über []byte:
    • das Master-Passwort kommt als String von der Oberfläche (die Brücke zwischen Webview und Go transportiert es so), und Kopien bleiben im Speicher der Webview und in Puffern der Brücke zurück, die die App nicht kontrolliert;
    • der Datenbankschlüssel wird als hexadezimaler String an den SQLite-Treiber übergeben (PRAGMA hexkey, in Go unveränderlich) und bleibt im Speicher des Treibers, bis der Prozess endet; das Schließen der Datenbank ist keine überprüfte Löschung;
    • die zur Authentifizierung einer Sitzung gelesenen privaten Schlüssel bleiben im Speicher des SSH-Clients, solange die Sitzung besteht;
    • der Speicher ist nicht gesperrt (mlock) und kann im Swap oder in einer Absturz- bzw. Ruhezustandsdatei landen. Daher kann, wer den Arbeitsspeicher des Prozesses oder einen Datenträger- bzw. RAM-Dump lesen kann, auch nach der Sperre Geheimnisse finden. Das ist eine ausdrücklich genannte Grenze, kein isolierter Fehler: siehe auch §2.
  • In Protokollen und Fehlermeldungen erscheinen keine Geheimnisse (durch Codesuche überprüft: kein Anwendungslog; das einzige log.Fatalf steht in main.go und gibt den Startfehler von Wails aus).

5.5 Entsperren per Fingerabdruck

Das Master-Passwort wird vom System hinter der Biometrie verwahrt und von der App nie auf die Festplatte geschrieben. Unter macOS liegt das Geheimnis bei signierter App mit Provisioning-Profil im Schlüsselbund mit kSecAccessControlBiometryCurrentSet und WhenUnlockedThisDeviceOnly: Das System verlangt beim Lesen den Fingerabdruck, und das Geheimnis wird ungültig, wenn sich die registrierten Fingerabdrücke ändern. Ein nicht signierter Build oder einer mit Ad-hoc-Signatur nutzt eine schwächere Stufe (app): Das Geheimnis liegt dennoch im Schlüsselbund, und ein anderes Programm liest es nicht ohne Zustimmung des Systems, aber die Abfrage des Fingerabdrucks ist eine Kontrolle der App und nicht des Systems; die Oberfläche zeigt die verwendete Stufe an. Das Entsperren nutzt dasselbe Unlock: Es gibt keinen alternativen Weg zum Schlüssel. Nicht überprüft unter Windows (Windows Hello) und Linux: Sie sind noch nicht implementiert.

5.6 Serverschlüssel (TOFU)

Bei der ersten Verbindung zeigt die App den SHA256-Fingerabdruck an und verlangt eine Bestätigung; der Schlüssel wird auf dem Gerät gespeichert. Präsentiert der Server später einen anderen Schlüssel (oder einen anderen Typ als die bekannten), wird die Verbindung mit ausdrücklicher Fehlermeldung blockiert, ohne Möglichkeit, spontan zu akzeptieren (HostKeyChangedError). Im Code gibt es kein InsecureIgnoreHostKey (überprüft). Intrinsische Grenze von TOFU: Ist bereits die erste Verbindung abgefangen, fällt der Angriff nicht auf; die Überprüfung des Fingerabdrucks über einen anderen Kanal bleibt Sache des Nutzers. Die bekannten Schlüssel werden nicht zwischen Geräten synchronisiert: Das Vertrauen gilt je Gerät.

5.7 Ein feindlicher SFTP-Server

Der Server wählt die Namen der Dateien, die er auflistet. Solange die Namen nur angezeigt werden, ist das Risiko gering (die Oberfläche verwendet kein v-html/innerHTML: überprüft). Das eigentliche Risiko liegt bei den Übertragungen: Beim Herunterladen eines Ordners verließ ein Name wie ..\..\Startup\x.exe unter Windows, wo \ Ordner trennt, den vom Nutzer gewählten Ordner. Mit dieser Revision behoben (§6.2): Von einer entfernten Seite gelesene Namen werden geprüft, bevor irgendein lokaler Pfad gebildet wird; gefährliche werden übersprungen und erscheinen in den Hinweisen zur Übertragung. Der SFTP-Client verwirft bereits . und .. und verwendet path.Base für die Namen. Entfernte symbolische Links werden bei Übertragungen nie verfolgt.

Außerdem werden eintreffende Dateien nun mit den Berechtigungen 0600 angelegt und übernehmen die Berechtigungen des Originals erst nach abgeschlossenem Kopieren, sodass eine vertrauliche Datei für andere Benutzer nicht lesbar ist, solange sie unvollständig ist.

5.8 Eine feindliche .shelleasy-Datei

Die Kopfdaten sind authentifiziert, ihre numerischen Teile werden jedoch vor der Überprüfung des Passworts gelesen. Eine Datei, die darauf angelegt ist, viel Speicher belegen oder stundenlang rechnen zu lassen, musste im Voraus abgelehnt werden: behoben durch die Obergrenze für die Parameter (§6.1). Nach dem Öffnen gelten: maximale Größe der gelesenen Datei (128 MiB), des dekomprimierten Umschlags (64 MiB, gegen Dekompressionsbomben), maximale Anzahl von Elementen (200.000), Validierung jedes Feldes, kein Inhalt wird ausgeführt. Der Import zeigt eine Vorschau, legt eine Sicherungskopie der Datenbank an und ist eine einzige Transaktion. Eine feindliche Datei, die von jemandem stammt, der das Passwort kennt, kann dennoch Hosts oder Snippets mit von ihm gewähltem Inhalt einfügen: Snippets weisen Steuerzeichen (ESC, C1, DEL) zurück, die im Terminal dazu dienen würden, Eingefügtes zu verbergen oder zu verändern; der Nutzer sieht den endgültigen Befehl dennoch, bevor er ihn einfügt, wenn Parameter vorhanden sind.

5.9 Import aus ~/.ssh/config

Die Datei wird gelesen und interpretiert (einschließlich verschachtelter Include mit Tiefenbegrenzung und Zykluserkennung), aber es wird nichts ausgeführt: ProxyCommand wird nicht unterstützt, der Host wird als nicht importierbar gemeldet, und die App startet diesen Befehl nie.

5.10 Tunnel und Proxys

Standardmäßig wird nur auf 127.0.0.1 gelauscht. Wird eine aus dem Netzwerk erreichbare Adresse gewählt, zeigt die App eine Warnung im Dialog und in der Liste (mit SOCKS5 wäre das ein offener Proxy für alle, ohne Authentifizierung). Ein lokaler Tunnel, der auf 127.0.0.1 lauscht, ist dennoch von jedem Programm auf demselben Rechner aus erreichbar (einschließlich anderer Benutzer auf Mehrbenutzersystemen): Das ist das Verhalten von ssh -L; es wird dokumentiert und nicht abgemildert. Das Sperren des Tresors stoppt die Tunnel nicht.

5.11 Die Webview und das Frontend

Das Frontend ist eine lokale Seite, die aus dem Paket der App ausgeliefert wird, ohne entfernte Inhalte, ohne v-html/innerHTML/eval (überprüft), mit deaktivierten Entwicklerwerkzeugen von Nuxt (und dem Inspektor der Webview, den Wails nur in Debug-Builds aktiviert). Die Ausgabe der Terminals läuft als Text über xterm.js. Es fehlt eine Content-Security-Policy: Sie wäre heute nicht trivial zu setzen, weil die erzeugte Seite Inline-Skripte enthält (Import-Map und Anfangsdaten); sie streng zu fassen erfordert Hashes oder Nonces. Sie ist als Verbesserung vermerkt (§9): Sie wäre eine zusätzliche Verteidigungsebene, nicht die Behebung einer bekannten Lücke.

5.12 Aktualisierungen und Lieferkette

Keine automatische Aktualisierung und keine Telemetrie: Das Frontend lädt keine entfernten Ressourcen, und die einzigen ausgehenden Verbindungen gehen zu den SSH-Hosts, die der Nutzer öffnet, und – nur wenn er ihn aktiviert – zum Server des KI-Assistenten (§5.13). Der einzige HTTP-Client der App ist internal/ai. Die Go- und npm-Abhängigkeiten sind durch die Lock-Dateien festgelegt; appimagetool ist nach Version und Prüfsumme fixiert. Die Release-Builds sind noch nicht signiert (macOS/Windows): Solange sie es nicht sind, lässt sich die Integrität des heruntergeladenen Pakets nur mit SHA256SUMS überprüfen. Eine automatische Prüfung auf verwundbare Abhängigkeiten gibt es noch nicht: Sie ist hinzuzufügen (§9).

5.13 Lokaler KI-Assistent

Der Assistent (Erklären, Beschreibung zu Befehl, Zusammenfassen, Snippet-Hilfe) ist standardmäßig ausgeschaltet und öffnet einen neuen Kanal: Er überträgt Terminaltext an einen vom Nutzer gewählten Modellserver. Es ist kein Modell enthalten, und keine Anfrage geht ab, ohne dass der Nutzer sie gesehen und gesendet hat.

Wohin der Text gehen kann.

  • Standardmäßig nur an den Rechner des Nutzers: „Lokal“ sind ausschließlich localhost und ausgeschrieben angegebene Loopback-Adressen (127.x, ::1). Jeder beliebige Name gilt als entfernt, auch wenn er heute auf den lokalen Rechner zeigt, weil sich die Namensauflösung ändern kann (DNS-Rebinding).
  • Ein nicht lokaler Server kann nur nach ausdrücklicher Bestätigung verwendet werden, die an diesen Namen und diesen Port gebunden ist: Ein Serverwechsel hebt die Bestätigung auf. Bei einem nicht lokalen Server mit Schlüssel ist https:// erforderlich.
  • Beim Verbindungsaufbau muss bei lokalen Servern die tatsächliche Adresse eine Loopback-Adresse sein, andernfalls wird die Verbindung abgelehnt. Weiterleitungen werden nicht verfolgt, und die Umgebungsvariablen für Proxys werden ignoriert.
  • Der Schlüssel des Servers, falls vorhanden, ist in der Datenbank verschlüsselt (AES-GCM mit eigener AAD), gelangt nie zur Oberfläche und wird nicht an einen anderen als den konfigurierten Server gesendet. Die Konfiguration wird nicht synchronisiert.

Was abgesendet wird. Jede Anfrage läuft über eine Vorschau, die vom Backend erstellt wird: Der angezeigte Text ist der, der abgeht, Byte für Byte (getestet), und zwar an das angezeigte Ziel (Host, Port, Modell); ändern sich die Einstellungen nach der Vorschau, wird das Senden abgelehnt. Vor der Vorschau durchlaufen Text und Anfrage einen Filter, der private Schlüssel, Zugangsdaten in Adressen, Autorisierungs-Header, bekannte Token (GitHub, AWS, Slack, JWT…), Werte von Variablen mit Namen wie password, secret, token, api_key, Kommandozeilenoptionen (--password, sshpass -p, mysql -p…, curl -u) und die im Tresor gespeicherten Passwörter und Passphrasen, sofern sie im Text vorkommen, schwärzt. Die Vorschau gibt an, was geschwärzt wurde. Vom Terminal gehen höchstens die letzten 12.000 Zeichen ab.

Grenzen des Filters (auch in der Vorschau ausgewiesen). Er erkennt Geheimnisse an Form und bekanntem Wert: Er kann nicht ein im Klartext geschriebenes Passwort ohne verräterischen Namen erkennen, kein Geheimnis, das nicht im Tresor liegt, und auch keine sensiblen Informationen, die keine Geheimnisse sind (Hostnamen, IP-Adressen, Pfade, Dateiinhalte, personenbezogene Daten in der Ausgabe). Bei einem nicht lokalen Server verlässt all dies den Rechner. Deshalb ist die Vorschau zu lesen.

Programme, die die App startet (Erkennung der Server). Die Schaltfläche Starten lässt die App ein externes Programm ausführen und ist daher ein Punkt, der eng zu halten ist: Es werden nur ollama serve und lms server start|stop ausgeführt, gesucht mit festem Namen im PATH und in bekannten Pfaden; kein Befehl, Argument oder Pfad stammt vom Nutzer, von der Oberfläche oder vom Modell. Ollama wird mit erzwungenem OLLAMA_HOST=127.0.0.1:porta gestartet, sodass es nicht im Netzwerk lauschen kann; von der App gestartete Prozesse werden mit der App beendet, und Stoppen betrifft nur diese. Restrisiko: Wer bereits eine ausführbare Datei namens ollama in den PATH des Nutzers legen kann, bewirkt die Ausführung dieses Programms, wenn der Nutzer Starten drückt (gleiche Stufe wie bei jemandem, der die Umgebung des Nutzers bereits verändern kann, §2). Die Schaltfläche Herunterladen öffnet im Browser eine Seite, die aus einer festen Tabelle im Backend gewählt wird (HTTPS, drei offizielle Adressen): Die App lädt nichts herunter, installiert nichts und baut keine Verbindungen zum Internet auf; was heruntergeladen und installiert wird, entscheidet der Nutzer im Browser. Die Befehle von lms sind noch nicht an einem echten LM Studio erprobt.

Das Modell ist eine nicht vertrauenswürdige Instanz.

  • Feindliche Terminalausgabe (Prompt-Injection): Ein Server oder eine Datei kann Text ausgeben lassen, der dem Modell „Befehle erteilt“. Gegenmaßnahmen: Der Text wird von Trennzeichen umschlossen, die er nicht schließen kann (sie werden neutralisiert), und die Anweisungen besagen, ihn als Daten zu behandeln; das Modell hat weder Werkzeuge noch Zugriff auf das System und kann daher nur Text erzeugen. Es bleibt möglich, dass die Erklärung irreführend oder der vorgeschlagene Befehl schädlich ist.
  • Vorgeschlagener Befehl: Er wird nie ausgeführt. Er wird als Einfügen (ohne Eingabetaste) in den Prompt eingesetzt und vom Nutzer gestartet; die Oberfläche zeigt den Befehl in einem Feld an, warnt, wenn er mehrere Zeilen hat (die Shell unterstützt das geschützte Einfügen möglicherweise nicht), und entfernt jedes Steuerzeichen, auch ESC, damit eine feindliche Antwort das geschützte Einfügen nicht beenden und etwas anderes ausführen lassen kann. Das Restrisiko ist, dass der Nutzer die Eingabetaste drückt, ohne zu lesen.
  • Übermäßige oder langsame Antwort: Obergrenze von 256 KB je Antwort, Abbruch nach 120 s ohne Daten, höchstens 3 Anfragen gleichzeitig.

Sperren des Tresors. Laufende Anfragen werden abgebrochen, und noch nicht gesendete Vorschauen werden verworfen.

Nicht überprüft: Die Kompatibilität mit den echten Servern (Ollama, LM Studio, llama.cpp) wurde mit einem simulierten Server getestet, der die API von OpenAI nachahmt, nicht mit den echten Programmen; die Streaming-Formate können unvorhergesehene Varianten haben. Der Filter wurde nicht an echten, umfangreichen Ausgaben bewertet: Falsch-Positive (ohne Grund geschwärzter Text) sind möglich und Falsch-Negativen vorzuziehen.

6. Mit dieser Revision vorgenommene Korrekturen

#ProblemKorrekturNachweis
6.1Die Argon2id-Parameter von vault.json und der .shelleasy-Umschläge hatten nur ein Minimum: Feindliche Kopfdaten konnten vor der Passwortprüfung Gigabyte oder Stunden an Rechenzeit anfordernObergrenze: 16 Durchgänge, 1 GiB, 16 Threads (vault.MaxTime/MaxMemoryKiB/MaxThreads, Fehler ErrHugeParams), angewendet beim Anlegen, Entsperren, Export und ImportTestParamsCeiling, TestHugeParamsRejectedOnOpen: Die Ablehnung erfolgt in weniger als einer Sekunde, ohne Schlüssel abzuleiten
6.2Von einem SFTP-Server gelesene Dateinamen wurden ungeprüft zum Bilden lokaler Pfade verwendet (Verlassen des Zielordners unter Windows; reservierte Namen; alternative NTFS-Datenströme)unsafeName / safeReadDir / checkTopName: verworfen werden "", ., .., /, NUL und, unter Windows, \ : * ? " < > |, Steuerzeichen, Namen, die auf Punkt oder Leerzeichen enden, und Gerätenamen (CON, NUL, COM1…); die Verworfenen landen in den Hinweisen zur ÜbertragungTestUnsafeName, TestSafeReadDirSkipsHostileNames
6.3Lokale Dateien wurden beim Herunterladen mit den Berechtigungen 0644 angelegtAnlegen mit 0600; die Berechtigungen des Originals werden nach abgeschlossenem Kopieren angewendetTestLocalCreateIsPrivate

Die vollständige Go-Testsuite besteht auch mit -race. Nicht überprüft: das Verhalten von 6.2 unter echtem Windows (die Windows-Regeln sind anhand einer Tabelle geprüft, nicht auf einem Windows-Dateisystem).

7. Außerhalb des Tresors: was die App nicht schützt

  • Die Daten, die durch die Sitzungen laufen (Terminalausgabe, übertragene Dateien, Eingefügtes): Sie sind im gespeicherten Zustand nicht verschlüsselt und hängen vom Rechner und vom Server ab. Wird der KI-Assistent verwendet, verlässt der Text, dessen Senden beschlossen wird, den Tresor in Richtung des Modellservers (§5.13). Der Scrollback-Puffer des Terminals und die lokale Shell nutzen Speicher und Dateien des Betriebssystems.
  • Die heruntergeladenen Dateien bleiben auf dem Zieldatenträger mit den Schutzmechanismen dieses Datenträgers.
  • Die Zwischenablage des Systems: Ein kopiertes Geheimnis bleibt in der Zwischenablage, bis etwas anderes es ersetzt.
  • Die .shelleasy-Exporte sind während ihrer gesamten Lebensdauer nur durch ihr Passwort geschützt: Sie sind so aufzubewahren, wie Schlüssel aufbewahrt würden.

8. Externe Prüfung

Empfehlung: ja, vor Version 1.0 und bevor die App für produktive Zugangsdaten empfohlen wird, begrenzt auf einen kleinen, klaren Umfang:

  1. internal/vault, internal/envelope, internal/store und der Entsperrpunkt (internal/core/core.go): rund 1.000 Zeilen, eine Prüfung von wenigen Tagen.
  2. Die Verwaltung der Serverschlüssel (internal/sshconn/hostkey.go) und der Übertragungen (§5.7).
  3. Die biometrische Integration unter macOS (internal/biometric).

Ein Budget ist nicht vorgesehen: Kostenlose Alternativen sind ein Peer-Review durch Personen, die Go und Kryptografie kennen, die Veröffentlichung des Codes zusammen mit diesem Dokument, um Meldungen anzuziehen, und das Aktivieren der Sicherheitswarnungen von GitHub. Solange keine Prüfung erfolgt ist, lautet die ehrliche Formulierung: Kryptografie mit Standardprimitiven und dokumentierten Entscheidungen, nicht von Dritten überprüft.

9. Offene Follow-ups

  • Rotation der DEK beim Passwortwechsel (optional): Datenbank und Geheimnisse mit einer neuen DEK neu verschlüsseln, sodass eine alte Kopie von vault.json plus das alte Passwort nichts mehr öffnen. Das ist ein langwieriger und heikler Vorgang (atomare Kopie, Rollback im Fehlerfall).
  • Erkennen des Wiedereinspielens alter Kopien mit einem Anker im Schlüsselbund des Systems (§5.3).
  • Content-Security-Policy für die Webview (§5.11).
  • Automatische Prüfung der Abhängigkeiten (govulncheck, pnpm audit) in den Kontrollen (§5.12).
  • Signierung der Pakete und Überprüfung von AppImage/Windows.
  • Windows Hello und biometrisches Entsperren unter Linux.