# Wie Sie einen SSH-Helfer vor dem Start prüfen

Ein lokales Gateway muss seinen SSH-Helfer als privilegierten Code behandeln, auch wenn der Helfer keinen Tresor enthält und keinen Zustand speichert. Der Helfer erhält einen Befehl, erbt Dateideskriptoren und die Umgebung und kann zu dem Prozess werden, der mit dem entfernten Host spricht. Wenn ein Angreifer diese ausführbare Datei ersetzen kann, verlieren alle darüber erteilten Freigaben ihre Wirkung.

Ein sicherer Entwurf hat zwei verschiedene Aufgaben. Vorabprüfungen erkennen ein beschädigtes oder falsch installiertes Bundle, bevor eine Anfrage den Runner erreicht. Eine Code-Anforderung beim Start weist macOS an, das tatsächliche Prozessabbild abzulehnen, wenn seine Signaturidentität nicht stimmt. Wer diese Aufgaben verwechselt, schafft die bekannte Lücke, in der eine Anwendung eine Datei prüft und eine andere ausführt.

## Leiten Sie den Helfer aus dem laufenden Bundle ab

Bestimmen Sie den Helfer aus dem Bundle der Anwendung, die tatsächlich läuft, niemals aus `PATH`, dem aktuellen Verzeichnis, einer Einstellung oder einem vom Agenten gelieferten Pfad. Der erwartete Ort muss eine Konstante aus dem Build sein. Ein Gateway, das nach `sp-ssh` sucht, hat seine Umgebung bereits ausführbaren Code auswählen lassen.

Apple dokumentiert Standardorte für eingebetteten Code, darunter `Contents/MacOS` und `Contents/Helpers`. Wählen Sie einen Ort, legen Sie dort nur Code ab und lassen Sie den Build fehlschlagen, wenn der Helfer woanders landet. Signieren Sie zuerst den Helfer und zuletzt die äußere Anwendung. So kann die äußere Signatur den Verweis auf den eingebetteten Code versiegeln.

Foundation kann ein Hilfsprogramm finden, doch die Sicherheitsentscheidung muss weiterhin seine genaue Beziehung zum Haupt-Bundle prüfen. Lösen Sie symbolische Links auf, standardisieren Sie beide URLs und vergleichen Sie Pfadbestandteile statt Textpräfixen. Eine Prüfung wie `candidate.path.hasPrefix(bundle.path)` akzeptiert Nachbarn wie `/Applications/Good.app.backup` und kann bei Großschreibung oder Normalisierung versagen. Der Kandidat muss genau der einen erwarteten URL im laufenden Bundle entsprechen.

Akzeptieren Sie keinen neben die Anwendung kopierten Helfer als Ausweichlösung. Während der Entwicklung ist das verlockend, weil fehlerhafte Paketierung weniger stört. In Produktion verschiebt es die Vertrauensgrenze unbemerkt vom signierten Bundle-Inhalt zu einer beliebigen Datei an einem benachbarten Pfad. Entwicklungs-Builds sollten eine ausdrückliche Konfiguration verwenden und bei falscher Paketierung klar fehlschlagen.

Der Ort ist ein Hinweis auf die Paketierung, nicht auf die Identität. Ein Angreifer, der eine Datei in einem beschreibbaren Bundle ersetzen kann, kann denselben Pfad beibehalten. Deshalb untersuchen die nächsten Prüfungen das geöffnete Objekt und seine Signatur.

## Öffnen Sie zuerst und prüfen Sie dann die Dateiidentität

Öffnen Sie den Kandidaten mit `O_NOFOLLOW`, halten Sie den Deskriptor offen und rufen Sie `fstat` für diesen Deskriptor auf. Die Reihenfolge zählt. Wer `lstat` aufruft, das Ergebnis prüft und später `open` verwendet, gibt einem anderen Prozess die Möglichkeit, den Verzeichniseintrag zwischen den Operationen zu ersetzen.

Apples Secure Coding Guide empfiehlt aus diesem Grund deskriptorbasierte Operationen. Er verlangt nach dem Öffnen insbesondere Prüfungen von Typ, UID, GID, Modus und Anzahl der Links. Für einen ausführbaren Helfer verwende ich eine Vorabprüfung in dieser Form:

