6 meilleures pratiques de sécurité pour Go

Une traduction de l'article a été préparée spécialement pour les étudiants du cours de développeur de Golang .





La popularité de Golang a considérablement augmenté ces dernières années. Des projets réussis tels que Docker , Kubernetes et Terraform ont fait un gros pari sur ce langage de programmation. Go est récemment devenu la norme de facto pour la création d'outils de ligne de commande. En termes de sécurité, Go a réussi à gérer selon ses rapports de vulnérabilité, n'ayant qu'un seul registre CVE depuis 2002 .

Cependant, l'absence de vulnérabilités ne signifie pas que le langage de programmation est hautement sécurisé. Nous, les humains, pouvons créer des applications dangereuses si nous ne respectons pas certaines règles. Par exemple, connaître les règles pour écrire du code sécurisé depuis OWASP, nous pouvons réfléchir à la façon d'appliquer ces méthodes lors de l'utilisation de Go. Et c'est exactement ce que je vais faire cette fois. Dans cet article, je vais vous montrer six pratiques à prendre en compte lors du développement avec Go.

1. Vérifiez l'entrée


La vérification des entrées des utilisateurs est nécessaire non seulement à des fins fonctionnelles, mais elle permet également d'éviter les attaques de cybercriminels qui nous envoient des données intrusives qui pourraient endommager le système. De plus, vous aidez les utilisateurs à utiliser cet outil en toute confiance, en les empêchant de commettre des erreurs stupides et courantes. Par exemple, vous pouvez empêcher un utilisateur d'essayer de supprimer plusieurs entrées à la fois.

Pour tester l'entrée utilisateur, vous pouvez utiliser des packages Go natifs tels que strconv pour gérer la conversion de chaînes en d'autres types de données. Go prend également en charge les expressions régulières avec regexpdes vérifications complexes. Bien que Go préfère utiliser des bibliothèques natives, il existe des packages tiers tels que validator. Avec validator, vous pouvez activer la validation de structures ou de champs individuels beaucoup plus facilement. Par exemple, le code suivant vérifie que la structure Usercontient une adresse e-mail valide:

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. Utilisez des modèles HTML


Une vulnérabilité critique courante est le scriptage intersite ou XSS. Le principal exploit est qu'un attaquant peut injecter du code malveillant dans l'application pour modifier la sortie. Par exemple, quelqu'un peut envoyer du code JavaScript dans le cadre d'une chaîne de requête dans une URL. Lorsque l'application renvoie la valeur de l'utilisateur, le code JavaScript peut être exécuté. Par conséquent, en tant que développeur, vous devez être conscient de cette possibilité et effacer les données saisies par l'utilisateur.

Go a un package html / template pour encoder ce que l'application retourne à l'utilisateur. Ainsi, au lieu que le navigateur fasse une entrée comme<script>alert(‘ !’);</script>il donnera un avertissement; Vous pouvez encoder l'entrée, et l'application traitera l'entrée comme un code HTML typique imprimé dans un navigateur. Un serveur HTTP qui renvoie un modèle HTML ressemblera à ceci:

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)
}


Mais il existe des bibliothèques tierces que vous pouvez utiliser lors du développement d'applications Web sur Go. Par exemple, la boîte à outils Web Gorilla , qui comprend des bibliothèques pour aider les développeurs à faire des choses comme coder les valeurs d'authentification des cookies. Il existe également nosurf , qui est un package HTTP qui aide à prévenir la falsification de requêtes intersites ( CSRF ).

3. Protégez-vous contre l'injection SQL


Si vous développez depuis un certain temps, vous connaissez peut-être les injections SQL, qui occupent toujours la première position dans le top OWASP . Cependant, vous devez prendre en compte certains points spécifiques lorsque vous utilisez Go. La première chose que vous devez faire est de vous assurer que l'utilisateur qui se connecte à la base de données dispose de droits limités. Il est également recommandé de purifier les entrées utilisateur, comme je l'ai décrit dans la section précédente, ou d'échapper des caractères spéciaux et d'utiliser la fonction HTMLEscapeString du package de modèle HTML.

Mais l'élément de code le plus important que vous devez inclure est l'utilisation de requêtes paramétrées. Dans Go, vous ne préparez pas d'expression (il s'agit d'une instruction préparée) en connection; Vous le préparez dans la base de données. Voici un exemple d'utilisation des requêtes paramétrées:

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


Cependant, que se passe-t-il si le moteur de base de données ne prend pas en charge l'utilisation d'expressions préparées? Ou si cela affecte les performances des requêtes? Eh bien, vous pouvez utiliser la fonction db.Query (), mais assurez-vous d'abord de nettoyer les entrées utilisateur, comme indiqué dans les sections précédentes. Il existe également des bibliothèques tierces, telles que sqlmap , pour empêcher l'injection SQL.

Malgré tous nos efforts, des vulnérabilités glissent parfois ou pénètrent dans nos applications par le biais de tiers. Pour protéger vos applications Web contre les attaques critiques, telles que l'injection SQL, envisagez d'utiliser une plate-forme de gestion de la sécurité des applications telle que Sqreen .

