Pourquoi même les développeurs Go tombent dans le piège des hackers ?
\n\nGo est réputé pour sa rapidité, sa simplicité et ses outils de sécurité intégrés. Mais se rassurer en se disant « j'écris en Go, donc je suis en sécurité » est une illusion dangereuse. Les injections SQL et les attaques XSS ne sont pas des reliques du passé. Elles font encore tomber des sites et fuiter des bases de données quotidiennement, et Go ne fait pas exception.
\n\nLe problème est que Go offre au développeur des outils puissants, mais ne les utilise pas à votre place. Vous pouvez accidentellement concaténer une requête SQL via fmt.Sprintf ou afficher un commentaire utilisateur sans échappement — et un attaquant obtiendra l'accès à votre base de données ou volera les cookies des visiteurs.
Dans cet article, nous allons décortiquer les deux types d'attaques les plus dangereuses — les injections SQL et le Cross-Site Scripting (XSS) — et montrer comment les prévenir avec Go natif et des bibliothèques populaires. Vous n'aurez pas de théorie, mais des modèles de code prêts à l'emploi que vous pouvez intégrer dans votre projet dès aujourd'hui.
\n\nInjections SQL en Go : comment elles surviennent et comment les éradiquer
\n\nErreur typique : la concaténation de chaînes
\n\nImaginez que vous ayez un endpoint pour récupérer un utilisateur par son ID. La façon la plus naïve (et dangereuse) d'écrire une requête ressemble à ceci :
\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 // DANGEREUX ! Concaténation de chaînes\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 // ... traitement\n}\n\nSi un attaquant transmet dans userID la chaîne 1' OR '1'='1, la requête devient :
SELECT id, name, email FROM users WHERE id = '1' OR '1'='1'\n\nEt voilà — il obtient tous les utilisateurs de la base de données. Et s'il saisit 1'; DROP TABLE users; --, vous perdez la table.
Solution : les requêtes paramétrées (prepared statements)
\n\nLe driver Go pour PostgreSQL, MySQL et SQLite prend en charge les expressions préparées. Elles séparent le code SQL des données, et le driver échappe automatiquement les caractères dangereux.
\n\nfunc getUserByIDSafe(db *sql.DB, userID string) {\n // SÉCURISÉ : requête paramétrée\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 // ... traitement\n}\n\nNotez le $1 — c'est un espace réservé pour le premier argument. Avec MySQL, on utilise le symbole ?. Désormais, même si userID contient 1' OR '1'='1, le driver l'interprète comme une valeur de chaîne, et non comme une partie du code SQL.
Travailler avec le package sqlx
\n\nLa bibliothèque github.com/jmoiron/sqlx rend le code encore plus propre. Elle prend en charge les paramètres nommés et le scan automatique dans les structures :
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 // Paramètre nommé :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\nImportant : même si vous utilisez un ORM comme GORM ou Ent, assurez-vous qu'il est configuré pour utiliser des expressions préparées. Certaines anciennes versions d'ORM permettaient des requêtes « brutes ».
\n\nXSS en Go : quand votre site se tire une balle dans le pied
\n\nComment se produit une attaque XSS
\n\nLe XSS (Cross-Site Scripting) survient lorsque vous insérez du contenu utilisateur sur une page sans échappement. Un atta