6 Empfehlungen für die Entwicklung sicherer Go-Anwendungen

In den letzten Jahren hat sich Golang immer weiter verbreitet. Erfolgreiche Projekte wie Docker, Kubernetes und Terraform haben große Einsätze in dieser Programmiersprache gemacht. Go ist zum De-facto-Standard für die Erstellung von Befehlszeilentools geworden. Und wenn wir über Sicherheit sprechen, stellt sich heraus, dass in diesem Bereich bei Go alles in bester Ordnung ist. Seit 2002 wurde nämlich nur eine Golang-Sicherheitslücke in der CVE-Registrierung registriert.

Die Tatsache, dass die Programmiersprache keine Sicherheitslücken aufweist, bedeutet jedoch nicht, dass jede in dieser Sprache geschriebene Anwendung absolut sicher ist. Wenn der Entwickler sich nicht an bestimmte Empfehlungen hält, kann er auch in einer solchen Sprache eine ungeschützte Anwendung erstellen. Im Fall von Go finden Sie ähnliche Empfehlungen in den OWASP- Materialien.



Der Autor des Artikels, dessen Übersetzung wir heute veröffentlichen, formulierte auf der Grundlage von OWASP-Daten 6 Empfehlungen für die Entwicklung sicherer Anwendungen auf Go.

1. Überprüfen Sie die Benutzereingaben


Die Validierung der vom Benutzer eingegebenen Daten ist nicht nur erforderlich, um das ordnungsgemäße Funktionieren der Anwendung sicherzustellen. Sie zielt auch auf die Bekämpfung von Eindringlingen ab, die mit Hilfe von Daten, die auf besondere Weise aufbereitet wurden, versuchen, das System zu stören. Durch die Überprüfung der Benutzerdaten können Benutzer sicherer mit der Anwendung arbeiten, da sie vor häufigen Fehlern geschützt sind. Durch die Analyse von Benutzerbefehlen können Sie beispielsweise verhindern, dass versucht wird, mehrere Datensätze gleichzeitig zu löschen, wenn eine solche Aktion zu einer Fehlfunktion des Systems führen kann.

Sie können standardmäßige Go-Pakete verwenden, um Benutzereingaben zu überprüfen. strconv Paket strconv Sie beispielsweise Zeichenfolgendaten in andere Datentypen konvertieren. Go unterstützt dank regexp auch reguläre Ausdrücke. Sie können verwendet werden, um komplexe Datenvalidierungsskripten zu implementieren. Obwohl in der Entwicklungsumgebung von Go in der Regel Standardbibliotheken bevorzugt werden, gibt es Pakete von Drittanbietern, mit denen Daten überprüft werden sollen. Zum Beispiel Validator . Mit diesem Paket können komplexe Datenstrukturen oder einzelne Werte auf einfache Weise überprüft werden. Der folgende Code überprüft beispielsweise die User auf die Richtigkeit der darin enthaltenen E-Mail-Adresse:

 package main import (  "fmt"  "gopkg.in/go-playground/validator.v9" ) type User struct {  Email string `json:"email" validate:"required,email"`  Name string `json:"name" validate:"required"` } func main() {  v := validator.New()  a := User{    Email: "a",  }  err := v.Struct(a)  for _, e := range err.(validator.ValidationErrors) {    fmt.Println(e)  } } 

2. Verwenden Sie HTML-Vorlagen


XSS (Cross Site Scripting, Cross Site Scripting) ist eine schwerwiegende und weit verbreitete Sicherheitsanfälligkeit. Die XSS-Sicherheitsanfälligkeit ermöglicht es einem Angreifer, schädlichen Code in die Anwendung einzufügen, der sich auf die von der Anwendung generierten Daten auswirken kann. Beispielsweise kann jemand JavaScript-Code als Teil einer Abfragezeichenfolge in einer URL an die Anwendung senden. Wenn die Anwendung eine solche Anforderung verarbeitet, kann dieser JavaScript-Code ausgeführt werden. Infolgedessen sollte der Anwendungsentwickler dies erwarten und die vom Benutzer empfangenen Daten bereinigen.