```c
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

int inspect_helper(const char *path, uid_t expected_uid, struct stat *snapshot) {
    int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
    if (fd < 0) return -1;

    struct stat st;
    if (fstat(fd, &st) != 0 ||
        !S_ISREG(st.st_mode) ||
        st.st_uid != expected_uid ||
        (st.st_mode & (S_IWGRP | S_IWOTH)) != 0 ||
        (st.st_mode & S_IXUSR) == 0 ||
        st.st_nlink != 1) {
        int saved = errno ? errno : EPERM;
        close(fd);
        errno = saved;
        return -1;
    }

    *snapshot = st;
    return fd;
}
```

`expected_uid` folgt aus dem Installationsmodell, nicht aus der Datei. Eine systemverwaltete Installation kann root als Eigentümer verlangen. Eine Installation pro Benutzer kann rechtmäßig diesem Benutzer gehören. Verlangen Sie UID 0 nicht nur deshalb, weil root vertrauenswürdig wirkt. Apple warnt außerdem, dass ein Pfad in ein anderes eingehängtes Dateisystem wechseln kann, in dem der Eigentümer allein weniger aussagt, als Entwickler oft annehmen.

Die Regel zur Link-Anzahl verlangt eine bewusste Entscheidung. Eine normale ausführbare Datei im Bundle hat üblicherweise einen Hardlink, daher ist die Ablehnung anderer Werte sinnvoll. Falls Ihr Paketsystem absichtlich Hardlinks erstellt, dokumentieren und testen Sie die erwartete Zahl. Wenn Sie die Prüfung entfernen, weil ein Build Sie überrascht hat, bleibt ein weiterer Name, über den derselbe Inode verändert werden kann.

Bewahren Sie Deskriptor und `stat`-Abbild bis zum Ende des Starts auf. Sie machen einen pfadbasierten Spawn nicht atomar, helfen aber dabei, einen Austausch zu erkennen, Gerät und Inode zu protokollieren und eine Ablehnung zu erklären, ohne einen vom Angreifer kontrollierten Namen erneut zu öffnen.

## Prüfen Sie jedes beschreibbare Verzeichnis der Kette

Ein perfekter Dateimodus schützt den Helfer nicht, wenn sein übergeordnetes Verzeichnis umbenannt oder verändert werden kann. Der Angreifer braucht keine Rechte zum Bearbeiten der ausführbaren Bytes, wenn er den Verzeichniseintrag ersetzen kann, der auf diese Bytes zeigt.

Gehen Sie mit Verzeichnisdeskriptoren vom Elternverzeichnis des Helfers bis zur Bundle-Wurzel. Lehnen Sie für jeden Bestandteil symbolische Links ab, bestätigen Sie den Verzeichnistyp, notieren Sie Gerät und Inode und wenden Sie die Eigentums- und Schreibregeln Ihres Installationsmodells an. `openat` und `fstatat` mit einem gehaltenen Elterndeskriptor sind besser als das wiederholte Auflösen absoluter Zeichenketten. Der Weg muss an der bereits geprüften Bundle-Wurzel enden, nicht an einem Pfad, der nur eine vertraute Endung trägt.

Bei benutzerbezogenen Installationen ist eine ehrliche Aussage nötig. Wenn derselbe Benutzer, der das Gateway ausführt, ein beschreibbares App-Bundle besitzt, kann ein anderer Prozess dieses Benutzers Bundle-Dateien ersetzen. Modus- und Eigentümerprüfungen entdecken eine unbeabsichtigte Freigabe für andere Konten, schützen aber nicht vor der vollständigen Übernahme des aktuellen Kontos. Die Signaturidentität bleibt wichtig, denn eine Ad-hoc-Signatur erfüllt Ihre vorgegebene Anforderung nicht.

Vergleichen Sie auch Geräte-IDs, wenn Ihre Richtlinie ein lokales Bundle auf einem einzigen Volume verlangt. Apple weist darauf hin, dass ein Pfad eine Mount-Grenze überschreiten kann. Unerwartete Grenzen abzulehnen ist nützlich, beweist aber nicht die Vertrauenswürdigkeit eines Volumes. Es beweist nur, dass das Objekt nicht an dem von der Paketierung zugesagten Ort liegt.

