Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article traite des pratiques de sécurité pour les applications Go qui se connectent à SQL Server en utilisant le go-mssqldb pilote. Il traite de la prévention de l’injection SQL, de la gestion des identifiants, de la configuration du chiffrement et du principe du moindre privilège.
Empêcher l’injection SQL
L’injection SQL est la vulnérabilité la plus courante en matière de sécurité des bases de données. Le go-mssqldb pilote fournit des requêtes paramétrées qui séparent le code SQL des valeurs des données. Utilisez toujours des paramètres pour les entrées fournies par l’utilisateur.
Utiliser des requêtes paramétrables
Les paramètres sont envoyés séparément du texte SQL, de sorte que les entrées de l’utilisateur ne peuvent jamais être interprétées comme du code SQL :
// CORRECT: Parameters are sent separately from the SQL text.
rows, err := db.QueryContext(ctx,
"SELECT * FROM Sales.vSalesPerson WHERE FirstName = @name AND CountryRegionName = @loc",
sql.Named("name", userName),
sql.Named("loc", userLocation))
Ne jamais concaténer les entrées utilisateur en SQL
La concaténation de chaînes permet aux attaquants d’injecter des SQL arbitraires :
// WRONG: SQL injection vulnerability.
query := fmt.Sprintf("SELECT * FROM Sales.vSalesPerson WHERE FirstName = '%s'", userName)
rows, err := db.QueryContext(ctx, query) // If userName is "'; DROP TABLE HumanResources.Department;--" ...
Noms dynamiques de tables ou colonnes
Vous ne pouvez pas utiliser de requêtes paramétrées pour les noms de tables, les noms de colonnes ou d’autres identifiants. Si votre application nécessite des identifiants dynamiques, validez-les par rapport à une liste de permis :
// Validate against known-safe values.
var validColumns = map[string]bool{
"FirstName": true, "CountryRegionName": true, "TerritoryName": true,
}
func queryByColumn(ctx context.Context, db *sql.DB, column, value string) (*sql.Rows, error) {
if !validColumns[column] {
return nil, fmt.Errorf("invalid column name: %q", column)
}
// Safe to use column directly because it was validated against the allowlist.
query := fmt.Sprintf("SELECT * FROM Sales.vSalesPerson WHERE [%s] = @p1", column)
return db.QueryContext(ctx, query, sql.Named("p1", value))
}
Attention
Ne placez jamais une entrée fournie directement par l’utilisateur dans des identifiants SQL, même avec la sortie des crochets. Validez toujours par rapport à une liste de valeurs autorisées.
Procédures stockées et injection SQL
Les procédures stockées n’empêchent pas automatiquement l’injection SQL. Si la procédure utilise EXECUTE ou sp_executesql avec des chaînes concaténées en interne, elle peut tout de même être vulnérable. Utilisez des appels paramétrés :
// Parameters are passed safely to the procedure.
_, err := db.ExecContext(ctx, "dbo.SearchEmployees",
sql.Named("searchTerm", userInput))
Gérer les secrets de chaîne de connexion
Les chaînes de connexion peuvent contenir des mots de passe, des secrets clients et des chemins de certificat. Ne les stockez jamais dans le code source.
Utiliser des variables d’environnement
Lisez la chaîne de connexion complète à partir d’une variable d’environnement :
connString := os.Getenv("MSSQL_CONNECTION_STRING")
if connString == "" {
log.Fatal("MSSQL_CONNECTION_STRING environment variable is required")
}
db, err := sql.Open("sqlserver", connString)
Construire des chaînes de connexion à partir de secrets individuels
Assemblez la chaîne de connexion à partir de variables d’environnement distinctes pour chaque composant :
import (
"net/url"
"os"
)
query := url.Values{}
query.Add("database", os.Getenv("DB_NAME"))
query.Add("encrypt", "true")
password := os.Getenv("DB_PASSWORD")
u := &url.URL{
Scheme: "sqlserver",
User: url.UserPassword(os.Getenv("DB_USER"), password),
Host: os.Getenv("DB_HOST"),
RawQuery: query.Encode(),
}
db, err := sql.Open("sqlserver", u.String())
Éliminer complètement les mots de passe
La chaîne de connexion la plus sécurisée n’a pas de mot de passe du tout. Utilisez l’authentification Microsoft Entra ID avec identité managée :
import _ "github.com/microsoft/go-mssqldb/azuread"
// No password, no secret. The managed identity handles authentication.
db, err := sql.Open("azuresql",
"sqlserver://<server>.database.windows.net?database=AdventureWorks2025&fedauth=ActiveDirectoryDefault&encrypt=true&TrustServerCertificate=false")
Options de stockage secrète
| Environnement | Stockage recommandé | Remarques |
|---|---|---|
| Développement local | Variables d’environnement ou fichier de secrets utilisateur | N’archivez pas les fichiers .env dans le contrôle de code source. |
| Azure App Service / Container Apps | Paramètres de l’application ou références à Key Vault | Les références Key Vault injectent des secrets sous forme de variables d’environnement à l’exécution. |
| Kubernetes | Kubernetes Secrets ou Azure Key Vault avec le pilote CSI | Les secrets sont montés sous forme de fichiers ou de variables d’environnement. |
| Pipelines CI/CD | Secrets du pipeline / secrets GitHub Actions | Utilisez ActiveDirectoryServicePrincipal ou ActiveDirectoryAzurePipelines pour Azure SQL. |
Important
Ajoutez .env, *.pfx, et *.pem à votre .gitignore fichier. Valider accidentellement des informations sensibles dans un dépôt est l’une des sources les plus courantes de fuites d’identifiants.
Configurer le chiffrement
Utilisez le chiffrement pour toutes les connexions distantes
Définir encrypt=true pour toutes les connexions distantes. Un chiffrement exigé uniquement par le serveur est vulnérable aux attaques de l’homme du milieu. Le client doit faire respecter le chiffrement. Azure SQL permet le chiffrement par défaut :
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true
Utilisez TDS 8.0 pour le chiffrement le plus fort
TDS 8.0 (mode strict) effectue la poignée de main TLS avant l’échange de données du protocole TDS, empêchant ainsi les attaques de rétrogradation du protocole :
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=strict
TDS 8.0 nécessite SQL Server 2022 ou Azure SQL.
N’utilisez pas TrustServerCertificate en production
Le paramètre TrustServerCertificate=true désactive la validation des certificats, ce qui permet des attaques adversaires au milieu :
// DEVELOPMENT ONLY. Never deploy to production.
connString := "sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&TrustServerCertificate=true"
Pour la production, configurez une validation correcte des certificats. Voir Chiffrement et certificats.
Définissez une version TLS minimale
Appliquer TLS 1.2 ou ultérieur pour prévenir les attaques de rétrogradation de protocole.
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true&tlsmin=1.2
Appliquer le principe du privilège minimum
Utilisez des utilisateurs contenus dans la base de données
Créer des utilisateurs de bases de données contenus limités à une seule base de données au lieu de connexions au niveau serveur qui accèdent à plusieurs bases de données.
-- Create a contained database user (no server-level login needed).
CREATE USER [app_user] WITH PASSWORD = '<password>';
-- Grant only the permissions the application needs.
ALTER ROLE db_datareader ADD MEMBER [app_user];
ALTER ROLE db_datawriter ADD MEMBER [app_user];
GRANT EXECUTE ON SCHEMA::dbo TO [app_user];
Séparez les connexions de lecture et d’écriture
Si votre application a des chemins de lecture et d’écriture distincts, créez des utilisateurs de base de données séparés avec les permissions appropriées.
// Read-only queries use a restricted user.
readDB, _ := sql.Open("sqlserver",
"sqlserver://app_reader:password@<server>?database=AdventureWorks2025&ApplicationIntent=ReadOnly&encrypt=true")
// Write operations use a user with write permissions.
writeDB, _ := sql.Open("sqlserver",
"sqlserver://app_writer:password@<server>?database=AdventureWorks2025&encrypt=true")
Évitez d’utiliser sa ou dbo dans les applications
L’sa identifiant de connexion et l’dbo utilisateur bénéficient d’un accès sans restriction. Si une application connectée à sa possède une vulnérabilité d’injection SQL, l’attaquant prend le contrôle total du serveur de base de données.
Protéger les clés Always Encrypted
Lorsque vous utilisez Always Encrypted, la clé maîtresse de colonne (CMK) protège les clés de chiffrement de colonne (CEK). Sécuriser le CMK de manière appropriée :
| Fournisseur clé | Meilleures pratiques |
|---|---|
| Certificat local (PFX) | Stockez le fichier PFX en dehors du répertoire de l’application. Définissez des permissions de fichiers restrictives. Utilisez une variable d’environnement pour le mot de passe. |
| Magasin de certificats Windows | Utilisez CurrentUser\My pour des comptes de service. Définir les permissions des certificats en utilisant certutil. |
| Azure Key Vault | Utilisez une identité gérée pour l’accès. Définissez les politiques d’accès au coffre-fort à clé avec le moins de privilèges. Activez la suppression temporaire ainsi que la protection contre la purge. |
Pour la configuration du fournisseur de clés, voir Toujours chiffré.
Connectez-vous en toute sécurité
Ne pas enregistrer les chaînes de connexion ni les identifiants
Les chaînes de connexion peuvent contenir des mots de passe qui se retrouvent dans des fichiers journaux :
// WRONG: Logs the password.
log.Printf("Connecting to %s", connString)
// CORRECT: Log only the server and database.
log.Printf("Connecting to server=%s database=%s", serverName, dbName)
Si vous devez enregistrer les détails de connexion pour les diagnostics, masquez d’abord la chaîne de connexion :
func redactSQLServerURL(raw string) string {
u, err := url.Parse(raw)
if err != nil {
return "<invalid connection string>"
}
if u.User != nil {
username := u.User.Username()
if username != "" {
u.User = url.UserPassword(username, "REDACTED")
}
}
q := u.Query()
for _, key := range []string{"password", "clientassertion", "systemtoken"} {
if q.Has(key) {
q.Set(key, "REDACTED")
}
}
u.RawQuery = q.Encode()
return u.String()
}
Enregistrez la valeur expurgée uniquement lorsque vous en avez besoin pour des diagnostics de courte durée. Je préfère enregistrer séparément le nom du serveur, le nom de la base de données et le mode d’authentification.
Ne consignez pas les paramètres sensibles de requête
Évitez d’enregistrer les valeurs des paramètres contenant des données personnelles ou sensibles :
// WRONG: Logs sensitive parameter values.
log.Printf("Query: SELECT * FROM HumanResources.Employee WHERE NationalIDNumber = %s", nationalIDNumber)
// CORRECT: Log the query structure without parameter values.
log.Printf("Querying HumanResources.Employee by NationalIDNumber")
Utilisez les indicateurs de journalisation avec précaution en production
Le paramètre du log pilote peut produire des instructions SQL et des valeurs de paramètre :
| Flag | Risque | Production sécurisée ? |
|---|---|---|
1 (erreurs) |
Low | Oui |
2 (messages) |
Low | Oui |
4 (lignes) |
Medium, expose les données | Non |
8 (SQL) |
Medium, expose les requêtes | Conditional |
16 (params) |
Élevé, expose les valeurs des paramètres | Non |
32 (transactions) |
Low | Oui |
64 (débogage) |
Élevé, expose les détails du protocole | Non |
Pour la production, utiliser log=1 (erreurs uniquement) ou log=3 (erreurs + messages).
Liste de contrôle de sécurité
| Area | Recommendation |
|---|---|
| Injection de code SQL | Utilisez des requêtes paramétrées pour toutes les entrées utilisateurs. Validez les identifiants dynamiques par rapport à une liste de permis. |
| Credentials | Utilisez l’identité gérée Microsoft Entra lorsque c’est possible. Ne codez jamais les mots de passe en dur dans le code source. |
| Encryption | Fixer encrypt=true ou encrypt=strict pour toutes les connexions de production. Définissez tlsmin=1.2. |
| Certificates | N’utilisez TrustServerCertificate=true pas en production. Validez les certificats serveur. |
| Privilège minimum | Créez des utilisateurs de bases de données spécifiques à une application avec uniquement les autorisations requises. |
| Secrets | Stockez les chaînes de connexion dans des variables d’environnement, Key Vault ou des magasins secrets de plateforme. |
| Logging | Ne journalisez pas les chaînes de connexion, les mots de passe ou les valeurs de paramètres sensibles. |
| Gestion des sources | Ajouter .env, *.pfx, et *.pem à .gitignore. |