In this article
Comprendre l’injection de modèles côté serveur en Golang
L’injection de modèles côté serveur (SSTI) est une vulnérabilité de sécurité peu courante chez les développeurs. Pourtant, elle peut ouvrir la voie à des risques de sécurité croissants, comme l’inclusion de fichiers, le cross-site scripting (XSS) ou même des attaques par injection de code.
Lorsque les développeurs utilisent Golang pour créer des applications web full stack et gérer à la fois le backend et le frontend, ils risquent davantage de commettre des erreurs dans leur code, qui peuvent conduire à une SSTI.
Dans cet article, vous allez découvrir une application web simple en Golang, contenant plusieurs vulnérabilités et mauvaises pratiques de codage que j’espère vous voir repérer. Nous examinerons ensuite en détail un cas précis de SSTI pour comprendre comment les attaquants peuvent exploiter cette vulnérabilité et comment la bibliothèque intégrée à Golang html/template est utilisée dans ce contexte.
Créer une application web en Golang
Commençons par construire étape par étape les composants d’une petite application web en Golang. Nous utiliserons les éléments suivants :
Le framework web Gin pour Golang s’appuie sur la bibliothèque Go open source
github.com/gin-gonic/gin.La base de données SQLite 3 utilise la bibliothèque Go open source
database/sqlet le pilote SQLite « github.com/mattn/go-sqlite3 ».La bibliothèque Go intégrée
html/templatepermet de composer des réponses HTML à partir d’un modèle déclaré.
Déclarer les dépendances d’un projet Golang
Pour commencer, notre application web, définie dans app.go, déclare les dépendances importées dans le projet :
package main
import (
"database/sql"
"fmt"
"html/template"
"log"
"net/http"
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3"
)Si la syntaxe de Golang ne vous est pas familière, il s’agit de noms ou d’URL permettant d’importer des dépendances. Le pilote SQLite3 utilise notamment la syntaxe du trait de soulignement _ pour indiquer que le package importé n’est pas utilisé explicitement dans le code, mais qu’il est nécessaire à du code indirect qui dépend de sa présence dans le code de notre application.
Routeur web et fonctions gestionnaires en Golang
Voyons ensuite, dans les grandes lignes, comment organiser le code de cette application web simple. Voici une vue d’ensemble des fonctions utilisées :
package main
import (...)
func search(c *gin.Context) {...}
func main() {
r := gin.Default()
r.GET("/search", search)
r.Run(":8080")
}Dans l’extrait de code ci-dessus, nous déclarons la fonction d’entrée main() du programme Golang. Elle définit un nouveau routeur à l’aide du framework web Gin, puis un point de terminaison HTTP GET à l’adresse /search, qui appelle la fonction de recherche que nous avons définie. Enfin, l’application web est lancée pour traiter les requêtes sur le port 8080.
Afficher les résultats de la base de données
Nous extrayons ici la chaîne de requête de l’URL pour analyser la recherche de l’utilisateur, l’utiliser pour interroger les enregistrements de la base de données, les extraire, les mettre en forme dans un modèle HTML, puis envoyer le tout en réponse.
Remarque :
L’extrait de code suivant contient de nombreuses failles de sécurité et est fourni à des fins pédagogiques uniquement. Ne le copiez-collez pas et ne l’utilisez pas en dehors d’un exercice d’apprentissage.
Voici le code complet de notre fonction gestionnaire search :
func search(c *gin.Context) {
query := c.Query("q")
db, err := sql.Open("sqlite3", "posts.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(fmt.Sprintf("SELECT * FROM posts WHERE title LIKE '%%%s%%'", query))
if err != nil {
c.String(http.StatusInternalServerError, "Error executing query")
return
}
defer rows.Close()
tmpl, err := template.New("search").Parse(fmt.Sprintf(`
<h2>Results for query "%s":</h2>
<ul>
{{range .}}
<li>{{.}}</li>
{{end}}
</ul>
`, query))
if err != nil {
c.String(http.StatusInternalServerError, "Error creating template")
return
}
var results []struct {
ID int
Title string
Content string
}
for rows.Next() {
var id int
var title string
var content string
err := rows.Scan(&id, &title, &content)
if err != nil {
log.Fatal(err)
}
results = append(results, struct {
ID int
Title string
Content string
}{id, title, content})
}
err = tmpl.Execute(c.Writer, results)
if err != nil {
c.String(http.StatusInternalServerError, "Error executing template")
return
}
}Vulnérabilités de sécurité dans une application web Golang
Dans l’implémentation ci-dessus de la recherche dans la base de données, nous avons du code non sécurisé qui ne devrait pas figurer dans une application en production.
Quelles vulnérabilités de sécurité avez-vous repérées en particulier ?
Si vous aviez installé l’extension Snyk pour IDE (elle est gratuite !), vous repéreriez ces vulnérabilités gênantes en quelques secondes après avoir utilisé le raccourci clavier d’enregistrement du fichier :

L’injection SQL dans le gestionnaire de recherche
En examinant rapidement les lignes de code pertinentes de la fonction gestionnaire func search(), nous constatons que la variable query est lue à partir de l’entrée utilisateur provenant de la chaîne de requête de l’URL :
query := c.Query("q")Elle est ensuite transmise à la requête de base de données sans nettoyage et de manière non sécurisée, sans liaison de paramètres :
func search(c *gin.Context) {
query := c.Query("q")
db, err := sql.Open("sqlite3", "posts.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(fmt.Sprintf("SELECT * FROM posts WHERE title LIKE '%%%s%%'", query))
if err != nil {
c.String(http.StatusInternalServerError, "Error executing query")
return
}
defer rows.Close()
// ... rest of the program code ...
}Ce code vulnérable permet aux attaquants de lancer des attaques par injection SQL.
La SSTI dans le gestionnaire de recherche
Dans la suite de la fonction gestionnaire func search() présentée ci-dessus, la partie suivante du code crée un modèle HTML comme suit :
tmpl, err := template.New("search").Parse(fmt.Sprintf(`
<h2>Results for query "%s":</h2>
<ul>
{{range .}}
<li>{{.}}</li>
{{end}}
</ul>
`, query))L’utilisation de la variable query transmise à la bibliothèque html/template via le caractère de remplacement %s entraîne au moins deux risques de sécurité évidents :
Cross-site scripting (XSS) : les attaquants peuvent spécifier des éléments HTML bruts, qui seront insérés dans le modèle HTML sans être encodés de manière sécurisée.
Injection de modèles côté serveur (SSTI) : les attaquants peuvent fournir une syntaxe de modèle littérale, telle que
{{ .Title | unsafeHTML }}, prise en charge parhtml/templateet qui sera évaluée par la bibliothèque.
À propos de l’injection de code dans la bibliothèque de modèles Golang
La bibliothèque intégrée à Golang html/template intègre des mesures de sécurité qui limitent l’accès aux fonctions potentiellement dangereuses de l’environnement utilisateur, telles que os.system() et d’autres.
Cependant, le package permet de définir des fonctions personnalisées qui acceptent des entrées utilisateur et renvoient un résultat. Les développeurs peuvent ainsi définir leur propre logique et utiliser des API sensibles ainsi que des fonctions de l’environnement utilisateur issues de la bibliothèque standard Golang ou d’autres packages tiers de l’écosystème Golang.
Prenons l’exemple de la fonction de modèle personnalisée hypothétique suivante :
func dangerousFunction(input string) string {
output, err := exec.Command("bash", "-c", input).Output()
if err != nil {
return "Error executing command"
}
return string(output)
}
tmpl, err := template.New("search").Funcs(template.FuncMap{"dangerous": dangerousFunction}).Parse( /* ... */ )Remarque :
La fonction personnalisée ci-dessus est dangereuse et ne doit être utilisée qu’à des fins d’apprentissage.
Revenons à notre exemple : l’entrée utilisateur provenant de la chaîne de requête URL q est transmise à la variable query, puis au modèle. Elle peut alors être interprétée comme une syntaxe spéciale valide de html/template si la valeur du paramètre de requête est la suivante : {{ dangerous "rm -rf /tmp" }}.
Renforcer la sécurité des applications Golang
Récapitulons les enseignements et les conclusions de cette exploration des vulnérabilités de sécurité en Golang. Go fournit des outils puissants pour créer des applications web, comme le package html/template, mais la sécurité doit toujours rester une priorité.
En comprenant et en atténuant les vulnérabilités potentielles telles que l’injection SQL, la SSTI et le XSS, nous pouvons renforcer la sécurité de notre code et faire un pas vers des applications Golang plus sûres.
De plus, utiliser l’extension Snyk pour IDE dans VS Code, IntelliJ ou d’autres outils de développement contribue à sécuriser le code de vos applications en détectant les vulnérabilités et en vous aidant à les corriger au fur et à mesure que vous écrivez.
Conseils pratiques pour développer en toute sécurité avec Golang
Injection SQL : utilisez toujours des requêtes paramétrées pour éviter les vulnérabilités d’injection SQL.
SSTI : restez attentif aux risques potentiels de SSTI, en particulier lorsque vous utilisez des fonctions de modèle personnalisées ou intégrez des bibliothèques tierces. Veillez à interpoler les variables de manière sécurisée.
Nettoyage des données : nettoyez toutes les entrées utilisateur avant de les utiliser dans des modèles, des requêtes SQL ou toute autre partie de l’application. Veillez à ne pas concaténer les entrées utilisateur telles quelles ou de manière non sécurisée.
Sécurité de html/template : utilisez avec prudence et uniquement si nécessaire les fonctionnalités de sécurité de la bibliothèque html/template, comme le type template.HTML. Sinon, vous risquez d’introduire des vulnérabilités XSS.
Audits de sécurité réguliers : utilisez Snyk pour effectuer régulièrement des audits de sécurité de votre application afin d’identifier et de corriger les vulnérabilités potentielles dans votre code Golang, vos modules Go et vos dépendances tierces et open source.
Sécurisez le développement de l’IA générative avec Snyk
Définissez des garde-fous de sécurité pour tout développement assisté par l’IA.