Verzeichnisprüfungen finden erstaunlich alltägliche Bereitstellungsfehler: Ein Updater hinterlässt ein für die Gruppe beschreibbares Zwischenverzeichnis, ein Helfer wird außerhalb der signierten Anwendung wiederhergestellt oder ein Entwicklungsskript fügt einen symbolischen Link ein. Solche Fehler müssen den Runner stoppen, bevor er einen Befehl erhält. Mit einer Warnung fortzufahren macht aus einem Paketierungsfehler eine Logik zur Auswahl ausführbarer Dateien.

## Prüfen Sie Identität und nicht nur Gültigkeit

Eine gültige Signatur beantwortet, ob der Code weiterhin zu dieser Signatur passt. Sie sagt nicht, ob Ihr Team ihn signiert hat oder ob die Datei Ihr Helfer ist. Unter macOS kann Code ad hoc signiert sein, und ein anderer Entwickler kann Code mit einer vollständig gültigen Signatur herstellen.

Verwenden Sie Code Signing Services mit einer ausdrücklichen Anforderung, die den erwarteten Signaturbezeichner und das Team für den Vertriebskanal nennt. Apple TN3127 trennt die Begriffe klar: Der Code-Signaturbezeichner ist ein vom Unterzeichner gewählter Name, die Signaturidentität enthält Zertifikat und privaten Schlüssel, und die designierte Anforderung bestimmt, was über Versionen hinweg als derselbe Code gilt. Der Vergleich der angezeigten Zeichenkette `Authority` ist eine Diagnosehilfe, keine Sicherheitsgrenze.

Erstellen Sie einen `SecStaticCode` für die absolute Helfer-URL, kompilieren oder laden Sie die Anforderung und rufen Sie `SecStaticCodeCheckValidityWithErrors` auf. Nehmen Sie `kSecCSStrictValidate` und `kSecCSCheckAllArchitectures` auf. Apple dokumentiert, dass standardmäßig möglicherweise nur die native Architektur einer universellen Binärdatei geprüft wird. Die Prüfung aller Slices verhindert eine andere oder beschädigte Signatur in einer ungeprüften Architektur.

Verwenden Sie für diese Aufgabe nicht `kSecCSBasicValidateOnly`. Dieses Flag überspringt die Validierung des Hauptprogramms und der Ressourcen und vereitelt damit die Integritätsprüfung. Vertrauen Sie auch nicht der eigenen designierten Anforderung des Helfers, ohne sie mit einer vom Gateway kontrollierten Anforderung zu vergleichen. Eine Selbstbeschreibung ist keine Autorisierung.

Auch die äußere Anwendung sollte geprüft werden. Apple definiert Standardorte für eingebetteten Code, damit das Signatursystem Hilfsprogramme als Code behandelt. Eine korrekt signierte Veröffentlichung bildet eine Kette der Absicht: Der Helfer erfüllt seine erwartete Anforderung, und das Siegel der äußeren Anwendung erfasst die eingebettete Komponente. Beide Ebenen zu prüfen erkennt einen eigenständig gültigen Helfer, der in ein beschädigtes Bundle versetzt wurde.

Bewahren Sie den ausführlichen `CFError` intern auf und ordnen Sie ihn wenigen Ablehnungskategorien für Benutzer zu: fehlende Signatur, nicht erfüllte Anforderung, ungültige Ressource, nicht unterstützte Architektur oder veränderte Datei. Machen Sie aus einem Fehler niemals einen erneuten Versuch über einen anderen Pfad. Ein Signaturfehler bedeutet, dass der Runner nicht verfügbar ist.

## Eine statische Prüfung schließt das Startrennen nicht

Statische Validierung gilt nur, solange die Datei unverändert bleibt. Apple sagt das in der Dokumentation zu `SecStaticCodeCheckValidity` ausdrücklich und nennt dynamische Netzwerk-, Union- und FUSE-Dateisysteme. Die Warnung betrifft auch einen gewöhnlichen ersetzbaren Verzeichniseintrag: Nach der Validierung kann ein Prozess eine andere ausführbare Datei über den geprüften Pfad umbenennen, bevor `posix_spawn` ihn auflöst.