4. Crypter les informations sensibles


Le fait que la chaîne soit difficile à lire, par exemple au format base 64, ne signifie pas que la valeur cachée est secrète. Vous avez besoin d'un moyen de crypter des informations que les attaquants ne peuvent pas facilement décoder. Les informations typiques que vous souhaitez crypter sont les mots de passe de base de données, les mots de passe utilisateur ou même les numéros de sécurité sociale.

Le OWASP a plusieurs recommandations sur ce que les algorithmes de chiffrement utilisés, par exemple, bcrypt, PDKDF2, Argon2ou scrypt. Heureusement, il existe un package Go qui comprend des implémentations fiables pour le cryptage des informations - crypto . Le code suivant est un exemple d'utilisation bcrypt:

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)
	}
}


Veuillez noter que vous devez toujours faire preuve de prudence lors du transfert d'informations entre les services. Vous ne voudriez pas envoyer de données utilisateur en texte brut. Peu importe que l'application crypte les données utilisateur avant de les enregistrer. Supposons que quelqu'un sur Internet puisse écouter votre trafic et conserver des journaux des demandes de votre système. Un attaquant peut utiliser ces informations pour les associer à d'autres données d'autres systèmes.

5. Fournir une connectivité HTTPS


La plupart des navigateurs nécessitent actuellement HTTPS pour fonctionner sur tous les sites. Chrome, par exemple, vous affichera un avertissement si le site n'utilise pas HTTPS. Le service de sécurité de l'information peut utiliser le cryptage de transit pour la communication entre les services comme une politique. Autrement dit, pour assurer la sécurité de la connexion de transit dans le système, il ne s'agit pas seulement d'écouter l'application sur le port 443. Vous devez également utiliser les certificats appropriés et utiliser HTTPS pour empêcher les attaquants d'abaisser le protocole à HTTP.

Voici un extrait de code pour une application Web qui fournit et utilise le protocole HTTPS:

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"))
}


Notez que l'application écoutera sur le port 443. La ligne suivante est la ligne qui fournit la configuration HTTPS:

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


Vous pouvez également spécifier le nom du serveur dans la configuration TLS de cette manière:
config := &tls.Config{ServerName: "yourSiteOrServiceName"}


Il est toujours recommandé d'utiliser le chiffrement principal, même si votre application Web est uniquement destinée à la communication interne. Imaginez si, pour une raison quelconque, un attaquant pouvait écouter votre trafic interne. Dans la mesure du possible, il est toujours préférable d'élever la barre pour d'éventuels futurs attaquants.

6. Faites attention aux erreurs et aux journaux.


Dernier point, mais non le moindre, la gestion des erreurs et la journalisation de vos applications Go.

Pour résoudre correctement les problèmes de production, vous devez configurer correctement vos applications. Vous devez prendre en compte les erreurs que vous montrez aux utilisateurs. Vous ne voudriez pas que les utilisateurs sachent exactement ce qui s'est mal passé. Les attaquants peuvent utiliser ces informations pour déterminer les services et technologies que vous utilisez. De plus, vous devez vous rappeler que bien que les journaux soient merveilleux, ils sont stockés quelque part. Et si les journaux tombent entre de mauvaises mains, ils peuvent être utilisés pour intégrer l'attaque à venir dans le système.

Donc, la première chose que vous devez apprendre ou retenir est qu'il n'y a aucune exception à Go. Cela signifie que vous devez gérer les erreurs différemment des autres langues. La norme ressemble à ceci:

if err != nil {
    //  
}


Go propose également une bibliothèque intégrée pour travailler avec les journaux. Le code le plus simple ressemble à ceci:

package main

import (
	"log"
)

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


Mais il existe des bibliothèques tierces pour gérer les journaux. Certains d'entre eux sont logrus, gloget loggo. Voici un petit extrait de code utilisant 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")
}


Enfin, assurez-vous d'appliquer toutes les recommandations précédentes, telles que le chiffrement et la désinfection des données que vous avez placées dans les journaux.

Il y a toujours de la place pour grandir


Ces directives sont le minimum qui devrait être spécifique à vos applications Go. Cependant, si votre application est un outil de ligne de commande, vous n'aurez pas besoin de méthodes de chiffrement pour le transit. Mais le reste des conseils de sécurité s'applique à presque tous les types d'applications. Si vous voulez en savoir plus, il existe un livre de OWASP spécifiquement pour Go qui aborde certains sujets. Il existe également un référentiel contenant des liens vers des frameworks, des bibliothèques et d'autres outils de sécurité dans Go.

Peu importe ce que vous finissez par faire, n'oubliez pas que vous pouvez toujours augmenter la sécurité de votre application. Les attaquants trouveront toujours de nouvelles façons d'exploiter les vulnérabilités, essayez donc de travailler en permanence sur la sécurité de votre application.


. — , , , .NET, Node.js Java, Docker.

.

All Articles