Go verfügt über ein HTML- / Vorlagenpaket , mit dem Sie HTML-Code generieren können, der vor böswilligen Fragmenten geschützt ist. Infolgedessen erkennt der Browser, der die angegriffene Anwendung anzeigt, anstelle von Code wie <script>alert('You've Been Hacked!');</script> , der den Benutzer darüber informiert, dass er gehackt wurde, bösartigen JavaScript-Code als Klartext . So sieht ein HTTP-Server mit HTML-Vorlagen aus:

 package main import (  "html/template"  "net/http" ) func handler(w http.ResponseWriter, r *http.Request) {  param1 := r.URL.Query().Get("param1")  tmpl := template.New("hello")  tmpl, _ = tmpl.Parse(`{{define "T"}}{{.}}{{end}}`)  tmpl.ExecuteTemplate(w, "T", param1) } func main() {  http.HandleFunc("/", handler)  http.ListenAndServe(":8080", nil) } 

Es gibt Bibliotheken von Drittanbietern, die Sie beim Entwickeln von Webanwendungen auf Go verwenden können. Angenommen, dies ist das Gorilla-Web-Toolkit . Dieses Toolkit enthält Bibliotheken, die dem Entwickler helfen, beispielsweise Werte in Authentifizierungscookies zu codieren. Und hier ist ein weiteres Projekt - nosurf . Dies ist ein HTTP-Paket, mit dessen Hilfe CSRF- Angriffe verhindert werden.

3. Schützen Sie Ihr Projekt vor SQL Injection


Wenn Sie nicht neu in der Webentwicklung sind, kennen Sie wahrscheinlich SQL-Injection-Angriffe (mithilfe einer Methode zum Injizieren von beliebigem SQL-Code in Abfragen). Die entsprechende Schwachstelle steht weiterhin an erster Stelle im OWASP Top 10 Ranking. Um Anwendungen vor SQL Injection zu schützen, müssen Sie einige Funktionen berücksichtigen. Das erste, was sichergestellt werden muss, ist, dass der Benutzer, der eine Verbindung zur Datenbank herstellt, eingeschränkte Berechtigungen hat. Es wird außerdem empfohlen, die vom Benutzer eingegebenen Daten zu löschen, über die wir bereits gesprochen haben, oder Sonderzeichen zu umgehen und die HTMLEscapeString- Funktion aus dem html/template Paket zu verwenden.

Das Wichtigste beim Schutz vor SQL-Injection ist jedoch die Verwendung von parametrisierten Abfragen (vorbereiteten Ausdrücken). In Go werden Ausdrücke nicht für eine Verbindung, sondern für eine Datenbank vorbereitet. Hier ist ein Beispiel für die Verwendung parametrisierter Abfragen:

 customerName := r.URL.Query().Get("name") db.Exec("UPDATE creditcards SET name=? WHERE customerId=?", customerName, 233, 90) 

Was aber, wenn das Datenbankmodul die Verwendung vorbereiteter Ausdrücke nicht unterstützt? Was aber, wenn dies die Abfrageleistung beeinträchtigt? In solchen Fällen können Sie die Funktion db.Query() verwenden, aber zuerst müssen Sie daran denken, die Benutzereingaben zu löschen. Um Angriffe mit SQL Injection zu verhindern, können Sie Bibliotheken von Drittanbietern wie sqlmap verwenden .

Es sollte beachtet werden, dass trotz aller Bemühungen, Anwendungen vor SQL-Angriffen zu schützen, Angreifer diese Angriffe manchmal immer noch erfolgreich ausführen können. Sprich - durch externe Anwendungsabhängigkeiten. Um die Projektsicherheit zu erhöhen, können Sie die Anwendungssicherheit mit den entsprechenden Tools überprüfen. Zum Beispiel die Tools der Sqreen- Plattform.

4. Verschlüsseln Sie wichtige Informationen


Wenn eine Person beispielsweise in der BASE64-Codierung eine bestimmte Zeichenfolge nicht lesen kann, bedeutet dies nicht, dass die darin verborgenen Informationen zuverlässig geschützt sind. Daher müssen wichtige Informationen verschlüsselt werden, um sie vor Eindringlingen zu schützen, die Zugriff auf verschlüsselte Daten erhalten können. Normalerweise verschlüsselte Informationen wie Datenbankkennwörter, Benutzerkennwörter, persönliche Benutzerdaten.

Im Rahmen des OWASP-Projekts werden einige Empfehlungen zu bevorzugten Verschlüsselungsalgorithmen ausgesprochen. Zum Beispiel ist es bcrypt , PDKDF2 , Argon2 , scrypt . Es gibt ein Paket Go, crypto , das zuverlässige Implementierungen verschiedener Verschlüsselungsalgorithmen enthält. Hier ist ein Beispiel mit dem bcrypt Algorithmus:

 package main import (  "database/sql"  "context"  "fmt"  "golang.org/x/crypto/bcrypt" ) func main() {  ctx := context.Background()  email := []byte("john.doe@somedomain.com")  password := []byte("47;u5:B(95m72;Xq")  hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)  if err != nil {    panic(err)  }  stmt, err := db.PrepareContext(ctx, "INSERT INTO accounts SET hash=?, email=?")  if err != nil {    panic(err)  }  result, err := stmt.ExecContext(ctx, hashedPassword, email)  if err != nil {    panic(err)  } } 