Der Fehler besteht aus vier Schritten:

1. Das Gateway löst `/Applications/Example.app/Contents/Helpers/runner` auf und prüft Datei A.
2. Ein konkurrierender Prozess benennt Datei B auf diesen Pfad um.
3. Das Gateway fordert `Process` oder `posix_spawn` zur Ausführung des Pfads auf.
4. Der Kernel öffnet B, weil der Aufruf einen Namen und nicht den für A gehaltenen Deskriptor erhielt.

Ein Vergleich von `stat` vor und nach der Prüfung verkleinert das Fenster und erkennt viele Versuche, macht die Schritte zwei und drei aber nicht unteilbar. Ein selbst berechneter Hash hat dieselbe Grenze und wiederholt die Arbeit der Signatur. Eine Sperrdatei koordiniert nur Prozesse, die sich daran halten. Ein Angreifer tut das nicht.

Ein verbreiteter Rat lautet, einmal beim App-Start zu validieren und den Erfolg zu speichern. Er ist beliebt, weil Signaturprüfungen Aufwand verursachen und eingebetteter Code im normalen Betrieb unveränderlich erscheint. Für ein Gateway ist er falsch. Updates, Wiederherstellungen, Volume-Wechsel und absichtliche Ersetzungen können stattfinden, während eine Menüleisten-App weiterläuft. Speichern Sie bei Bedarf das kompilierte Anforderungsobjekt, wie Apple empfiehlt, aber bewerten Sie die ausführbare Datei bei jedem Start erneut.

Ein offener Deskriptor hilft trotzdem. Halten Sie ihn während der Validierung, führen Sie unmittelbar vor dem Spawn erneut `fstat` aus und lehnen Sie Änderungen an Gerät, Inode, Größe, Änderungszeit oder Metadatenzeit ab. Erstellen Sie nach der Prozesserzeugung ein drittes Abbild für die Telemetrie. Die Vergleiche verbessern die Diagnose und erschweren den Angriff, aber die Aussage muss genau bleiben: Nur eine beim Start vom Betriebssystem angewandte Anforderung bindet Identität und Prozesserzeugung zusammen.

## Binden Sie die Anforderung an die Prozesserzeugung

Setzen Sie auf Systemen mit LightweightCodeRequirements vor `run` die Eigenschaft `Process.launchRequirement`. Apple erklärt, dass das Betriebssystem den Prozess nicht ausführt und einen Absturzbericht erstellt, wenn das Programm die `LaunchCodeRequirement` nicht erfüllt. Die entscheidende Identitätsprüfung wird Teil der Prozesserzeugung, sodass die Pfadauswahl nicht mehr von der Entscheidung abweichen kann.

Die wesentliche Swift-Konfiguration ist kurz:

```swift
import Foundation
import LightweightCodeRequirements

func configuredProcess(helper: URL, team: String, identifier: String) throws -> Process {
    let requirement = try LaunchCodeRequirement.allOf {
        ValidationCategory(.developerID)
        TeamIdentifier(team)
        SigningIdentifier(identifier)
    }

    let process = Process()
    process.executableURL = helper
    process.launchRequirement = requirement
    return process
}
```

Team und Bezeichner müssen aus der Release-Konfiguration stammen, die in das signierte Gateway kompiliert wurde. Laden Sie sie nicht aus Einstellungen neben dem Helfer. Wenn Sie über mehrere Signaturkanäle veröffentlichen, erstellen und testen Sie für jeden unterstützten Kanal eine ausdrückliche Anforderung, statt einen Ausdruck so weit zu lockern, bis jeder Build akzeptiert wird.

Wenden Sie die Anforderung auf einen Mach-O-Helfer an, nicht auf ein Shell-Skript. Apple weist darauf hin, dass die Startanforderung bei einem Shebang-Skript den Interpreter bewertet. Der Nachweis, dass `/bin/bash` Apple-Code ist, sagt nichts über die Skriptbytes aus. Legen Sie die Logik in signierten ausführbaren Code, oder behandeln Sie das Skript als versiegelte Daten, die vertrauenswürdiger Code intern verarbeitet, ohne es als privilegierten Runner zu starten.

