Sicherheitsdesign für Auto-Updates - Warum HTTPS allein nicht reicht
· Go Komura · Windows-Entwicklung, Sicherheit, Updater, Auto-Update, Signatur, MSIX, ClickOnce
Inhaltsverzeichnis
- Vorab das Fazit
- Warum Auto-Update gefährliches Terrain ist
- Anti-Patterns
- Best Practices
- Minimale sichere Konfiguration
- Überlegungen für Windows-Projekte
- Mindest-Checkliste
- Zusammenfassung
- Quellen
1. Vorab das Fazit
Zunächst die praktischen Eckpunkte im Überblick.
- Wenn die Anforderungen passen, zuerst auf eine bestehende Update-Infrastruktur wie MSIX App Installer oder ClickOnce setzen
- Wird ein eigener Updater benötigt, sollte zuerst nicht die UI, sondern Signaturverifikation und Wiederherstellung im Fehlerfall implementiert werden
- Update-Informationen wie
latest.jsonals signierte Metadaten behandeln, nicht als unsignierte Konfigurationsdatei - TLS ist notwendig, aber keine hinreichende Bedingung
- Die Update-Entscheidung nicht auf „der Server sagt es“ stützen, sondern auf „der Client hat verifiziert und als korrekt beurteilt“
- Signaturschlüssel für Entwicklung und Produktion trennen und durch HSM oder einen Signaturdienst schützen
- Bei Update-Fehlern Fail-Closed statt Fail-Open verwenden
- Ein Updater ohne Rollback-Schutz sollte so behandelt werden, als könnte er jederzeit auf eine verwundbare Version zurückgesetzt werden
- Solange Signaturverifikation noch nicht implementiert ist, ist die manuelle Verteilung signierter Installer sicherer als Auto-Update
Zunächst die Begriffe fixieren; im Folgenden werden sie in diesem Sinn verwendet.
| Begriff | Bedeutung |
|---|---|
| Fail-Closed | Design, das im Fehlerfall auf der „Stopp“-Seite landet. Schlägt die Signaturverifikation fehl, wird das Update nicht fortgesetzt |
| Fail-Open | Design, das auch im Fehlerfall auf der „Weiter“-Seite landet. Es wird nur eine Warnung ausgegeben, das Update läuft aber weiter |
| Staging | Verfahren, bei dem die neue Version zunächst an einem separaten Ort bereitgestellt und erst nach abgeschlossener Verifikation umgeschaltet wird |
| Kill Switch | Mechanismus, um ein laufendes Update-Rollout serverseitig sofort zu stoppen |
| Trust Anchor | Der Ausgangspunkt, dem der Client von vornherein vertraut — ein Root-Public-Key oder eine fixierte Zertifikatskette |
Kurz gesagt: Der Kern von Auto-Update ist nicht „wie wird heruntergeladen“, sondern „wem wird vertraut, wo wird verifiziert und wie wird im Fehlerfall zurückgekehrt“.
2. Warum Auto-Update gefährliches Terrain ist
Gewöhnliche Funktionen bleiben innerhalb der App gekapselt. Ein Updater dagegen vereint auf einen Schlag drei Dinge.
- Er holt Dateien von außen
- Er vertraut diesen Dateien
- Er ersetzt bestehende ausführbare Dateien
Mit anderen Worten: Ein Pfad zur Ausführung beliebigen Codes ist von Anfang an im Produkt eingebaut.
Ein verbreitetes Missverständnis lautet: „Es ist HTTPS, also ist es sicher.“ TLS ist natürlich notwendig. Es schützt jedoch vor allem den Übertragungsweg und die Legitimität der Gegenstelle. Wenn der Update-Server selbst kompromittiert wurde, ein falsches Artefakt auf dem legitimen CDN landet oder ein unsigniertes Manifest ausgetauscht wird, reicht das allein nicht aus.
Betrachtet man allein die von TUF (The Update Framework, eine Spezifikation, die das Vertrauensmodell für Software-Updates definiert) katalogisierten Bedrohungen, stehen Update-Systeme vor all dem hier.
- Beliebige bösartige Software einschleusen lassen
- Rollback-Angriffe, die auf eine verwundbare alte Version zurückstufen
- Freeze-Angriffe, die neue Versionen verbergen
- Mix-and-Match-Angriffe, die inkonsistente Metadaten und Artefakte kombinieren
Das heißt: Auto-Update ist nicht „Dateiübertragung“, sondern „Verteilung von Vertrauen“. Erst wenn man dies durchdenkt, läuft Auto-Update sicher.
2.1 Zuordnung von Bedrohung und Gegenmaßnahme
Bedrohungen und Gegenmaßnahmen sind in diesem Artikel auf verschiedene Kapitel verteilt. Vorab die Zuordnung in einer Übersicht — das Grundgerüst des Designs ist damit abgedeckt.
| Bedrohung | Was passiert | Reicht TLS allein? | Wesentliche Gegenmaßnahme | Ausführliches Kapitel |
|---|---|---|---|---|
| Verteilung eines bösartigen Artefakts | Eine vom Angreifer vorbereitete Datei wird als reguläres Update installiert | Nein (wirkungslos bei Origin-Kompromittierung, Fehlverteilung) | Signierte Metadaten und clientseitige Verifikation von Hash / Signatur des Artefakts | 4.2 / 4.3 / 4.4 |
| Rollback | Die Signatur ist korrekt, aber es wird auf eine ältere Version mit bekannter Schwachstelle zurückgestuft | Nein (Signatur und TLS sind beide korrekt) | Höchste bekannte Release-Version behalten, Älteres zurückweisen | 4.8 |
| Freeze | Eine neue Version ist verfügbar, aber es werden weiterhin alte Metadaten geliefert, sodass kein Update erfolgt | Nein | expires_at in den Metadaten, zu Altes zurückweisen |
4.3 / 4.8 |
| Mix-and-Match | Eine inkonsistente Kombination aus Metadaten und Artefakt wird ausgeliefert | Nein | Hash / Größe / Version des Ziel-Artefakts im Manifest fixieren | 4.3 / 4.8 |
| Kompromittierung des Signaturschlüssels | Ein regulär signiertes, aber bösartiges Update wird verteilt | Nein | Trennung von Entwicklungs- und Produktionsschlüssel, HSM oder Signaturdienst, Freigabeprozess und Audit-Log, Trennung von Root- und Metadatenschlüssel | 4.5 |
| Fehler / Abbruch beim Update | Der Austausch bricht mittendrin ab, die App startet nicht mehr | Nicht anwendbar | Staging + atomare Aktivierung + Rollback | 4.6 |
| Umgehung der Verifikation | Ein Hintertürchen wie skipVerify bleibt in der Produktion |
Nicht anwendbar | Fail-Closed als feste Vorgabe, kein Umgehungs-Flag | 3.7 / 4.6 |
| Kein Stoppen im Ernstfall möglich | Eine problematische Version wird weiter verteilt | Nicht anwendbar | Blocklist, Minimum Allowed Version, Kill Switch | 4.8 / 7 |
Dass die Spalte „Reicht TLS allein?“ durchgehend „Nein“ oder „Nicht anwendbar“ ergibt, ist die Kernaussage dieses Artikels. TLS schützt den Übertragungsweg, aber den Inhalt dessen, was verteilt wird, und die Berechtigung zur Verteilung schützt ein anderer Mechanismus.
3. Anti-Patterns
Zunächst eine Zusammenfassung gefährlicher Muster, die in der Praxis häufig vorkommen.
| Anti-Pattern | Warum gefährlich | Mindestmaßnahme |
|---|---|---|
version.json per HTTPS abrufen und das zip / exe der URL unverändert ausführen |
Anfällig für Origin-Kompromittierung, Konfigurationsmanipulation, Fehlverteilung | Auf clientseitige Verifikation signierter Metadaten und Artefakte umstellen |
| Nur die Binärdatei ist signiert, das Manifest unsigniert | URL, Version, Channel und Pflicht-Update-Flag können manipuliert werden | Signiertes Manifest mit Version / Hash / Größe / Channel / Ablaufdatum verwenden |
| Signaturschlüssel liegt auf einem Entwickler-PC oder in CI-Dateien | Bei Kompromittierung kann regulär signierte Malware verteilt werden | HSM / Signaturdienst + Freigabeprozess + Audit-Log |
| Bei Update-Fehlern „Verifikationsfehler ignorieren und fortsetzen“ | Genau im Ernstfall öffnet sich der schwächste Pfad | Fail-Closed |
| Überschreibendes Update ohne Erhalt der alten Version | Stromausfall, Speicherplatzmangel oder Abbruch führen zu einer nicht mehr startenden App | Staging + atomare Aktivierung + Rollback |
| Alte Versionen allein per Versionsvergleich zulassen | Rollback auf eine verwundbare Version wird durchgelassen | Monoton steigende Release-Version und Speichern der höchsten bekannten Version |
| Gesamten Updater mit Administratorrechten ausführen | Großer Schadensradius bei Kompromittierung | Download/Verifikation mit niedrigen Rechten, nur den Austausch in einen minimalen Helper auslagern |
| Mit Delta-Updates beginnen | Implementierung ist komplex, Verifikationslücken häufen sich | Zuerst mit vollständigen Paket-Updates beginnen |
Im Folgenden etwas ausführlicher.
3.1 Es bleibt bei „HTTPS, also ist es in Ordnung“
Das ist der häufigste Fall.
- Beim Start
latest.jsonlesen downloadUrlextrahieren- zip / exe herunterladen
- Entpacken und ersetzen
- Fertig
Das sieht plausibel aus, aber die Vertrauensbasis stützt sich zu stark auf die Server-Antwort. Ist der Update-Server oder die Verteilungskonfiguration kompromittiert, kann ein bösartiges Update über korrektes HTTPS verteilt werden.
TLS ist notwendig. Aber TLS allein macht das Design eines Updaters nicht vollständig.
3.2 Es wird signiert, aber der Client verifiziert nicht
Selbst wenn Dateien beim Release signiert werden, bringt das nichts, wenn der Client die Signatur nie prüft.
Ein häufiges Muster:
- CI signiert
- Der Updater prüft aber nur einen Hash
- Und dieser Hash selbst stammt aus einem unsignierten Manifest
In diesem Fall wird beim Austausch des Manifests der Hash gleich mit ausgetauscht. „Wir prüfen den Hash, also ist es sicher“ gilt nur, wenn auch die Herkunft des Hashes geschützt ist.
3.3 Das Manifest ist unsigniert
Was ein Update-System wirklich schützen muss, ist nicht nur die ausführbare Datei selbst. Mindestens die folgenden Informationen sind bei Manipulation gefährlich.
- Version / Release-ID
- URL oder Dateiname des Download-Ziels
- Hash / Größe
- Channel (stable / beta usw.)
- ob das Update verpflichtend ist
- anwendbares OS / Architektur
- Ablaufdatum der Metadaten
- minimal benötigte Updater-Version
Das heißt: Alles, was für die Update-Entscheidung verwendet wird, gehört in signierte Metadaten — diese Faustregel passt gut.
3.4 Nachlässiger Umgang mit dem Signaturschlüssel
Die Sicherheit einer Update-Funktion ist zu einem großen Teil die Sicherheit der Schlüsselverwaltung.
Liegt der Produktions-Signaturschlüssel in einer der folgenden Formen vor, ist das ziemlich gefährlich.
- Er verbleibt im Zertifikatspeicher eines Entwickler-PCs
- Eine
.pfxwird als CI-Secret hochgeladen - Derselbe private Schlüssel wird lokal an mehrere Personen verteilt
- Entwicklungs- und Produktionssignatur teilen sich dieselbe Vertrauenskette
In diesem Fall lässt sich ein „regulär signiertes, aber bösartiges Update“ nicht verhindern, selbst wenn der Updater selbst korrekt arbeitet.
3.5 Überschreibendes Update ohne Erhalt der alten Version
Bei Updates ist das Design für den Fehlerfall wichtiger als für den Erfolgsfall.
- Der Download wurde mittendrin unterbrochen
- Das Entpacken schlug fehl
- Während des Austauschs kam es zu einem Stromausfall
- Die neue Version startete, stürzte aber bei der ersten Migration ab
Ist die alte Version zu diesem Zeitpunkt bereits gelöscht, wird die Wiederherstellung aufwendig. In der Praxis ist „die App startet vor Ort nicht mehr“ ein größeres Problem als die Tatsache, dass „das Update fehlgeschlagen ist“.
3.6 Rollback wird nicht bedacht
Auch eine signierte, reguläre Version kann für einen Angreifer nützlich sein, wenn es sich um eine alte, verwundbare Version handelt.
Zum Beispiel:
- Version 1.8 hat eine bekannte Schwachstelle
- Im Feld läuft bereits Version 2.3
- Ein Angreifer verteilt 1.8 erneut
Geht das durch, ist es gefährlich, obwohl die Signatur selbst korrekt ist.
Es reicht nicht zu prüfen, „ist es signiert“; man muss auch prüfen, „darf diese Version jetzt installiert werden“.
3.7 Fail-Open
Das ist das Schlimmste, was man in Produktion tun kann.
- Bei fehlgeschlagener Signaturverifikation nur eine Warnung anzeigen und fortfahren
- Ein verstecktes Flag, mit dem sich Zertifikatsablauf-Fehler ignorieren lassen
- Ein Debug-Flag
skipVerify=true, das auch in Produktion erhalten bleibt
Gerade bei Störungen oder Angriffen werden solche Hintertüren zum bevorzugten Pfad.
4. Best Practices
4.1 Zuerst auf eine bestehende Update-Infrastruktur setzen
Es ist sicherer, zuerst zu hinterfragen, ob wirklich ein eigener Updater nötig ist.
Unter Windows lassen sich, sofern die Anforderungen passen, folgende Optionen zuerst prüfen.
- MSIX + App Installer
- ClickOnce
- Store / MDM / interne Verteilungsinfrastruktur
- MSI + unternehmensseitiges Verteilungsmanagement
Der Grund ist einfach: Ein beträchtlicher Teil der Verantwortung für das Update lässt sich auf die Plattform verlagern. Man verliert zwar an Flexibilität, aber Update-UI, Verteilungsmanifest, Paketsignatur und Betriebsabstimmung werden dadurch leichter konsistent zu halten.
Ein eigener Updater wird zum Beispiel in folgenden Fällen nötig.
- Mehrere Channels (stable / beta / preview) sollen streng kontrolliert werden
- Gestaffelte Verteilung oder Rollout-Quoten werden benötigt
- Aus geschäftlichen Gründen ist eine feingranulare Steuerung des Update-Zeitpunkts nötig
- Es gibt Konfigurationen, die nicht zu MSIX / ClickOnce passen
Auch in diesem Fall bleibt man klarer im Denken, wenn man es nicht als „wir wollen Flexibilität“, sondern als „wir übernehmen die Verantwortung für Updates selbst“ versteht.
4.2 Den Vertrauensursprung auf die Clientseite legen
Ein sicherer Updater vertraut der Server-Antwort nicht blind. Die Clientseite benötigt mindestens diese beiden Dinge.
- Einen vertrauenswürdigen Public Key oder eine Zertifikatskette
- Einen Mechanismus, um mit diesem Schlüssel signierte Metadaten zu verifizieren
Anders gesagt: Es muss ein Zustand geschaffen werden, in dem der Client nicht bestätigt „der Server sagt, das sei die neueste Version“, sondern „diese Metadaten sind die neueste Version, ausgegeben von einem Signierenden, dem wir vertrauen“.
Wie das Vertrauen von der Root bis zur Datei durchgereicht wird, zeigt das folgende Diagramm.
flowchart TD
ROOT["Root-Schlüssel<br/>im Client eingebetteter Trust Anchor"] --> SIGNKEY["Metadaten-Signaturschlüssel<br/>vom Root delegiert, häufig erneuert"]
SIGNKEY --> META["Signierte Update-Metadaten<br/>Version / URL / Hash / Größe / Ablaufdatum"]
META --> CHECK1{"Signatur, Ablaufdatum, Version prüfen"}
CHECK1 -- "NOK" --> STOP["Update abbrechen<br/>Fail-Closed"]
CHECK1 -- "OK" --> DL["Artefakt in den Staging-Bereich herunterladen"]
DL --> CHECK2{"Größe / Hash / Paketsignatur prüfen"}
CHECK2 -- "NOK" --> STOP
CHECK2 -- "OK" --> ACT["Aktivieren, alte Version bleibt erhalten"]
ACT --> HEALTH{"Health-Check beim ersten Start"}
HEALTH -- "NOK" --> RB["Rollback zur alten Version"]
HEALTH -- "OK" --> DONE["Update abgeschlossen"]
Zwei Punkte sind hier wichtig: Von oben nach unten läuft eine einzige, ununterbrochene Vertrauenskette. Und egal, an welcher Stelle diese Kette reißt, das Ergebnis ist immer Update abbrechen oder Rollback — niemals „einfach weitermachen“.
4.3 Design rund um signierte Metadaten
Mindestens sollten folgende Punkte in die Update-Metadaten aufgenommen und mitsigniert werden.
| Feld | Grund für die Aufnahme |
|---|---|
| Release-Version / Release-ID | Rollback-Schutz, Audit |
| Artefaktname, URL, Package-Type | Legt fest, welche Datei geholt wird |
| Hash, Größe | Manipulationserkennung, Erkennung defekter Zustellung |
| Channel | Verhindert, dass Beta in Stable gemischt wird |
| Ziel-OS / -Architektur | Verhindert Fehlverteilung |
| Minimum Updater Version | Stoppt alte Updater bei Protokolländerungen |
| expires_at | Freeze-Schutz |
| published_at | Audit, Eingrenzung |
| mandatory / optional | Macht auch die UX-Verzweigung des Updates manipulationssicher |
Entscheidend ist hier, alle Entscheidungsgrundlagen für das Update in signierten Metadaten zu bündeln. Die Logik liegt beim Client, die Echtheit der Information wird durch die Signatur geschützt — dieses Muster verringert Fehler.
Ohne eine konkrete Form lässt sich das schlecht umsetzen, deshalb hier ein minimales Beispiel. Zunächst der Inhalt, der signiert wird.
{
"schema_version": 1,
"channel": "stable",
"release_version": "2.4.1",
"published_at": "2026-04-09T01:00:00Z",
"expires_at": "2026-04-16T01:00:00Z",
"minimum_updater_version": "2.0.0",
"minimum_allowed_version": "2.2.0",
"mandatory": false,
"artifacts": [
{
"os": "windows",
"arch": "x64",
"package_type": "msi",
"file_name": "MyApp-2.4.1-x64.msi",
"url": "https://updates.example.com/stable/MyApp-2.4.1-x64.msi",
"size": 48234496,
"sha256": "5f2c...64-stellig hexadezimal..."
}
]
}
Das wird zusammen mit der Signatur verpackt.
{
"signed": {
"schema_version": 1,
"channel": "stable",
"release_version": "2.4.1",
"_comment": "das obige Objekt unverändert übernehmen"
},
"signatures": [
{
"keyid": "3f9a...",
"sig": "MEUCIQ..."
}
]
}
Diese Form wird gewählt, weil alle für die Update-Entscheidung verwendeten Werte innerhalb von signed liegen. URL, Version, Channel und Pflicht-Update-Flag liegen alle innen; wird nur der äußere Teil ausgetauscht, schlägt die Verifikation fehl. Die Verarbeitungsreihenfolge auf der Clientseite wird auf „zuerst signed verifizieren → erst danach die darin enthaltenen Werte verwenden“ festgelegt. Eine Implementierung, die schon vor der Verifikation die url liest und den Download startet, macht diesen Sinn zunichte.
Ein Hinweis zur Implementierung: JSON kann je nach Reihenfolge der Schlüssel und Leerzeichen eine andere Bytefolge ergeben. Die Signaturverifikation erfolgt über die Bytefolge, deshalb muss vorab festgelegt werden, ob das signierte Objekt eine „normalisierte Darstellung“ oder die „empfangene Bytefolge selbst“ ist. Legt man das nicht fest und signiert die Serverseite das erzeugte Objekt, während die Clientseite die neu serialisierte Fassung als Signaturziel verwendet, schlägt die Verifikation trotz eines korrekten Updates fehl. Umgekehrt: Beginnt man, diese Diskrepanz mit „das lassen wir eben durchgehen“ zu umgehen, ist das der erste Schritt in Richtung Fail-Open.
4.4 Auch das Artefakt selbst verifizieren
Nach der Verifikation der Metadaten wird auch beim heruntergeladenen Artefakt Folgendes geprüft.
- Größe
- Hash
- Paketsignatur / Codesignatur
- Herausgeber oder erwarteter Identifikator
Wer Windows PE / MSI / MSIX behandelt, sollte davon ausgehen, dass die Authenticode- oder Paketsignaturverifikation clientseitig erfolgt. Unter macOS ist es konsistenter, Developer ID und Notarisierung auch für den Update-Pfad vorauszusetzen.
4.5 Schlüssel werden durch Betrieb geschützt, nicht durch Funktionen
Die Schlüsselverwaltung zeigt Unterschiede eher im Betrieb als in der Implementierung.
Mindestens sollten folgende Schlüssel getrennt werden.
- Entwicklungs-Signaturschlüssel
- Staging-Signaturschlüssel
- Produktions-Signaturschlüssel
Für den Produktionsschlüssel sollte darüber hinaus Folgendes durchdacht werden:
- HSM
- Cloud-Signaturdienst
- Signiersystem mit Freigabeprozess
- Audit-Log
- Key-Rotation-Verfahren
- Zeitgestempelte Signaturen
„CI signiert automatisch, sobald der Produktions-Build durchläuft“ ist bequem, vergrößert aber den Schadensradius im Kompromittierungsfall. Zumindest sollte nachvollziehbar sein, wer wann was signiert hat.
Ist der Betrieb ausgereift, ist es zusätzlich sicherer, den selten geänderten Root-Trust von den häufig neu signierten Update-Metadaten-Schlüsseln zu trennen. Wird der Root eher offline gehalten und für die Update-Metadaten ein separater Schlüssel verwendet, lässt sich der Schadensradius einer Schlüsselkompromittierung leichter reduzieren.
4.6 Fail-Closed und gestaffeltes Update
Der Update-Ablauf folgt grundsätzlich dieser Reihenfolge.
- Metadaten abrufen
- Signatur, Ablaufdatum und Version verifizieren
- Artefakt in den Staging-Bereich herunterladen
- Hash / Größe / Signatur verifizieren
- Aktivierung vorbereiten, alte Version bleibt erhalten
- Beim Neustart oder über einen dedizierten Helper umschalten
- Health-Check beim ersten Start
- Bei Problemen zurückrollen
Wichtig sind hier zwei Dinge: Nicht ersetzen, bevor die Verifikation abgeschlossen ist. Bei einem Fehlschlag nicht fortfahren.
4.7 Die Rechte des Updaters einschränken
Es sollte vermieden werden, den gesamten Updater mit Administratorrechten laufen zu lassen.
Ideal ist folgende Trennung:
- Download und Verifikation: niedrige Rechte
- Nur der eigentliche Dateiaustausch: ein Helper mit minimalen Rechten
- Der Helper tut nichts über „ein verifiziertes Paket am vorgesehenen Ort ablegen“ hinaus
Je mehr ein Design eine Rechteerhöhung erfordert, desto gefährlicher wird es, wenn nicht klar getrennt ist, was vor der Erhöhung bereits verifiziert wurde.
4.8 Rollback / Freeze / Mix-and-Match von Anfang an ausschließen
Das lässt sich nur schwer nachträglich einbauen, deshalb sollte es von Anfang an vorhanden sein.
-
Rollback-Schutz Der Client behält „die höchste bisher gesehene Metadaten-Version / Release-Version“ und weist Älteres zurück
-
Freeze-Schutz Metadaten mit einem Ablaufdatum versehen und zu alte Metadaten zurückweisen
-
Mix-and-Match-Schutz Konsistenz zwischen den Metadaten sicherstellen. Mindestens Hash / Größe / Version des Ziel-Artefakts im Manifest selbst fixieren
Lässt sich zusätzlich eine Blocklist bestimmter Builds oder eine Minimum Allowed Version über signierte Metadaten verteilen, wird die Eindämmung im Ernstfall deutlich schneller.
Auch ohne TUF vollständig zu übernehmen, sind diese drei Eigenschaften sehr wichtig.
4.9 Zuerst mit vollständigen Updates beginnen
Delta-Updates sparen Bandbreite, sind aber als erste Implementierung komplex.
- Von welcher alten zu welcher neuen Version das Delta angewendet wird
- Der vorausgesetzte Hash vor Anwendung des Deltas
- Der finale Hash nach Anwendung des Deltas
- Wiederherstellung bei einem Fehlschlag mittendrin
- Bereinigung teilweise angewendeter oder veralteter Deltas
All das häuft sich auf einmal an. Für die Erstversion reicht es, ein signiertes Vollpaket sicher auszutauschen.
5. Minimale sichere Konfiguration
Auch ohne die volle TUF-Umsetzung sieht die minimale sichere Konfiguration für einen eigenen Updater ungefähr so aus.
5.1 Was der Client vorhält
Der Client hält das Material vor, um die Server-Antwort infrage zu stellen. Fehlt das, wird allein anhand der Aussage des Servers entschieden, ob aktualisiert werden darf.
- Vertrauenswürdiger Root-Public-Key oder eine fixierte Zertifikatskette
- Die aktuell laufende Version
- Die höchste in der Vergangenheit gesehene Metadaten-Version / Release-Version
- Der zulässige Channel
- Die unmittelbar vorherige Version für Rollback
5.2 Was der Server liefert
Der Server liefert nicht die Entscheidung selbst, sondern das Material für die Entscheidung. Keines davon wird für sich genommen vertraut — Bedeutung erhält es erst nach erfolgreicher Verifikation über den in 5.1 genannten Trust Anchor.
- Signierte Update-Metadaten
- Signierte oder plattform-signierte Artefakte
- Bei Bedarf Blocklist- / Minimum-Allowed-Version-Informationen
5.3 Typischer Ablauf
Metadaten abrufen
↓
Signatur, Ablaufdatum, Version, Channel verifizieren
↓
Artefakt in Staging herunterladen
↓
Größe / Hash / Paketsignatur verifizieren
↓
Aktivieren, alte Version bleibt erhalten
↓
Bei fehlgeschlagenem ersten Start zurückrollen
Wichtig ist hier: Allein durch die Antwort des Update-Servers wird nichts zustande gebracht. Zustande gebracht wird es durch den Trust Anchor, den der Client hält, und die Verifikationslogik.
6. Überlegungen für Windows-Projekte
Bei Windows-Apps lässt sich am besten rückwärts von der Verteilungsart her denken.
- Wenn die Anforderungen passen, MSIX App Installer
- Für interne .NET-Apps, bei denen Per-User passt, ClickOnce
- Werden Dienste, Treiber, Shell-Erweiterungen oder eine eigene Channel-Steuerung benötigt, ist MSI + eigener Updater ebenfalls einen Vergleich wert
Wählt man jedoch einen eigenen Updater, wird der Aufwand nicht kleiner. Eher wächst er.
- Verifikation von Authenticode / Paketsignatur
- Signiertes Manifest
- Rollback-Schutz
- Rechtetrennung für den Update-Helper
- Eine Update-Strategie für den Updater selbst
6.1 Wie Authenticode-Signaturen clientseitig verifiziert werden
Nur zu schreiben „Authenticode verifizieren“ lässt sich nicht direkt in eine Implementierung übersetzen, deshalb hier die Einstiegspunkte unter Windows.
| Ziel | Mittel |
|---|---|
| Verifikation im Code des Updaters | Die WinVerifyTrust-API (wintrust.dll). Mit WINTRUST_ACTION_GENERIC_VERIFY_V2 wird die Authenticode-Verifikationsrichtlinie angewendet |
| Prüfung aus Betriebsabläufen oder CI | Get-AuthenticodeSignature in PowerShell |
| Signieren und Prüfen im Release-Prozess | signtool sign / signtool verify aus dem Windows SDK |
In PowerShell reicht für die minimale Prüfung Folgendes.
$path = ".\MyApp-2.4.1-x64.msi"
$sig = Get-AuthenticodeSignature -FilePath $path
if ($sig.Status -ne 'Valid') {
throw "signature check failed: $($sig.Status) / $($sig.StatusMessage)"
}
# "Signatur ist gültig" allein reicht nicht. Es muss auch feststehen, wessen Signatur es ist.
# Der Subject (Distinguished Name) ist jedoch nicht eindeutig. Zertifikate mit demselben
# CN/O/C koennen von einer anderen CA ausgestellt werden, und wenn der Client dieser CA
# vertraut, wird Status Valid und auch der Subject-Vergleich schlaegt an.
# Fixiert werden sollte die "Herausgeberkette" oder der "Public Key"; Subject dient nur
# zur zusaetzlichen Eingrenzung.
$expectedIssuers = @( # Fingerabdrücke der Herausgeber (Zwischen-CA/Root)
'9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B' # <- durch den tatsaechlichen Wert ersetzen
)
$expectedSubject = 'CN=Example Software Inc., O=Example Software Inc., C=JP'
# Die hier neu aufgebaute Kette dient nur dazu, den Herausgeber zurückzuverfolgen;
# die Vertrauensentscheidung ist bereits mit Status = Valid oben getroffen.
# VerificationTime von X509ChainPolicy ist standardmaessig der Zeitpunkt des
# Konstruktoraufrufs (= jetzt). Baut man ohne Anpassung, schlaegt Build mit
# NotTimeValid fehl, sobald das Signaturzertifikat ablaeuft. Eine zeitgestempelte
# Signatur bleibt jedoch auch nach Ablauf Valid, sodass ein Updater in dieser
# Standardeinstellung "eine korrekt verifizierte alte Version genau in dem Moment
# verwirft, in dem das Zertifikat erneuert wird".
# Ist der Signaturzeitpunkt bekannt, in VerificationTime eintragen. Ist er nicht
# bekannt, wird die Ablaufpruefung bereits oben erledigt, sodass dieser Build sie
# nicht erneut vornehmen muss
$chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new()
$chain.ChainPolicy.VerificationFlags = 'IgnoreNotTimeValid'
$chain.ChainPolicy.RevocationMode = 'NoCheck'
try {
$built = $chain.Build($sig.SignerCertificate)
# Irgendwo zwischen dem Signierenden und der Root muss der erwartete Herausgeber liegen.
# Schlaegt Build fehl, ist die Kette nur teilweise aufgebaut, sodass auch dieser
# Fall hier durchfällt (Fail-Closed)
$chainThumbprints = @($chain.ChainElements | ForEach-Object { $_.Certificate.Thumbprint })
if (-not ($expectedIssuers | Where-Object { $chainThumbprints -contains $_ })) {
throw "unexpected issuing chain (built=$built): $($chainThumbprints -join ' / ')"
}
}
finally {
$chain.Dispose()
}
if ($sig.SignerCertificate.Subject -ne $expectedSubject) {
throw "unexpected signer: $($sig.SignerCertificate.Subject)"
}
Dabei sind fünf Einschränkungen zu beachten.
- Auch wenn
StatusValidist, bedeutet das nur, dass die Signatur formal korrekt ist. Wer signiert hat, wird separat geprüft - Eine Übereinstimmung des
Subjectbeweist nicht die Identität. Der Distinguished Name ist kein eindeutiger Identifikator. Sowohl eine unternehmensinterne als auch eine öffentliche CA können ein Codesignaturzertifikat mit demselben Distinguished NameCN=Example Software Inc., O=Example Software Inc., C=JPausstellen. Vertraut der Client dieser CA, wird eine mit anderem Schlüssel und anderem Herausgeber signierte, ausgetauschte Version mitStatus = Validund übereinstimmendemSubjectdurchgelassen.Fixiert werden sollte die Herausgeberkette (dass der Sumprint der erwarteten Zwischen-CA / Root im Pfad vom Signierenden bis zur Root enthalten ist) oder der Public Key;Subjectdient nur zur zusätzlichen Eingrenzung darauf - Ist einmal fixiert, muss vorab ein Umstellungsverfahren feststehen. Sonst kommt es an dem Tag, an dem Zertifikat oder CA gewechselt werden, auf allen Geräten zum Update-Stillstand. Der erwartete Wert sollte immer als „Array“ gehalten werden, sodass alt und neu parallel akzeptiert werden können, bevor umgestellt wird (dreistufig: zuerst einen Updater ausliefern, der den neuen Herausgeber in der Zulassungsliste zusätzlich erlaubt → dann die Signatur umstellen → danach den alten Wert entfernen). Dieses „vorab ausliefern“ funktioniert nur, wenn sich der Updater selbst aktualisieren lässt — deshalb muss dies zusammen mit der zu Beginn von Kapitel 6 genannten „Update-Strategie für den Updater selbst“ konzipiert werden
- Eine Signatur ohne Zeitstempel wird ab dem Ablauf des Zertifikats nicht mehr verifiziert. Beim Release sollte immer ein Zeitstempel gesetzt werden. Genau deshalb wird in 4.5 die zeitgestempelte Signatur genannt
- Der
X509Chainzur Rückverfolgung des Herausgebers darf die Gültigkeitsdauer nicht anhand der aktuellen Zeit neu beurteilen.X509ChainPolicy.VerificationTimeist standardmäßig der Zeitpunkt des Konstruktoraufrufs, also jetzt. Auch die Dokumentation hält ausdrücklich fest, dass diese Eigenschaft wichtig ist, weil eine signierte Nachricht zum Zeitpunkt der Signatur, nicht zum Zeitpunkt der Verifikation, gültig sein muss. Bleibt es bei der Standardeinstellung, wird ein Paket, das dank ZeitstempelStatus = Validerhalten hat, unmittelbar danach als „Zertifikat abgelaufen“ zurückgewiesen. Der Sinn des Zeitstempels geht damit verloren, und mit der Erneuerung des Zertifikats fallen sämtliche vergangenen Releases auf einmal durch. Ist der Signaturzeitpunkt bekannt, sollte er inVerificationTimeeingetragen werden; ist er nicht bekannt, wirdIgnoreNotTimeValidgesetzt. Das ist keine Abschwächung. Die Beurteilung von Gültigkeitsdauer und Vertrauen ist bereits einen Schritt weiter oben mitStatus = Validerledigt, und dieserBuild-Aufruf dient nur dazu, „wer ausgestellt hat“ zu ermitteln. Aus demselben Grund wird hier auch keine Sperrprüfung vorgenommen (die CRL eines abgelaufenen Zertifikats wird nicht zwangsläufig weiter veröffentlicht). Das Mittel, um bei einem Schlüsselleck zu stoppen, ist nicht die CRL, sondern die Blocklist und die Minimum Allowed Version (4.8)
Zu beachten ist außerdem, dass das Verifikationsergebnis vom Zertifikatspeicher und den Vertrauenseinstellungen des jeweiligen Geräts abhängt.In einer Umgebung mit locker konfigurierten clientseitigen Vertrauenseinstellungen wird auch die Bedeutung von Status = Valid entsprechend locker. Hält der Updater selbst den erwarteten Herausgeber wie oben gezeigt fest, bleibt er davon unberührt, selbst wenn die Vertrauenseinstellungen des Geräts weiter gefasst sind.
Ein unter Windows verbreitetes gefährliches Muster ist die direkte Kette DownloadFile -> unzip -> kill process -> overwrite -> restart.
Das kann funktionieren, ist aber sowohl bei der Sicherheit als auch bei der Wiederherstellbarkeit schwach.
SmartScreen- oder UAC-Warnungen über „Weitere Informationen → Trotzdem ausführen“ wegklicken zu lassen, ist kein Update-Design, sondern Warnungsgewöhnung. Wer einen sauberen Update-Pfad baut, sollte nicht die Nutzer an Warnungen gewöhnen, sondern auf eine Verteilungs- und Verifikationsstruktur hinarbeiten, bei der Warnungen selten auftreten.
Den Vergleich der Verteilungsarten selbst haben wir auch in diesem Artikel zusammengestellt: Wie man die Verteilungsart für Windows-Apps wählt - Entscheidungstabelle für MSI/MSIX/ClickOnce/xcopy/eigenen Updater
7. Mindest-Checkliste
Vor der Auslieferung eines eigenen Updaters sollte mindestens Folgendes geprüft werden.
- Die Update-Metadaten sind signiert
- Die Metadaten enthalten Version / Hash / Größe / Channel / Ablaufdatum
- Der Client verifiziert Signatur und Version
- Die Fixierung des Signierenden erfolgt über die Herausgeberkette oder den Public Key, nicht über den Distinguished Name (Subject)
- Es gibt ein Umstellungsverfahren für fixierte Werte (Übergangszeit mit paralleler Akzeptanz von alt und neu)
- Hash und Plattformsignatur des Artefakts werden verifiziert
- Der Produktions-Signaturschlüssel ist von der Entwicklungsumgebung getrennt
- Nutzungsprotokoll und Freigabevermerke des Schlüssels bleiben erhalten
- Es werden zeitgestempelte Signaturen verwendet
- Beim gestaffelten Update bleibt die alte Version beim Umschalten erhalten
- Es gibt Bedingungen und ein Verfahren für Rollback
- Bei fehlgeschlagener Verifikation stoppt das System Fail-Closed
- Es gibt eine Update-Richtlinie für den Updater selbst
- Eine Blocklist / Minimum Allowed Version kann verteilt werden
- Es gibt einen Kill Switch, um die gestaffelte Verteilung zu stoppen
- Fehlerrate, Rollback-Rate und Signaturverifikationsfehler sind beobachtbar
Sind viele dieser Punkte noch offen, bringt es mehr, zuerst das Vertrauensmodell der Verteilung zu klären, statt zuerst die UI des Updaters zu bauen.
8. Zusammenfassung
Bei der Sicherheit einer Auto-Update-Funktion läuft am Ende alles auf Folgendes hinaus.
Nicht der Komfort des Updates zählt, sondern die Entscheidung, wem vertraut wird und wie der Client dieses Vertrauen verifiziert.
Darauf aufbauend, grob zusammengefasst die praktischen Entscheidungen:
- Reicht eine bestehende Infrastruktur aus, sollte zuerst darauf gesetzt werden
- Wird ein eigener Updater gebaut, kommen signierte Metadaten und Schlüsselverwaltung vor HTTPS
- Ein Updater ohne durchdachte Wiederherstellung und Rollback wird in Produktion schmerzhaft
- Der Updater ist keine Verteilungsfunktion, sondern die Sicherheitsgrenze des Produkts selbst
Ist die aktuelle Konfiguration nahe an latest.json + zip-Austausch, sollte zuerst nicht die Download-Logik, sondern wo das Vertrauen verankert ist, korrigiert werden.
Allein diese Korrektur verändert den Risikograd erheblich.
9. Quellen
- CISA Secure by Design Pledge
- NIST: Security Considerations for Code Signing
- NIST Secure Software Development Framework (SSDF)
- The Update Framework Specification
- TUF: Roles and metadata
- TUF: Security
- Microsoft Learn: Authenticode Digital Signatures
- Microsoft Learn: WinVerifyTrust function
- Microsoft Learn: Get-AuthenticodeSignature
- Microsoft Learn: Auto-update and repair apps - MSIX
- Microsoft Learn: ClickOnce Deployment and Security
- Apple Developer: Developer ID
- CA/Browser Forum: Baseline Requirements for the Issuance and Management of Publicly-Trusted Code Signing Certificates
Verwandte Themen
Themenseiten, die diesem Thema nahestehen. Ausgehend von diesem Artikel geht es weiter zu verwandten Services und anderen Artikeln.
Windows-Technikthemen
Ein Einstiegspunkt, der technische Themen zu Windows-Entwicklung, Fehleruntersuchung und Weiternutzung bestehender Assets bündelt.
Services, die zu diesem Thema passen
Windows-Anwendungsentwicklung
Auto-Update ist nicht nur eine Frage der UI, sondern ein Design, das Verteilungsart, Rechte, Wiederherstellung und Betrieb umfasst. Bei der Neuentwicklung von Windows-Apps oder der Überarbeitung bestehender Software kann bereits bei der Klärung der Update-Methode unterstützt werden.
Technische Beratung & Design-Review
Beratung ist bereits in der frühen Klärungsphase möglich: „Brauchen wir wirklich einen eigenen Updater?“, „Reicht MSIX / ClickOnce aus?“, „Wo ist unser aktuelles Update-Design riskant?“
Autorenprofil
Go Komura
Geschäftsführer, KomuraSoft LLC
Schwerpunkte sind Windows-Softwareentwicklung, technische Beratung und Fehleruntersuchung, mit besonderer Stärke bei Projekten mit bestehenden Altbeständen und bei der Untersuchung von Störungen, deren Ursache schwer erkennbar ist.
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Warum Windows die Meldung „Der Computer wurde durch Windows geschützt“ anzeigt
Warum SmartScreen-Warnungen bei der Verteilung von Windows-Apps auftreten — eine praxisnahe Einordnung von Codesignierung, EV-/OV-Zertifi...
Windows-App-Verteilung wählen – MSI/MSIX/ClickOnce/xcopy/eigener Updater
Die Wahl der Verteilungsmethode für eine Windows-App ist keine Frage des Installer-Geschmacks, sondern der Kopplung mit dem OS und der Up...
CI/CD für WinForms-/WPF-Anwendungen in der Praxis ── Vom Build über die Signierung bis zur Distribution mit GitHub Actions automatisieren
Ein praktischer Leitfaden zur Einrichtung von CI/CD für WinForms-/WPF-Anwendungen mit GitHub Actions. Behandelt eine minimale YAML für Bu...
Wenn die eigene Windows-App als Virus gemeldet wird — Umgang mit Fehlalarmen von Microsoft Defender und den Auswirkungen auf die Leistung
Wir legen dar, wie richtig reagiert wird, wenn Microsoft Defender die eigene Windows-App als Malware meldet: wie moderne Antivirus-Erkenn...
Was ist ClickOnce? - Funktionsweise, Updates und wo es passt oder nicht, aus praktischer Sicht
Ein Überblick über ClickOnce, die Verteilungstechnologie für .NET-Windows-Desktopanwendungen: Manifeste, Updates, Cache, Signierung sowie...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Verteilung, Update, Signatur, Rollback und die Wahl zwischen MSIX und ClickOnce für Windows-Apps erfordern, nicht nur die Implementierung, sondern auch die Verteilungsart und das Betriebsdesign mitzudenken.
Technische Beratung und Design-Review
Das Design der Vertrauensgrenze von Auto-Updates, signierter Metadaten, des Schlüsselbetriebs und des Fail-Closed-Verhaltens erfordert eher eine Klärung der Gesamtarchitektur als einer einzelnen Implementierung.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Ist ein Auto-Update sicher, solange die Kommunikation über HTTPS läuft?
- TLS ist notwendig, aber keine hinreichende Bedingung. TLS schützt vor allem den Übertragungsweg und die Legitimität der Gegenstelle. Es hilft nicht, wenn der Update-Server selbst kompromittiert wurde, ein falsches Artefakt auf dem legitimen CDN landet oder ein unsigniertes Manifest ausgetauscht wird. Die Update-Entscheidung darf nicht darauf beruhen, dass „der Server es so sagt“, sondern muss darauf beruhen, dass „der Client signierte Metadaten verifiziert und als korrekt beurteilt hat“.
- Was sollte in die Update-Metadaten aufgenommen und signiert werden?
- Grundsätzlich sollte alles, was für die Update-Entscheidung relevant ist, in signierten Metadaten gebündelt werden. Konkret: die Release-Version, URL und Dateiname des Artefakts, Hash und Größe, der Channel (stable / beta usw.), Ziel-OS und -Architektur, die minimal erforderliche Updater-Version, das Ablaufdatum der Metadaten (expires_at) sowie ein Flag, ob das Update verpflichtend ist. Wird nur die Binärdatei signiert und das Manifest bleibt unsigniert, können URL, Version oder das Pflicht-Update-Flag manipuliert werden.
- Was ist ein Rollback-Angriff und wie schützt man sich davor?
- Dabei verteilt ein Angreifer erneut eine ältere, korrekt signierte Version mit einer bekannten Schwachstelle, um Clients auf diese zurückzustufen. Da die Signatur selbst korrekt ist, reicht die reine Signaturprüfung nicht aus. Als Schutz behält der Client die höchste bisher gesehene Release-Version und weist alles Ältere zurück. Zusätzlich sollten die Metadaten ein Ablaufdatum tragen, um Freeze-Angriffe zu verhindern, die kein neueres Update anzeigen, und das Manifest sollte Hash, Größe und Version des Artefakts fixieren, um Mix-and-Match-Angriffe auszuschließen.
- Sollte man einen eigenen Updater bauen oder auf vorhandene Mechanismen setzen?
- Wenn die Anforderungen passen, ist es sicherer, zunächst auf eine bestehende Update-Infrastruktur wie MSIX mit App Installer oder ClickOnce zu setzen, da sich damit ein großer Teil der Verantwortung für Updates auf die Plattform verlagern lässt. Ein eigener Updater wird nötig, wenn eine strikte Kontrolle mehrerer Channels, eine gestaffelte Verteilung oder andere Anforderungen bestehen, die sich nicht in die bestehenden Mechanismen einfügen. Auch dann sollte zuerst nicht die Benutzeroberfläche, sondern die Signaturverifikation und die Wiederherstellung im Fehlerfall gebaut werden, mit Fail-Closed-Verhalten bei fehlgeschlagener Verifikation und der Möglichkeit, bei Bedarf zur alten Version zurückzukehren, die dafür erhalten bleibt.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.