Download, Prüfsumme und Ausführung trennen, statt blind auszuführen, was der Server gerade liefert
curl -fsSL https://install.example.com | bash installiert in einer einzigen Zeile, führt dabei aber Code mit den vollen Rechten des aufrufenden Benutzers aus, ohne dass jemand ihn vorher gesehen hat. Wer versteht, welche konkreten Risiken dieses Muster mitbringt, kann es mit wenigen zusätzlichen Schritten deutlich sicherer machen, ohne die Bequemlichkeit für Endnutzer komplett zu opfern.
Inhaltsverzeichnis
- 1. Warum das curl-Bash-Pattern so verbreitet ist
- 2. Risiko 1: keine Prüfsumme, kein Nachweis über den tatsächlichen Inhalt
- 3. Risiko 2: TOCTOU und serverseitig variable Antworten
- 4. Risiko 3: Ausführung beginnt, bevor der Download vollständig ist
- 5. Sicherer machen: Download, Prüfsumme und Ausführung trennen
- 6. GPG-Signaturen als zusätzliche Absicherungsebene
- 7. Ausführung mit eingeschränkten Rechten und vorheriger Durchsicht
- 8. Wann das rohe Pattern trotzdem akzeptabel ist
- 9. Absicherungsstufen im Überblick
- 10. Zusammenfassung
- 11. FAQ
1. Warum das curl-Bash-Pattern so verbreitet ist
Das Muster curl -fsSL https://install.example.com | bash ist bei unzähligen Entwicklerwerkzeugen die vorgeschlagene Installationsmethode, weil es aus Sicht des Anbieters denkbar einfach ist: eine einzige Zeile, kein Paketmanager-Repository, das gepflegt werden muss, keine Plattform-spezifischen Pakete, und sofort lauffähig auf praktisch jedem System mit Bash und curl. Für den Anbieter senkt das die Einstiegshürde erheblich und reduziert den Supportaufwand für unterschiedliche Betriebssysteme.
Genau diese Einfachheit ist aber auch der Kern des Problems: Der Nutzer führt Code mit den eigenen Benutzerrechten aus, den er im Moment der Ausführung weder gesehen noch geprüft hat, und vertraut dabei implizit darauf, dass der Server exakt das ausliefert, was in der Dokumentation beworben wird, dass die Verbindung nicht manipuliert wurde, und dass der Server selbst nicht kompromittiert ist.
2. Risiko 1: keine Prüfsumme, kein Nachweis über den tatsächlichen Inhalt
Beim klassischen curl | bash gibt es keinerlei Mechanismus, der bestätigt, dass der empfangene Code tatsächlich dem entspricht, was der Anbieter veröffentlichen wollte. Ist die Verbindung zum Server nicht durchgängig per TLS abgesichert, kann ein Angreifer im Netzwerkpfad, etwa in einem offenen WLAN oder bei einem kompromittierten Proxy, den Inhalt der Antwort unbemerkt austauschen und beliebigen Code einschleusen, der dann mit den Rechten des aufrufenden Benutzers läuft.
Aber selbst bei einer sauberen TLS-Verbindung bleibt ein Risiko auf Serverseite bestehen: Wird der Installer-Endpunkt selbst kompromittiert, etwa durch einen gestohlenen Deployment-Zugang oder eine Schwachstelle im CDN, liefert der Server über eine völlig legitime, verschlüsselte Verbindung schadhaften Code aus, ohne dass eine reine TLS-Prüfung das erkennen könnte. Eine kryptografische Prüfsumme, gegen einen unabhängigen, vorab veröffentlichten Wert geprüft, ist der einzige Mechanismus, der beide Fälle abdeckt.
# The risky classic: no verification of any kind before execution,
# and if the download stalls mid-stream, bash may already be
# executing the first, incomplete part of the script.
curl -fsSL https://install.example.com | bash
3. Risiko 2: TOCTOU und serverseitig variable Antworten
Eine weitere, subtilere Schwäche ist ein TOCTOU-Problem (Time-of-Check to Time-of-Use): Selbst wenn ein Nutzer den Installer-Code vorab in einem Browser prüft, garantiert das nicht, dass genau derselbe Inhalt beim eigentlichen Aufruf tatsächlich ankommt. Zwischen der Prüfung und der Ausführung kann der Server den Inhalt ändern, sei es durch ein reguläres Update, durch einen kompromittierten Deployment-Prozess, oder durch eine gezielt unterschiedliche Antwort für unterschiedliche Anfragen.
Manche Installer-Server liefern zudem abhängig vom User-Agent-Header, von der IP-Adresse oder von anderen Anfrage-Merkmalen unterschiedlichen Code aus, etwa um Betriebssystem-spezifische Installationsschritte automatisch auszuwählen. Das ist für sich genommen kein Sicherheitsproblem, macht aber jede manuelle Vorab-Prüfung des Skripts im Browser wertlos, weil der tatsächlich per curl ausgelieferte Inhalt ein anderer sein kann als der im Browser angezeigte.
4. Risiko 3: Ausführung beginnt, bevor der Download vollständig ist
Bash liest die Eingabe aus einer Pipe zeilenweise beziehungsweise blockweise, nicht erst nach vollständigem Empfang. Bricht die Netzwerkverbindung mitten in der Übertragung ab, kann Bash bereits einen Teil des Skripts ausgeführt haben, während der Rest fehlt, was zu einem inkonsistenten, teilweise ausgeführten Zustand führt, der schwerer zu diagnostizieren ist als ein sauberer, kompletter Fehlschlag. Ein gut geschriebenes Installer-Skript kapselt sich deshalb typischerweise in eine einzige äußere Funktion, die erst am Skriptende aufgerufen wird, um genau dieses Problem zu entschärfen, aber verlassen kann man sich darauf nicht bei jedem Anbieter.
Dieses Verhalten unterscheidet sich fundamental von einem Ansatz, bei dem das komplette Skript zunächst lokal als Datei gespeichert und erst danach ausgeführt wird: Dort existiert entweder die vollständige, unversehrte Datei, oder der Download ist erkennbar fehlgeschlagen, es gibt keinen Zwischenzustand mit teilweise ausgeführtem Code.
5. Sicherer machen: Download, Prüfsumme und Ausführung trennen
Der wichtigste einzelne Schritt zu einem sichereren Installer-Muster ist, die drei Phasen Download, Verifikation und Ausführung explizit als getrennte Kommandos auszuführen, statt sie in einer einzigen Pipe zu verschmelzen. Das Skript wird zuerst vollständig als Datei heruntergeladen, danach gegen eine unabhängig veröffentlichte, kryptografische Prüfsumme verglichen, und erst nach erfolgreicher Prüfung tatsächlich ausgeführt.
Entscheidend ist dabei, dass die Prüfsumme selbst nicht von derselben Quelle stammt wie das Skript, wenn diese Quelle kompromittiert sein könnte, sondern idealerweise über einen zweiten, unabhängigen Kanal veröffentlicht wird, etwa in den Release Notes eines Git-Repositories oder in einer signierten Ankündigung, damit ein Angreifer, der nur den Download-Server kontrolliert, nicht automatisch auch die veröffentlichte Prüfsumme fälschen kann.
#!/usr/bin/env bash
set -euo pipefail
INSTALLER_URL="https://install.example.com/install.sh"
EXPECTED_SHA256="a3f5c9e1b8d2..." # published independently, e.g. in release notes
TMP_SCRIPT="$(mktemp)"
trap 'rm -f "$TMP_SCRIPT"' EXIT
# Step 1: download only, never execute directly from the pipe
curl -fsSL -o "$TMP_SCRIPT" "$INSTALLER_URL"
# Step 2: verify against an independently published checksum
actual_sha256="$(sha256sum "$TMP_SCRIPT" | cut -d' ' -f1)"
if [[ "$actual_sha256" != "$EXPECTED_SHA256" ]]; then
echo "Checksum mismatch! Refusing to execute." >&2
exit 1
fi
# Step 3: only now, after successful verification, actually run it
bash "$TMP_SCRIPT"
6. GPG-Signaturen als zusätzliche Absicherungsebene
Eine reine Prüfsumme bestätigt nur, dass die heruntergeladene Datei mit einer zuvor gesehenen Version übereinstimmt, sagt aber nichts darüber aus, ob diese Version tatsächlich vom vermeintlichen Anbieter stammt. Eine GPG-Signatur geht einen Schritt weiter: Der Anbieter signiert das Skript mit seinem privaten Schlüssel, und der Nutzer verifiziert die Signatur gegen den öffentlichen Schlüssel, den er über einen vertrauenswürdigen Weg, etwa einen Keyserver oder die offizielle Projekt-Webseite, bereits vorher bezogen hat.
Das schützt zusätzlich gegen den Fall, dass ein Angreifer sowohl das Skript als auch die dazugehörige Prüfsumme auf demselben kompromittierten Server austauscht, denn ohne Zugriff auf den privaten Signaturschlüssel des Anbieters kann der Angreifer keine gültige Signatur für sein manipuliertes Skript erzeugen, selbst wenn er die komplette Serverinfrastruktur kontrolliert.
#!/usr/bin/env bash
set -euo pipefail
curl -fsSL -o install.sh https://install.example.com/install.sh
curl -fsSL -o install.sh.sig https://install.example.com/install.sh.sig
# The public key must already be trusted locally, e.g. imported once
# from the project's official website, not fetched fresh every time
gpg --verify install.sh.sig install.sh
bash install.sh
7. Ausführung mit eingeschränkten Rechten und vorheriger Durchsicht
Selbst nach erfolgreicher Prüfsumme oder Signatur bleibt sinnvoll, ein unbekanntes Installer-Skript nicht standardmäßig mit vollen Root-Rechten laufen zu lassen, wenn das nicht zwingend nötig ist. Viele Installer verlangen sudo, obwohl sie in Wahrheit nur Dateien im Home-Verzeichnis des Benutzers anlegen, deshalb lohnt ein kurzer Blick in das Skript, welche Kommandos tatsächlich mit erhöhten Rechten laufen, bevor pauschal mit sudo ausgeführt wird.
Für zusätzliche Sicherheit lässt sich ein unbekanntes Installer-Skript zunächst in einem isolierten Container oder einer virtuellen Maschine testen, bevor es auf einem produktiven System läuft. So bleiben mögliche Schäden, etwa durch versehentlich mitinstallierte Zusatzsoftware oder unerwünschte Netzwerkverbindungen, auf die isolierte Umgebung begrenzt und lassen sich vor dem eigentlichen produktiven Einsatz beobachten.
#!/usr/bin/env bash
set -euo pipefail
# Dry run in an isolated, throwaway container before trusting the
# installer on a real machine
docker run --rm -it \
-v "$(pwd)/install.sh:/tmp/install.sh:ro" \
ubuntu:24.04 \
bash -c "apt-get update -qq && bash /tmp/install.sh"
8. Wann das rohe Pattern trotzdem akzeptabel ist
Nicht jeder Einsatz von curl | bash verdient dieselbe Vorsicht. Innerhalb einer eigenen, vertrauenswürdigen internen Infrastruktur, etwa wenn ein Deployment-Skript einen Installer vom eigenen, per Zertifikat abgesicherten internen Artefakt-Server lädt, ist das Risiko eines manipulierten Servers deutlich geringer als bei einem öffentlichen Drittanbieter-Endpunkt, und der zusätzliche Aufwand für Prüfsumme oder Signatur kann in einem solchen Kontext bewusst geringer ausfallen.
Für öffentlich verteilte Installer, die auf beliebigen Nutzer-Maschinen mit unbekannter Vertrauensbasis laufen sollen, gilt dagegen das Gegenteil: Genau dort, wo der Anbieter am wenigsten Kontrolle über die Umgebung des Nutzers hat, sind Prüfsumme und Signatur am wichtigsten, weil ein einzelner kompromittierter Installer-Endpunkt potenziell tausende Systeme gleichzeitig betrifft.
9. Absicherungsstufen im Überblick
Die Wahl der richtigen Absicherungsstufe hängt vom Vertrauensverhältnis zwischen Nutzer und Anbieter, von der Reichweite des Installers und vom Schutzbedarf des Zielsystems ab.
| Ansatz | Schutz gegen manipulierten Server | Schutz gegen TOCTOU | Zusatzaufwand |
|---|---|---|---|
curl | bash roh |
Nein | Nein | Keiner |
| Download, dann sha256sum prüfen | Ja, gegen bekannten Hash | Teilweise, bei getrennt veröffentlichtem Hash | Gering |
| Zusätzlich GPG-Signatur prüfen | Ja, auch bei kompromittiertem Server | Ja | Mittel, einmaliger Schlüsselimport nötig |
| Distributions-Paketmanager (apt, dnf) | Ja, über Repository-Signaturen | Ja | Gering, aber Paket muss gepflegt werden |
Mironsoft
Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur
Shell-Skripte, die in der Produktion zuverlässig laufen?
Wir analysieren bestehende Bash-Skripte, erkennen fragile Muster und ersetzen sie durch robuste Bash-Patterns: mit vollständiger Fehlerbehandlung, Logging und sicherer Parallelisierung für euren Deployment-Stack.
Code-Review
ShellCheck-Analyse und manuelle Prüfung auf kritische Bash-Pattern-Verstöße.
Refactoring
Fehlerbehandlung, Logging und sichere Dateioperationen nachrüsten.
CI-Integration
ShellCheck und BATS in Pipelines integrieren und Regressionstests aufbauen.
10. Zusammenfassung
curl-Bash-Installer sicher: Das Wichtigste auf einen Blick
Kernrisiko
curl | bash führt Code mit vollen Nutzerrechten aus, ohne Prüfsumme, Signatur oder vorherige Durchsicht.
TOCTOU
Eine manuelle Prüfung im Browser garantiert nicht, dass exakt derselbe Inhalt beim eigentlichen Aufruf ankommt.
Absicherung
Download, sha256sum-Prüfung gegen unabhängig veröffentlichten Hash und erst danach Ausführung als getrennte Schritte.
Zusatzschutz
Eine GPG-Signatur schützt zusätzlich gegen einen komplett kompromittierten Server, der Skript und Prüfsumme gleichzeitig austauscht.