Aktivieren Sie die API nach SDK-Verfügbarkeit und behalten Sie die statische Vorabprüfung für Diagnosen. Auf älteren Zielen kann `POSIX_SPAWN_START_SUSPENDED` ein Kind anhalten, bevor es Code im Benutzerraum ausführt. Holen Sie einen dynamischen `SecCode` über die PID, prüfen Sie die Anforderung und setzen Sie den Prozess dann fort oder beenden Sie ihn. Dieser Ersatzweg ist empfindlich: Prüfen Sie jeden Rückgabewert, verhindern Sie PID-Verwechslungen, schließen Sie unbeabsichtigte Deskriptoren und senden Sie vor dem Erfolg weder Befehl noch Zugangsdaten. Wenn Ihr Bedrohungsmodell diese Komplexität nicht akzeptiert, verlangen Sie eine Systemversion mit Startanforderungen.

## Starten Sie mit einem engen Prozessvertrag

Die Prüfung der Binärdatei bereinigt nicht ihre Eingaben. Bauen Sie den Argumentvektor aus strukturierten Feldern, setzen Sie eine ausdrückliche Umgebung, wählen Sie das Arbeitsverzeichnis bewusst und übergeben Sie nur die benötigten Deskriptoren. Rufen Sie nie eine Shell auf, um einen SSH-Befehl zusammenzusetzen.

Beginnen Sie mit einer leeren Umgebung oder einer Positivliste. Variablen für dynamisches Laden, Konfigurationssuche, Gebietsschema, Proxys oder das Home-Verzeichnis können das Verhalten eines signierten Programms ändern, ohne seinen Code zu ändern. Hardened Runtime und Library Validation verringern manche Ladeangriffe, machen aber keine beliebige geerbte Umgebung sicher.

Behandeln Sie Standardeingabe und Standardausgabe als Protokoll. Legen Sie maximale Nachrichtengrößen fest, lehnen Sie zusätzliche Felder ab, setzen Sie eine Frist und unterscheiden Sie Protokollfehler von SSH-Exitstatus. Ein zustandsloser Helfer sollte Schlüssel weder aus Umgebungsvariablen noch aus Argumenten, temporären Dateien oder Agentenpfaden lesen. Er sollte über einen kontrollierten Kanal die kleinste Anfrage erhalten und das kleinste Ergebnis zurückgeben.

Schließen Sie alle fremden Deskriptoren. `O_CLOEXEC` hilft bei Dateien der Vorabprüfung, doch prüfen Sie auch den Rest der Anwendung. Ein Kind, das den Deskriptor der Tresordatenbank, einen IPC-Listener oder eine Protokolldatei erbt, erhält Zugriff, den die Signaturprüfung nie gewähren sollte. Setzen Sie Ressourcenlimits, wenn der Vertrag es erlaubt, und beenden Sie bei Abbruch oder Zeitüberschreitung die gesamte Kindprozessgruppe.

Protokollieren Sie die Entscheidungsdaten ohne Geheimnisse: Bundle-relativen Ort, Version der Signaturanforderung, Geräte- und Inode-Abbilder, Prüfergebnis, Kind-PID und Beendigungsgrund. Der Eintrag muss aussagen, welcher Programmstart autorisiert war. Ein reiner Textpfad verliert die Tatsache, dass derselbe Name während eines Vorfalls verschiedene Dateien bezeichnet haben kann.

## Platzieren Sie die Prüfung in der Autorisierung

Die Annahme des Helfers gehört in dieselbe Transaktion wie die Autorisierung der Aktion und muss abgeschlossen sein, bevor das Gateway dem Kind eine Fähigkeit gibt. Beim Öffnen der App zu prüfen ist zu früh. Nach menschlicher Freigabe des Befehls zu prüfen ist zu spät, wenn ein Fehler den Befehl preisgeben, einen Socket öffnen oder einen Ersatzweg aktivieren kann.