Beachten Sie, dass Sie auch bei Verwendung der Verschlüsselung für die sichere Übertragung von Informationen zwischen Diensten sorgen müssen. Beispielsweise sollten Sie das Kennwort des Benutzers nicht im Klartext übertragen, selbst wenn es verschlüsselt gespeichert ist. Bei der Übertragung von Daten im Internet ist von der Annahme auszugehen, dass sie von einem Angreifer abgefangen werden können, der Daten von Anforderungen sammelt, die in einem bestimmten System ausgeführt werden. Der Angreifer kann die gesammelten Informationen mit den von anderen Systemen empfangenen Daten abgleichen und dadurch das für ihn interessante Projekt knacken.

5. HTTPS erzwingen


Heutzutage benötigen die meisten Browser Sites, die mit ihnen geöffnet werden, um HTTPS zu unterstützen. Chrome zeigt beispielsweise eine Benachrichtigung in der Adressleiste an, wenn Daten mit der Site ohne Verwendung von HTTPS ausgetauscht werden. In einer Organisation, die ein bestimmtes Projekt unterstützt, kann eine Sicherheitsrichtlinie angewendet werden, die darauf abzielt, einen sicheren Datenaustausch zwischen den Diensten zu organisieren, aus denen dieses Projekt besteht. Um die Sicherheit der Verbindungen zu gewährleisten, muss daher nicht nur darauf geachtet werden, dass die Anwendung an Port 443 empfangsbereit ist. Das Projekt muss über die entsprechenden Zertifikate verfügen. Außerdem muss die erzwungene Verwendung von HTTPS organisiert werden, um zu verhindern, dass der Angreifer auf den HTTP-Datenaustausch umschaltet.

