Warum selbst Go-Entwickler Hackern auf den Leim gehen?
\n\nGo ist bekannt für seine Geschwindigkeit, Einfachheit und integrierten Sicherheitsfunktionen. Aber sich mit dem Satz „Ich programmiere in Go, also bin ich sicher“ zu beruhigen, ist eine gefährliche Illusion. SQL-Injection und XSS-Angriffe sind keine Relikte der Vergangenheit. Sie legen täglich Websites lahm und leeren Datenbanken – und Go ist keine Ausnahme.
\n\nDas Problem ist, dass Go dem Entwickler zwar mächtige Werkzeuge an die Hand gibt, sie aber nicht automatisch für ihn einsetzt. Sie können versehentlich eine SQL-Abfrage mit fmt.Sprintf zusammenbauen oder einen Benutzerkommentar ohne Escaping ausgeben – und schon erhält ein Angreifer Zugriff auf Ihre Datenbank oder stiehlt die Cookies Ihrer Besucher.
In diesem Artikel analysieren wir die beiden gefährlichsten Angriffsarten – SQL-Injection und Cross-Site Scripting (XSS) – und zeigen, wie Sie sie mit nativen Go-Mitteln und gängigen Bibliotheken verhindern. Sie erhalten keine Theorie, sondern fertige Code-Vorlagen, die Sie noch heute in Ihr Projekt einbauen können.
\n\nSQL-Injection in Go: Wie sie entsteht und wie Sie sie im Keim ersticken
\n\nTypischer Fehler: String-Konkatenation
\n\nStellen Sie sich vor, Sie haben einen Endpunkt, um einen Benutzer anhand seiner ID abzurufen. Die naivste (und gefährlichste) Art, eine Abfrage zu schreiben, sieht so aus:
\n\npackage main\n\nimport (\n "database/sql"\n "fmt"\n "log"\n _ "github.com/lib/pq"\n)\n\nfunc getUserByID(db *sql.DB, userID string) {\n // GEFÄHRLICH! String-Konkatenation\n query := fmt.Sprintf("SELECT id, name, email FROM users WHERE id = '%s'", userID)\n rows, err := db.Query(query)\n if err != nil {\n log.Fatal(err)\n }\n defer rows.Close()\n // ... Verarbeitung\n}\n\nWenn ein Angreifer die Zeichenkette 1' OR '1'='1 als userID übergibt, wird die Abfrage zu:
SELECT id, name, email FROM users WHERE id = '1' OR '1'='1'\n\nVoilà – er erhält alle Benutzer der Datenbank. Und wenn er 1'; DROP TABLE users; -- eingibt, verlieren Sie die Tabelle.
Lösung: Parametrisierte Abfragen (Prepared Statements)
\n\nDer Go-Treiber für PostgreSQL, MySQL und SQLite unterstützt Prepared Statements. Sie trennen den SQL-Code von den Daten, und der Treiber escapet automatisch gefährliche Zeichen.
\n\nfunc getUserByIDSafe(db *sql.DB, userID string) {\n // SICHER: Parametrisierte Abfrage\n query := "SELECT id, name, email FROM users WHERE id = $1"\n rows, err := db.Query(query, userID)\n if err != nil {\n log.Fatal(err)\n }\n defer rows.Close()\n // ... Verarbeitung\n}\n\nBeachten Sie $1 – dies ist ein Platzhalter für das erste Argument. Bei MySQL wird das Zeichen ? verwendet. Selbst wenn userID nun 1' OR '1'='1 enthält, behandelt der Treiber dies als Zeichenkettenwert und nicht als Teil des SQL-Codes.
Arbeiten mit dem Paket sqlx
\n\nDie Bibliothek github.com/jmoiron/sqlx macht den Code noch sauberer. Sie unterstützt benannte Parameter und das automatische Einlesen in Strukturen:
import "github.com/jmoiron/sqlx"\n\ntype User struct {\n ID int `db:"id"`\n Name string `db:"name"`\n Email string `db:"email"`\n}\n\nfunc getUserByIDSqlx(db *sqlx.DB, userID string) (*User, error) {\n user := &User{}\n // Benannter Parameter :id\n err := db.Get(user, "SELECT * FROM users WHERE id = :id", sqlx.Named("id", userID))\n if err != nil {\n return nil, err\n }\n return user, nil\n}\n\nWichtig: Selbst wenn Sie ein ORM wie GORM oder Ent verwenden, stellen Sie sicher, dass es für die Verwendung von Prepared Statements konfiguriert ist. Einige ältere ORM-Versionen erlaubten „rohe“ Abfragen.
\n\nXSS in Go: Wenn Ihre Website sich selbst ins Bein schießt
\n\nWie ein XSS-Angriff funktioniert
\n\nXSS (Cross-Site Scripting) tritt auf, wenn Sie Benutzerinhalte ohne Escaping auf einer Seite einfügen. Bö