Modellieren Sie einen Start als Zustandsmaschine mit ausdrücklichen, unumkehrbaren Grenzen. Das Gateway empfängt und analysiert eine Aktion, solange die Anfrage keinen Zugriff auf Zugangsdaten hat. Es bestätigt die Verfügbarkeit des Tresors, löst den Helfer auf und prüft ihn, holt die nötige menschliche Freigabe ein und startet mit angehängter Code-Anforderung. Erst nach dem Erfolg erstellt es den kleinsten Zugangsdaten- oder Verbindungskanal. Ein Fehler in einem früheren Zustand vernichtet die wartende Aktion.

Die Position der Freigabe hängt von ihrer Bedeutung ab. Fragt eine Karte, ob ein Agent einen bestimmten SSH-Befehl ausführen darf, kann sie vor dem aufwendigeren Start erscheinen. Der genehmigte Befehl muss danach unveränderlich bleiben, und ein Prüffehler muss die Freigabe verbrauchen oder aufheben, statt sie für eine spätere Binärdatei vorzumerken. Drückt die Freigabe Vertrauen in den ausführenden Prozess aus, zeigen Sie sie erst nach bekannter Kandidatenidentität. Beide Entwürfe funktionieren, doch der Audit-Eintrag muss Anfrage-Hash, Entscheidung, Signaturanforderung und Kindprozess zu einem Versuch verbinden.

Geben Sie kein Geheimnis frei, nur weil die Prozesserzeugung Erfolg meldet. Bereiten Sie Pipes oder Sockets vorher vor, wenn die API es verlangt, halten Sie das Ende mit Zugangsdaten aber geschlossen oder logisch gesperrt. Warten Sie auf die erfolgreiche Startdurchsetzung, notieren Sie die Identität und senden Sie erst dann die Anfrage. Benötigt das Kind eine SSH-Schlüsseloperation, ist eine enge Signatur- oder Verbindungsschnittstelle besser als das Kopieren privater Schlüsselbytes in seinen Speicher. Je weniger Befugnis der Helfer erhält, desto kleiner sind die Folgen eines vorherigen Fehlers.

Ein Abbruch verlangt dieselbe Sorgfalt. Ein Benutzer kann eine Sitzung während der Prüfung widerrufen, oder der Agent kann sich zwischen Freigabe und Start trennen. Prüfen Sie die Autorisierungsgeneration unmittelbar vor dem Start und erneut vor Freigabe der Anfrage. Hat sie sich geändert, beenden Sie das Kind und schließen Sie die Kanäle. Das ist kein Dateisystemrennen, aber eine weitere Lücke zwischen Prüfung und Nutzung derselben Entscheidung.

Parallele Starts dürfen keine veränderliche Konfiguration teilen. Jeder Versuch braucht eine eigene unveränderliche Helfer-URL, Anforderungsreferenz, Argumentliste, Umgebung, Deskriptoren, Frist und Audit-ID. Eine globale `Process`-Vorlage, die ein Thread ändert, während ein anderer `run` aufruft, kann eine genehmigte Anfrage an das falsche Programm oder die falsche Umgebung senden, obwohl beide Binärdateien gültig sind. Synchronisieren Sie nur den kurzen Übergang, der die Freigabe verbraucht und den Prozess startet, nicht die gesamte SSH-Verbindung.

Eine nützliche Invariante lautet: Kein vom Kind kontrolliertes Byte gelangt in den privilegierten Zustand des Gateways, bevor die Identität durchgesetzt ist. Der Helfer kann sofort in eine Pipe schreiben. Lassen Sie den Parser bis zur Annahme getrennt, begrenzen Sie den Puffer und behandeln Sie frühe Ausgabe als Protokollverletzung. Verwenden Sie auch keine Fehlermeldung eines ungeprüften Kindes, um privilegierte Pfade zu bauen, Zugangsdaten zu wählen oder ein anderes Programm auszuprobieren.