Hier ist eine Beispielanwendung, die HTTPS erzwingt:

 package main import (  "crypto/tls"  "log"  "net/http" ) func main() {  mux := http.NewServeMux()  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {    w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")    w.Write([]byte("This is an example server.\n"))  })  cfg := &tls.Config{    MinVersion:        tls.VersionTLS12,    CurvePreferences:     []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},    PreferServerCipherSuites: true,    CipherSuites: []uint16{      tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,      tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,      tls.TLS_RSA_WITH_AES_256_GCM_SHA384,      tls.TLS_RSA_WITH_AES_256_CBC_SHA,    },  }  srv := &http.Server{    Addr:     ":443",    Handler:   mux,    TLSConfig:  cfg,    TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),  }  log.Fatal(srv.ListenAndServeTLS("tls.crt", "tls.key")) } 

Bitte beachten Sie, dass die Anwendung auf Port 443 lauscht. Und hier ist die Leitung, die für die erzwungene Verwendung von HTTPS verantwortlich ist:

 w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains") 

Darüber hinaus kann die Angabe des Servernamens in der TLS-Konfiguration sinnvoll sein:

 config := &tls.Config{ServerName: "yourSiteOrServiceName"} 

Es wird empfohlen, dass Sie immer die Verschlüsselung von Daten verwenden, die zwischen Teilen des Systems übertragen werden, auch wenn die Webanwendung eine Art internes Chat-Unternehmen ist. Stellen Sie sich die möglichen Folgen eines Angriffs vor, bei dem ein Angreifer Anwendungsdaten abfängt, die über ein Netzwerk übertragen werden. Wann immer möglich, lohnt es sich, sich auf mögliche Angriffe auf das Projekt vorzubereiten, um das Leben der Angreifer so weit wie möglich zu verkomplizieren.

6. Achten Sie auf Fehlerbehandlung und Protokollierung.


Dieser Artikel ist der letzte auf unserer Liste, aber definitiv nicht der letzte, der an Bedeutung gewinnt. Hier geht es um Fehlerbehandlung und Protokollierung.

Um die in der Produktion auftretenden Probleme rechtzeitig zu bewältigen, müssen Sie die Anwendung mit entsprechenden Werkzeugen ausstatten. Gleichzeitig sollten Sie sehr vorsichtig mit Fehlermeldungen sein, die Benutzern angezeigt werden. Sie sollten dem Benutzer nicht mitteilen, was schief gelaufen ist. Tatsache ist, dass ein Angreifer diese Informationen verwenden kann, um herauszufinden, welche Dienste und Technologien im Projekt verwendet werden. Darüber hinaus ist daran zu erinnern, dass die Protokolle, obwohl sie normalerweise in einem positiven Licht wahrgenommen werden, irgendwo gespeichert werden. Wenn das Anwendungsprotokoll in die falschen Hände gerät, kann die Analyse der darin enthaltenen Informationen dazu beitragen, das Projekt anzugreifen.

Das Erste, was hier berücksichtigt werden muss, ist, dass es keine Ausnahmen für Go gibt. Dies bedeutet, dass Fehler in Anwendungen, die in Go geschrieben wurden, nicht so behandelt werden wie Fehler in Anwendungen, die in anderen Sprachen geschrieben wurden. Normalerweise sieht es so aus:

 if err != nil {    //   } 

Darüber hinaus verfügt Go über eine Standardbibliothek für die Arbeit mit Protokollen, die als log . Hier ist ein einfaches Beispiel für seine Verwendung:

 package main import (  "log" ) func main() {  log.Print("Logging in Go!") } 

Es gibt Bibliotheken von Drittanbietern für die Organisation der Protokollierung. Zum Beispiel ist dies logrus , glog , loggo . Hier ist ein kleines Beispiel für die Verwendung von logrus :

 package main import (  "os"  log "github.com/sirupsen/logrus" ) func main() {  file, err := os.OpenFile("info.log", os.O_CREATE|os.O_APPEND, 0644)  if err != nil {    log.Fatal(err)  }  defer file.Close()  log.SetOutput(file)  log.SetFormatter(&log.JSONFormatter{})  log.SetLevel(log.WarnLevel)  log.WithFields(log.Fields{    "animal": "walrus",    "size":  10,  }).Info("A group of walrus emerges from the ocean") } 

Verwenden Sie schließlich beim Arbeiten mit Daten, die in die Protokolle fallen, die Sicherheitsempfehlungen, die wir bereits besprochen haben. Insbesondere löschen und verschlüsseln Sie solche Daten.

Zusammenfassung


Die hier gegebenen Empfehlungen sind ein Minimum, das ein in Go geschriebenes Projekt haben sollte. Wenn es sich bei dem betreffenden Projekt jedoch um ein Befehlszeilendienstprogramm handelt, muss kein Schutz für den über das Netzwerk übertragenen Datenverkehr implementiert werden. Die restlichen Tipps gelten für nahezu jede Art von Anwendung. Wenn Sie sich eingehend mit dem Thema der Entwicklung sicherer Anwendungen auf Go befassen möchten, lesen Sie das OWASP-Buch zu diesem Thema. Und hier ist ein Repository, das Links zu verschiedenen Tools enthält, die die Sicherheit von Go-Anwendungen gewährleisten sollen.

Was auch immer Sie im Bereich der Sicherung Ihrer Anwendungen tun, denken Sie daran, dass die Sicherheit immer verbessert werden kann und Angreifer ständig neue Angriffsmöglichkeiten finden. Daher müssen Sie den Anwendungsschutz nicht von Fall zu Fall, sondern ständig durchführen.

Sehr geehrte Leser! Wie schützen Sie Ihre in Go geschriebenen Anwendungen?

Source: https://habr.com/ru/post/de484614/


All Articles