Command-Injection-Schwachstellen in Go verstehen
14. November 2024
0 Min. LesezeitGo-Entwickler müssen in verschiedenen Situationen möglicherweise Systembefehle verwenden, etwa zur Bildbearbeitung, wenn sie Bilder verarbeiten oder in der Größe ändern müssen, oder um Ressourcen zu verwalten sowie Metriken oder Protokolle zu erfassen.
Vielleicht entwickeln Sie auch ein neues System in Go, das mit bestehenden Legacy-Systemen kommunizieren muss. Diese Schnittstelle basiert auf der Ausführung von Systembefehlen und der Verarbeitung ihrer Ausgaben.
In beiden Fällen ist es unerlässlich, beim Starten von Systembefehlen sichere Programmierkonventionen einzuhalten, da andernfalls eine als Command Injection bekannte Sicherheitslücke entstehen kann.
Was ist Command Injection?
Command Injection ist eine Sicherheitslücke, bei der eine Anwendung nicht vertrauenswürdige, vom Benutzer bereitgestellte Daten (etwa Eingaben aus einem Webformular) an eine System-Shell übergibt. Dadurch kann ein Angreifer beliebige Befehle auf dem Host-Betriebssystem mit den Rechten des Anwendungskontos ausführen.
In Go umfasst Command Injection häufig die Verwendung des Pakets os/exec, um Systembefehle zu starten.
Betrachten Sie das folgende Go-Codebeispiel:
In diesem Codeausschnitt wird cmdName direkt aus den Anfrageparametern extrahiert und zum Erstellen eines Befehls verwendet, der auf dem Server ausgeführt wird. Dies ist ein klassisches Beispiel für eine Command-Injection-Schwachstelle: Ein Angreifer kann den Wert des Abfrageparameters cmd manipulieren, um beliebige Befehle auszuführen und möglicherweise den Server zu kompromittieren.
Ein Angreifer könnte eine Anfrage mit einem schädlichen Befehl erstellen, zum Beispiel:
Warum ist Command Injection gefährlich? Sie ist gefährlich, weil Angreifer damit beliebige Befehle auf dem Server ausführen können. Das kann schwerwiegende Folgen haben, darunter:
Datenschutzverletzung: Angreifer können auf sensible Daten auf dem Server zugreifen. Stellen Sie sich vor, sie könnten Konfigurationsdateien wie
config.tomlund andere Dateien mit Ressourcen und Zugangsdaten einsehen.Kompromittierung des Systems: Angreifer können die Kontrolle über den Server erlangen und ihn weiter ausnutzen. Häufig breiten sie sich dabei lateral im kompromittierten System aus und suchen nach weiteren Servern, die sie kompromittieren könnten.
Dienstunterbrechung: Angreifer können Befehle ausführen, die Dienste stören und Ausfallzeiten verursachen. Dies kann unmittelbar zu finanziellen Verlusten und Geschäftsrisiken führen, etwa durch einen Denial-of-Service-Angriff.
Die Folgen eines erfolgreichen Command-Injection-Angriffs können verheerend sein: Betroffen sind nicht nur das kompromittierte System, sondern auch der Ruf des Unternehmens und das Vertrauen der Kunden.
Unsichere Prozessausführung und Command Injection in Go
Go-Entwickler bevorzugen bekanntlich Einfachheit und Leistung und entscheiden sich möglicherweise dafür, Systembefehle einzubinden, um die Funktionen solcher Tools zu nutzen. So können sie sich auf robuste Anwendungen konzentrieren, ohne das Rad neu erfinden zu müssen. Diese Integration bringt jedoch eigene Herausforderungen mit sich, insbesondere in puncto Sicherheit.
Betrachten wir zur Veranschaulichung ein realistischeres Szenario: eine Go-Anwendung, die Bilddateien mit einem Befehlszeilentool wie convert verarbeitet.
Wir sehen uns eine Go-Anwendung an, die Anfragen zum Ändern der Bildgröße verarbeitet. Die Anwendung verwendet das Gin-Webframework, um einen POST-Endpunkt /cloudpawnery/image zu definieren, der anhand von Benutzereingaben Bilder skaliert. Dieser Endpunkt akzeptiert Abfrageparameter wie tenantID, fileID und fileSize. Der Parameter fileSize ist optional und hat standardmäßig den Wert "200", wenn er nicht angegeben wird.
Der folgende Codeausschnitt zeigt eine unsichere Implementierung in Go.
Die Funktion downloadAndResize erstellt einen Befehlsstring, um das Bild mit convert zu skalieren, und führt ihn mit exec.CommandContext aus.
Wie erstellt die Funktion downloadAndResize einen Befehlsstring? Sie übernimmt dafür die Benutzereingabe fileSize. Dieser String wird anschließend ausgeführt, wodurch ein Angreifer möglicherweise schädliche Befehle einschleusen kann. Um dieses Risiko zu mindern, sollten Go-Entwickler alle Benutzereingaben validieren und bereinigen, parametrisierte Befehle verwenden und sichere Verfahren für die Befehlsausführung einsetzen.
Command-Injection-Schwachstellen entschärfen
In der Go-Entwicklung ist es – wie in jeder anderen Programmiersprache – entscheidend, Ihren Code vor Command-Injection-Schwachstellen zu schützen. Command Injection liegt vor, wenn ein Angreifer beliebige Befehle auf dem Host ausführen kann. Dies kann zu unbefugtem Zugriff, Datenschutzverletzungen und anderen schwerwiegenden Sicherheitsproblemen führen.
Sehen wir uns einige Best Practices an, um diese Risiken zu mindern.
Eingaben validieren und bereinigen
Ein grundlegender Schritt zur Vermeidung von Command Injection ist die sorgfältige Validierung und Bereinigung von Eingaben. Im gezeigten Beispiel erstellt die Funktion downloadAndResize einen Befehlsstring aus Benutzereingaben wie fileSize. Werden diese Eingaben nicht ordnungsgemäß validiert, könnte ein Angreifer schädliche Befehle einschleusen.
So können Sie die Eingabevalidierung verbessern:
Eingaben per Allowlist zulassen: Legen Sie fest, welche Werte für Eingaben wie
fileSizezulässig sind. Erlauben Sie beispielsweise nur Zahlen innerhalb eines angemessenen Bereichs.Eingaben bereinigen: Entfernen oder maskieren Sie potenziell gefährliche Zeichen in Benutzereingaben. Dazu gehören Shell-Metazeichen wie
;,|,&usw. Am besten codieren Sie den Befehl fest und lassen nicht zu, dass Benutzereingaben ihn steuern.Strenge Typen verwenden: Wandeln Sie Eingaben so früh wie möglich in den erwarteten Datentyp um. Konvertieren Sie beispielsweise
fileSizein eine Ganzzahl und überprüfen Sie, ob der Wert im zulässigen Bereich liegt.
Hier sehen Sie ein Beispiel für die Umsetzung dieser Maßnahmen:
Trotzdem gibt es noch deutlich bessere Möglichkeiten, Go-Code vor Command Injection zu schützen. Lesen Sie weiter!
Sichere APIs oder Bibliotheken statt Systembefehlen verwenden
Eine weitere wirksame Strategie besteht darin, Systembefehle nach Möglichkeit nicht direkt auszuführen. Verwenden Sie stattdessen sichere APIs oder Bibliotheken, die die erforderliche Funktionalität bereitstellen, ohne Ihre Anwendung den Risiken von Command Injection auszusetzen.
Wenn Ihre Anwendung beispielsweise Bilder bearbeiten muss, sollten Sie eine Go-Bibliothek wie github.com/disintegration/imaging verwenden, statt einen externen Befehl wie convert aus der ImageMagick-Softwarebibliothek aufzurufen. So bleibt die Funktionalität in der typsicheren Go-Umgebung gekapselt und die Angriffsfläche wird verkleinert.
Mit Bibliotheken wie imaging in Go müssen Sie keine Shell-Befehle erstellen und ausführen. Dadurch mindern Sie das Risiko von Command Injection. Bei manchen Bibliotheken und Sprachökosystemen kann ein Drittanbieterpaket jedoch lediglich ein einfacher Wrapper für die Befehlsausführung sein. Prüfen Sie deshalb unbedingt den Code auf derartige sicherheitskritische Operationen.
Die anfällige Funktion downloadAndResize so überarbeiten, dass sie vor Injection schützt
Im vorherigen Beispiel haben wir eine potenziell anfällige Go-Anwendung gezeigt, die die Funktion exec.CommandContext zur Ausführung von Shell-Befehlen verwendet. Wie bereits erwähnt, kann dieser Ansatz zu Command-Injection-Schwachstellen führen.
Versuchen wir, die Funktion downloadAndResize so zu überarbeiten, dass Benutzereingaben nicht zur Ausführung beliebiger Befehle führen.
Eine wirksame Methode, Command Injection zu verhindern, besteht darin, Shell-Befehlsstrings nicht direkt aus Benutzereingaben zu erstellen. Verwenden Sie stattdessen die Funktion exec.Command mit separaten Argumenten. So können Sie Benutzereingaben sicher als Parameter übergeben, ohne die Shell aufzurufen oder Benutzern die Kontrolle über den eigentlichen Befehl zu geben.
Hier ist eine überarbeitete Version der Funktion downloadAndResize, die die Command-Injection-Schwachstelle behebt:
Bei dieser Überarbeitung haben wir den Befehl von seinen Argumenten getrennt. Mit exec.CommandContext und separaten Argumenten müssen wir keinen Shell-Befehlsstring erstellen. Dadurch werden Benutzereingaben als Daten statt als ausführbarer Code behandelt, was das Risiko von Command Injection deutlich senkt.
Außerdem benötigen wir keinen Aufruf der Shell mehr. Der überarbeitete Code ruft die Shell (sh -c) nicht auf, die häufig als Angriffsvektor für Command Injection dient. Stattdessen wird das Dienstprogramm convert direkt mit den angegebenen Argumenten aufgerufen.
Mit Snyk die Codesicherheit verbessern
Snyk Code ist ein leistungsstarkes Tool für die statische Analyse, das Entwicklern dabei hilft, Schwachstellen in ihrer Codebasis zu erkennen und zu beheben. Es lässt sich nahtlos in Ihre IDE und Ihren Entwicklungsworkflow integrieren und liefert in Echtzeit Hinweise auf potenzielle Sicherheitsprobleme.
So hilft Snyk dabei, Command-Injection-Schwachstellen in Go zu erkennen
Im gezeigten Go-Beispiel erstellt die Funktion downloadAndResize mit einer Benutzereingabe einen Shell-Befehl:
Dieser Code ist anfällig für Command Injection, da er Benutzereingaben direkt in den Befehlsstring einfügt.
Was wäre, wenn in Ihrem Team Entwickler wären, die Command-Injection-Schwachstellen nicht kennen?
Könnten Sie den dateiübergreifenden Aufrufpfad der statischen Analyse in einem Code-Review problemlos nachvollziehen, um diese Command-Injection-Schwachstelle zu finden?
Hier kommt Snyk ins Spiel.
Sehen Sie, wie einfach Anwendungssicherheit wird: Die Snyk Code extension hebt anfälligen Code direkt im VS-Code-Editor in Echtzeit hervor:

Snyk kann solche Schwachstellen erkennen, indem es Ihren Go-Code scannt und Stellen kennzeichnet, an denen Benutzereingaben unsicher in Shell-Befehlen verwendet werden. Snyk zeigt Ihnen echte Commits aus anderen Open-Source-Projekten, mit denen genau diese Schwachstelle behoben wurde. So erhalten Sie mehrere Codebeispiele, die zeigen, wie eine gute Lösung aussieht.
Wenn Sie zusätzlich auf die Registerkarte ISSUE OVERVIEW klicken, erhalten Sie weitere Details und Best Practices zur Vermeidung von Command Injection. Dort finden Sie ausführliche Einblicke und Empfehlungen zur Minderung dieser Risiken – direkt in der IDE-Ansicht, während Sie programmieren. So können Sie die vorgeschlagenen Maßnahmen umsetzen, ohne kostspielig den Kontext wechseln zu müssen:

Wenn Sie mehr darüber erfahren möchten, wie Sie Ihren Code vor solchen Schwachstellen schützen können, installieren Sie die IDE-Erweiterung Snyk Code und verbinden Sie Ihre Git-Projekte, um Sicherheitsprobleme in Ihren Go-Anwendungen zu erkennen und zu beheben. Das geht ganz einfach und kostenlos: Melden Sie sich an, um Ihren Code auf Schwachstellen zu scannen.
Kostenloses Tool zur Codeprüfung
Sichern Sie Ihren Code, bevor Sie Ihren nächsten Commit erstellen.