Diese Reihenfolge erzeugt außerdem ein klareres Journal. Ein Versuch kann `request_received`, `candidate_preflight_passed`, `authorization_granted`, `launch_requirement_passed`, `request_released` und das Endergebnis zeigen. Fehlende Übergänge fallen auf. Ein Eintrag, der von der Freigabe direkt zum SSH-Exitcode springt, lässt offen, ob der erwartete Runner den Befehl je verarbeitet hat.

## Prüfen Sie den tatsächlich gestarteten Kindprozess

Die Durchsetzung beim Start sollte die entscheidende Schranke sein, doch Beobachtung danach erkennt Integrationsfehler und liefert eine stabile Prozessidentität. Erfassen Sie nach erfolgreichem `run` die PID und holen Sie einen dynamischen `SecCode` für den laufenden Prozess. Prüfen Sie ihn mit der entsprechenden Prozessanforderung, bevor Sie den Protokollparser verbinden oder sensible Eingaben freigeben.

Apples Unterscheidung zwischen `SecStaticCode` und `SecCode` ist hier hilfreich. Ein statisches Objekt beschreibt Code auf dem Datenträger und ist nicht von sich aus mit laufendem Code verbunden. Ein dynamisches Codeobjekt stellt den in einen Prozess geladenen Code dar. Die Prüfungen beantworten verschiedene Fragen: Die statische erklärt die Integrität des installierten Kandidaten, die dynamische bestätigt die von macOS dem aktuellen Kind zugeordnete Identität.

Eine Suche nur nach PID hat scharfe Kanten. PIDs werden wiederverwendet, und ein kurzlebiges Kind kann zwischen `run`, Suche und Validierung enden. Kann das Codeobjekt nicht erhalten oder geprüft werden, muss der Start als fehlgeschlagen gelten. Wenn eine IPC-API einen Audit-Token liefert, verwenden Sie diese stärkere Referenz statt einer PID aus einer Nachricht. Vertrauen Sie dem Kind nie bei Angaben zu eigener PID, Signaturkennung oder Programmpfad.

Die Nachprüfung darf auf einer Plattform, auf der das Kind vorher CPU-Zeit erhält, nicht die einzige Prüfung sein. Ein Ersatzprozess kann zwischen Spawn und Untersuchung handeln. Ein angehaltener Start vor Benutzerraumcode verkleinert die Lücke auf älteren Systemen, aber die Umsetzung darf nur die geprüfte PID fortsetzen und muss sie auf jedem Fehlerpfad beenden. Die Startanforderung ist einfacher, weil das System Abweichungen vor der Ausführung ablehnt.

Bewahren Sie nach der Annahme ein Prozess-Handle auf und binden Sie jede Protokollnachricht an diese Instanz. Öffnen Sie keinen benannten Socket erneut und verbinden Sie sich nicht mit einem Helfer-Endpunkt, den ein anderer Prozess besetzen könnte. Schließen Sie beim Ende alle Kanäle, machen Sie die Aktion ungültig und verlangen Sie für den nächsten Prozess einen neuen geprüften Start. Ein gültiges erstes Kind autorisiert kein späteres, das zufällig dieselbe PID oder denselben Namen erhält.

Protokollieren Sie Belege für installiertes Objekt und laufenden Prozess. Für das Objekt: relativen Pfad, Gerät, Inode, Größe, Zeitstempel und statisches Prüfergebnis. Für den Prozess: PID, Revision der Signaturanforderung, Ergebnis der Startdurchsetzung, dynamische Validierung, Startzeit und Endstatus. Diese Werte sind keine Geheimnisse und unterscheiden eine beschädigte Version, einen Update-Konflikt, eine fehlerhafte Anforderung und einen Austauschversuch, ohne Befehle oder Zugangsdaten auszugeben.

Die Prozessidentität braucht auch eine Lebensdauerregel. Führt der Helfer ein anderes Programm aus, geht die geprüfte Identität nicht automatisch auf das neue Abbild über. Vermeiden Sie einen signierten Wrapper, der korrekt geprüft wird und danach ein beliebiges `ssh` über `PATH` aufruft. Gehört ein exec-Übergang zum Entwurf, braucht das endgültige Programm einen festen Pfad und eine eigene erzwungene Anforderung, oder der vertrauenswürdige Helfer muss das Protokoll selbst ausführen. Die Signatur des Starters sagt nichts über das später gewählte Programm.

