In this article
So schreiben Sie sicheren Go-Code
Sprechen wir darüber, warum es wichtig ist, sicheren Code in Go zu schreiben. Sicheres Programmieren ist eine grundlegende Verantwortung von Entwicklerinnen und Entwicklern – besonders in einer Sprache wie Go, die häufig zum Aufbau skalierbarer und effizienter Systeme eingesetzt wird. Die Einfachheit und Leistungsfähigkeit von Go machen die Sprache zu einer beliebten Wahl für Cloud-native Anwendungen, insbesondere aufgrund ihrer Verbindung zum Kubernetes-Ökosystem. Sicherheitslücken können diese Vorteile jedoch zunichtemachen. Sicherer Go-Code schützt Ihre Anwendung und Nutzerdaten und erhält das Vertrauen Ihrer Nutzerinnen und Nutzer.
Welche dieser häufigen Sicherheitslücken betreffen Anwendungen in Go?
Überblick über häufige Sicherheitslücken in Go-Anwendungen
Go-Anwendungen sind anfällig für verschiedene Sicherheitslücken, die Integrität, Vertraulichkeit und Verfügbarkeit Ihrer Anwendung beeinträchtigen können – auch als CIA-Triade in der Sicherheitssprache bekannt. Zu den häufigsten Schwachstellen gehören:
SQL-Injection entsteht, wenn nicht vertrauenswürdige Eingaben in SQL-Abfragen verkettet werden. So können Angreifer die Ausführung der Abfrage manipulieren.
Command-Injection entsteht, wenn Nutzereingaben in Shell-Befehlen nicht ordnungsgemäß verarbeitet werden. Das kann zur Ausführung beliebiger Befehle führen.
Server-Side Request Forgery (SSRF) entsteht, wenn eine Anwendung eine vom Nutzer angegebene URL abruft. Dadurch können Angreifer möglicherweise Anfragen an interne Dienste senden.
Die Rolle von Tools wie Snyk Code beim Erkennen und Beheben von Schwachstellen
Tools wie Snyk Code sind entscheidend, um Schwachstellen in Go-Anwendungen zu erkennen und zu beheben. Snyk Code scannt Ihren Quellcode auf Sicherheitsprobleme und liefert umsetzbare Erkenntnisse und Empfehlungen zur Behebung. Wenn Sie Snyk in Ihren Entwicklungsworkflow integrieren, können Sie Schwachstellen frühzeitig erkennen, das Risiko von Sicherheitsverletzungen senken und eine sichere Codebasis erhalten.
Go-Beispiel: SQL-Injection mit Snyk Code erkennen
Sehen Sie sich den folgenden Go-Codeausschnitt an, der für SQL-Injection anfällig ist:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
_ "github.com/lib/pq"
)
func main() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
db, err := sql.Open("postgres", "user=youruser dbname=yourdb sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
rows, err := db.Query(query)
if err != nil {
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
defer rows.Close()
// Process rows...
})
log.Fatal(http.ListenAndServe(":8080", nil))
}In diesem Beispiel wird der Parameter username direkt in die SQL-Abfrage eingefügt. Dadurch ist der Code für SQL-Injection anfällig. Snyk Code kann diese Schwachstelle erkennen und vorschlagen, parametrisierte Abfragen zu verwenden, um sie zu verhindern:
query := "SELECT * FROM users WHERE username = $1"
rows, err := db.Query(query, username)1. Nutzereingaben in Go validieren und bereinigen
Die Validierung und Bereinigung von Eingaben sind entscheidend, um Ihre Go-Anwendungen vor gängigen Angriffsvektoren wie SQL-Injection und Cross-Site-Scripting (XSS) zu schützen. Eingaben aus Abfrageparametern, JSON-Antworttexten von HTTP-Anfragen oder Datenbanken sollten niemals als vertrauenswürdig gelten. Validieren Sie Eingaben immer anhand erwarteter Schemas, bereinigen Sie sie bei Bedarf und kodieren Sie Ausgaben kontextgerecht.
Best Practices zur Eingabevalidierung in Go
Beachten Sie beim Umgang mit Nutzereingaben in Go die folgenden Best Practices:
Eingabeschemas definieren: Legen Sie mit strikten Schemas fest, welche Eingaben gültig sind. Bibliotheken wie
go-playground/validatorkönnen dabei helfen, diese Regeln durchzusetzen.Eingaben bereinigen: Entfernen oder maskieren Sie potenziell schädliche Zeichen aus Nutzereingaben. Kodieren Sie Eingaben beispielsweise als HTML, um XSS zu verhindern. Diese Vorgehensweise ist vor allem bei Template-Engines hilfreich und wird von entsprechenden Tools umgesetzt.
Parametrisierte Abfragen verwenden: Verhindern Sie SQL-Injection mit parametrisierten Abfragen statt mit Zeichenfolgenverkettung.
Ausgaben kodieren: Kodieren Sie Ausgaben kontextabhängig, etwa als HTML für Webseiten oder als JSON für API-Antworten.
Go-Beispiel: Parameterbindung zum Schutz vor SQL-Injection
SQL-Injection ist eine schwerwiegende Sicherheitslücke auf Anwendungsebene. Sie entsteht, wenn Nutzereingaben direkt in SQL-Abfragen übernommen werden. Verwenden Sie parametrisierte Abfragen, um dies zu verhindern:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func getUser(db *sql.DB, userID string) (User, error) {
var user User
err := db.QueryRow("SELECT id, name FROM users WHERE id = ?", userID).Scan(&user.ID, &user.Name)
if err != nil {
return user, err
}
return user, nil
}In diesem Beispiel wird der Platzhalter ? in der SQL-Abfrage sicher durch den Parameter userID ersetzt, wodurch SQL-Injection-Angriffe verhindert werden. Mit dieser Best Practice kann die Datenbank-Engine die ursprüngliche Bedeutung der Abfrage von den darin verwendeten Werten unterscheiden.
2. Subshell-Aufrufe in Go vermeiden
Subshell-Aufrufe in Go bezeichnen das Starten von Unterprozessen mit Funktionen wie exec.CommandContext() oder exec.Command(). Diese Funktionen bieten zwar Flexibilität, bergen aber auch erhebliche Sicherheitsrisiken, insbesondere das Risiko von Command-Injection-Schwachstellen. Command-Injection entsteht, wenn ein Angreifer Eingabedaten manipuliert, um beliebige Befehle auf dem Hostsystem auszuführen. Dies kann zu unbefugtem Datenzugriff, Datenbeschädigung oder sogar einer umfassenden Kompromittierung des Systems führen.
Alternativen zu Subshell-Aufrufen in Go: native Bibliotheks-APIs, FFI usw.
Um die mit Subshell-Aufrufen verbundenen Risiken zu mindern, sollten Entwicklerinnen und Entwickler für Aufgaben, die sonst externe Befehle erfordern würden, bevorzugt die nativen Bibliotheks-APIs von Go oder die Foreign Function Interface (FFI) verwenden. Die Standardbibliothek von Go ist leistungsfähig und bietet zahlreiche Hilfsfunktionen für die Dateiverarbeitung, Bildbearbeitung und mehr. Diese können externe Befehle oft überflüssig machen.
Go-Beispiel: Command-Injection bei der Bildbearbeitung
Stellen Sie sich das folgende Beispiel vor, bei dem eine Nutzereingabe zur Bearbeitung einer Bilddatei verwendet wird. Es zeigt einen häufigen Fehler, der zu Command-Injection führen kann:
package main
import (
"os/exec"
)
func manipulateImage(targetFilename string) error {
convertCmd := fmt.Sprintf("convert %s -resize %sx%s %s", targetFilename, fileSize, fileSize, targetFilename)
_, err = exec.CommandContext(ctx, "sh", "-c", convertCmd).CombinedOutput()
}
Wenn targetFilename in diesem Beispiel aus einer Nutzereingabe stammt, könnte ein Angreifer durch einen manipulierten Dateinamen zusätzliche Befehle einschleusen. Ein Dateiname wie image.png; rm -rf /tmp könnte beispielsweise einen destruktiven Befehl ausführen, wenn er nicht ordnungsgemäß bereinigt wird.
Das Risiko entsteht dadurch, dass Nutzereingaben direkt zum Erstellen von Shell-Befehlen verwendet werden und der Code keine vorbereiteten Befehlsargumente zum Erstellen des Befehls nutzt. Mit den folgenden Maßnahmen können Sie dieses Risiko mindern:
Native Bibliotheken verwenden: Verwenden Sie die nativen Go-Bibliotheken, statt sich auf externe Befehlszeilenprogramme zu verlassen.
Eingaben validieren und bereinigen: Validieren und bereinigen Sie Nutzereingaben immer, um sicherzustellen, dass sie den erwarteten Mustern entsprechen. So verhindern Sie, dass schädliche Eingaben ausgeführt werden. Der anfällige Beispielcode sollte keine direkte Shell-Interpolation mit
sh -cverwenden, sondern den Befehl direkt ausführen. Dabei besteht jedoch auch das Risiko einer Argument-Injection, weshalb Argumente ordnungsgemäß bereinigt und maskiert werden müssen.
3. URLs sicher zusammensetzen
Die Anforderung, sichere URLs zusammenzusetzen, steht in direktem Zusammenhang mit einer der zehn wichtigsten Sicherheitslücken von OWASP: Server-Side Request Forgery (SSRF). SSRF ist eine kritische Sicherheitslücke, bei der ein Angreifer einen Server dazu bringen kann, unbeabsichtigte HTTP-Anfragen zu senden – etwa an interne Ressourcen, andere private Microservices oder beliebige andere nicht autorisierte Dienste. Dies kann zu unbefugtem Zugriff auf interne Dienste, Datenexfiltration oder sogar zur Remote-Codeausführung führen. Ausführliche Informationen zu SSRF finden Sie in der Snyk Learn-Lektion zu SSRF.
Strategien zum sicheren Zusammensetzen von URLs in Go
Beim Zusammensetzen von URLs in Go, insbesondere wenn Nutzereingaben beteiligt sind, müssen Sie unbedingt Maßnahmen zum Schutz vor SSRF umsetzen. Hier sind einige Best Practices:
URLs validieren: Prüfen Sie von Nutzern angegebene URLs immer anhand einer Zulassungsliste mit geprüften und freigegebenen Domains oder Mustern. So stellen Sie sicher, dass nur legitime Anfragen gesendet werden.
URL-Parsing verwenden: Verwenden Sie das Go-Paket
net/url, um URLs zu parsen und zu validieren. So lässt sich eine URL in ihre Bestandteile zerlegen und jeder Teil überprüfen. In diesem Schritt können Sie beispielsweise durch eine Sicherheitskontrolle sicherstellen, dass nur bestimmte unterstützte Protokollschemata (https://) und nichthttpoder andere zugelassen sind.Zugriff auf interne Netzwerke beschränken: Stellen Sie mithilfe von Kubernetes-Sidecars, HTTP-Proxys, API-Gateways und anderen netzwerkbewussten Sicherheitskontrollen sicher, dass Ihre Anwendung keine internen IP-Adressen oder sensiblen Endpunkte anfragt.
Timeouts und Limits festlegen: Geeignete Timeouts und Größenlimits für ausgehende Anfragen sind zwar nicht direkt mit SSRF verbunden, helfen aber, Ressourcenerschöpfung und umfangreiche Datenexfiltration zu verhindern und so die Auswirkungen eines SSRF-Angriffs zu minimieren.
Go-Beispiel: SSRF-Schwachstelle durch vom Nutzer angegebene URLs
Das folgende Beispiel zeigt einen unsicheren Umgang mit vom Nutzer angegebenen URLs, der zu einer potenziellen SSRF-Schwachstelle führt:
package main
import (
"net/http"
)
func fetchUserProfileImage(userURL string) (*http.Response, error) {
// Directly using user input to fetch remote data
resp, err := http.Get(userURL)
return resp, err
}In diesem Code übernimmt die Funktion fetchUserProfileImage eine URL vom Nutzer und verwendet sie direkt in einer HTTP-GET-Anfrage. Ein Angreifer kann dies ausnutzen, um Anfragen an interne Dienste zu senden.
Fazit
Sicheren Go-Code zu schreiben erfordert Wachsamkeit und die Einhaltung von Best Practices. Hier sind die wichtigsten Sicherheitsmaßnahmen für die sichere Go-Entwicklung, die wir besprochen haben:
Nutzereingaben, die Codepfade in Go durchlaufen, validieren und bereinigen.
Subshell-Operationen vermeiden und native Go-APIs und FFI bevorzugen, sofern verfügbar.
URLs aus Nutzereingaben mit größter Vorsicht behandeln, um SSRF-Schwachstellen zu verhindern.
Snyk Code für kontinuierliche Sicherheitsscans nutzen
Machen Sie den nächsten Schritt, um Ihre Go-Anwendungen zu schützen. Erstellen Sie noch heute ein kostenloses Snyk-Konto und integrieren Sie Sicherheit in Ihren Entwicklungsworkflow. Mit Snyk können Sie sicher Anwendungen entwickeln und Ihre Nutzer schützen.
Damit Ihr Go-Code sicher bleibt, nutzen Sie Snyk Code. Durch kontinuierliche Sicherheitsscans können Sie Schwachstellen in Ihrer Codebasis erkennen und beheben, bevor sie zum Problem werden.
Sehen Sie Snyk Code in meiner VS-Code-IDE in Aktion: Dort erkennt die Lösung Schwachstellen und nutzt eine Machine-Learning-Engine, um mir Vorschläge für besseren, sicheren Code in Golang zu machen:

Machen Sie bei Fetch the Flag 2026 mit!
Stellen Sie Ihre Security-Kenntnisse bei unserem Capture-the-Flag-Event vom 12. bis 13. Februar auf die Probe, von 12 Uhr mittags bis 12 Uhr mittags ET.