Achten Sie auch auf Bibliotheken und Konfiguration. Das laufende Hauptprogramm kann die Anforderung erfüllen, während unsichere Umgebungsvariablen oder beschreibbare Suchorte sein Verhalten verändern. Signieren Sie Abhängigkeiten, aktivieren Sie geeignete Hardened-Runtime-Optionen, verwenden Sie bei Kompatibilität Library Validation und entfernen Sie Suchpfade aus der Umgebung. Konfiguration sollte als vom Gateway geprüfte Eingabe kommen und nicht als vom Kind entdeckte Datei im Home-Verzeichnis.

Testen Sie schließlich die Aussagen Ihrer Telemetrie. In einem Austauschtest muss das statische Abbild A identifizieren, die Startdurchsetzung A ausführen oder B ablehnen und die dynamische Prüfung damit übereinstimmen. Kann das Protokoll A als bestanden melden, während ein nicht erfasstes B lief, folgt das Beweismodell noch dem Namen statt dem Prozess.

## Lehnen Sie sicher ab, ohne Updates zu verhindern

Jeder Prüffehler muss diesen Runner stoppen und den Rest der Anwendung in einem verständlichen Zustand lassen. Weichen Sie nicht auf das systemweite `ssh` aus, suchen Sie kein anderes Verzeichnis, entfernen Sie nicht die Startanforderung und bitten Sie Benutzer nicht um die Freigabe eines unbekannten Helfers. Eine Freigabe repariert keine Programmidentität.

Updates brauchen einen eigenen Zustandsübergang. Nehmen Sie keine neue SSH-Arbeit an, lassen Sie aktive Kinder enden oder beenden Sie sie, installieren Sie das vollständige signierte Bundle mit einem atomaren Austausch und wiederholen Sie alle Bundle-, Datei- und Signaturprüfungen. Helfer und Anwendung aus verschiedenen Versionen können jeweils gültig signiert sein und trotzdem ihren Protokollvertrag brechen. Tauschen Sie nach dem Start die Protokollversion aus und lehnen Sie Unterschiede ab, bevor Sie eine Aktion senden.

Entscheiden Sie, welche Änderungen eine sichtbare Wiederherstellung erfordern. Ein fehlender Helfer nach einem unvollständigen Update verlangt eine Neuinstallation. Ein falsches Team oder ein falscher Bezeichner kann Manipulation bedeuten und verdient eine deutlichere Meldung. Ein anderer Modus kann von einem Sicherungsprogramm stammen. Bewahren Sie intern den genauen Fehler auf und bieten Sie extern eine knappe Handlung an, die nicht zum Umgehen der Prüfung erzieht.

Testen Sie Austauschverhalten, nicht nur korrekte Signaturen. Die Release-Suite sollte einen symbolischen Link am Helferpfad, einen zweiten Hardlink, gruppenbeschreibbare Eltern, einen ad hoc signierten Ersatz, eine korrekt mit falschem Bezeichner signierte Datei, eine beschädigte nicht native Architektur, den Austausch zwischen Vorprüfung und Start sowie ein Update bei laufender App abdecken. Ein Test-Hook, der nach der statischen Prüfung pausiert, macht das Rennen reproduzierbar.

Sallyport verwendet für seinen SSH-Kanal einen gebündelten zustandslosen Go-Helfer, während der Tresorkern in der signierten Menüleisten-App bleibt. Diese Trennung hält Geheimnisse aus dem Helfer heraus, macht seine Prüfung aber nicht optional: Das Gateway muss beweisen, welcher Befehls-Runner startet, bevor es eine genehmigte Aktion übergibt.

Die Annahmeregel sollte in eine Zeile der Entwurfsprüfung passen: Die erwartete Datei im laufenden Bundle besteht Deskriptor- und Verzeichnisprüfungen, erfüllt die Release-Signaturanforderung, und die Prozesserzeugung erzwingt dieselbe Identität. Kann die Plattform den letzten Punkt nicht garantieren, dokumentieren Sie die schwächere Zusage und verringern Sie, was das Kind erhalten darf.
