Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo tratta le pratiche di sicurezza per le applicazioni Go che si collegano a SQL Server utilizzando il go-mssqldb driver. Si occupa della prevenzione dell'iniezione SQL, della gestione delle credenziali, della configurazione della crittografia e del principio del minor privilegio.
Prevenire l'iniezione SQL
SQL injection è la vulnerabilità più comune nella sicurezza dei database. Il go-mssqldb driver fornisce query parametrizzate che separano il codice SQL dai valori dei dati. Usa sempre i parametri per l'input fornito dall'utente.
Usare query con parametri
I parametri vengono inviati separatamente dal testo SQL, quindi l'input dell'utente non può mai essere interpretato come codice 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))
Mai concatenare input dell'utente in SQL
La concatenazione delle stringhe consente agli attaccanti di iniettare SQL arbitrario:
// 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;--" ...
Nomi dinamici di tabelle o colonne
Non puoi usare query parametrizzate per nomi di tabelle, colonne o altri identificatori. Se la tua applicazione richiede identificatori dinamici, validali rispetto a una lista di permessi:
// 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))
}
Attenzione
Non inserire mai input fornito dall'utente direttamente negli identificatori SQL, anche con la fuga di bracket. Convalida sempre rispetto a un elenco di valori consentiti.
Stored procedure e SQL injection
Le stored procedure non impediscono automaticamente l'iniezione SQL. Se la procedura utilizza EXECUTE o sp_executesql con stringhe concatenate internamente, può comunque essere vulnerabile. Usa chiamate parametrizzate:
// Parameters are passed safely to the procedure.
_, err := db.ExecContext(ctx, "dbo.SearchEmployees",
sql.Named("searchTerm", userInput))
Gestisci i segreti delle stringhe di connessione
Le stringhe di connessione possono contenere password, segreti client e percorsi di certificato. Non memorizzarli mai nel codice sorgente.
Utilizzare le variabili di ambiente
Leggi l'intera stringa di connessione da una variabile di ambiente:
connString := os.Getenv("MSSQL_CONNECTION_STRING")
if connString == "" {
log.Fatal("MSSQL_CONNECTION_STRING environment variable is required")
}
db, err := sql.Open("sqlserver", connString)
Costruire stringhe di connessione a partire da singoli segreti
Assembla la stringa di connessione da variabili di ambiente separate per ogni componente:
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())
Elimina completamente le password
La stringa di connessione più sicura non ha alcuna password. Usa l'autenticazione Microsoft Entra ID con identità gestita:
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")
Opzioni di archiviazione segreta
| Ambiente | Archiviazione consigliata | Note |
|---|---|---|
| Sviluppo locale | Variabili di ambiente o file di segreti utente | Non archiviare i file .env nel controllo del codice sorgente. |
| Servizio app di Azure / Container Apps | Impostazioni dell'app o riferimenti a Key Vault | I riferimenti di Key Vault iniettano i segreti come variabili di ambiente in fase di esecuzione. |
| Kubernetes | Kubernetes Secrets o Azure Key Vault con driver CSI | I segreti sono montati come file o variabili di ambiente. |
| Pipeline CI/CD | Segreti della pipeline / segreti di GitHub Actions | Usa ActiveDirectoryServicePrincipal o ActiveDirectoryAzurePipelines per Azure SQL. |
Importante
Aggiungi .env, *.pfx, e *.pem al tuo .gitignore file. Inviare accidentalmente segreti in un repository è una delle fonti più comuni di fughe di credenziali.
Configurare la crittografia
Usa la crittografia per tutte le connessioni remote
Imposta encrypt=true per tutte le connessioni remote. La crittografia richiesta solo dal server è vulnerabile ad attacchi avversari nel mezzo. Il client deve far rispettare la crittografia. Azure SQL abilita la crittografia di default:
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true
Usa TDS 8.0 per la crittografia più forte
TDS 8.0 (modalità rigorosa) esegue il handshake TLS prima che vengano scambiati dati del protocollo TDS, prevenendo attacchi di downgrade del protocollo:
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=strict
TDS 8.0 richiede SQL Server 2022 o Azure SQL.
Non usare TrustServerCertificate in produzione
Impostare TrustServerCertificate=true disabilita la validazione del certificato, che consente attacchi avversari nel mezzo:
// DEVELOPMENT ONLY. Never deploy to production.
connString := "sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&TrustServerCertificate=true"
Per la produzione, configura una validazione corretta dei certificati. Vedi Crittografia e certificati.
Imposta una versione TLS minima
Applicare TLS 1.2 o successivo per prevenire attacchi di downgrade del protocollo.
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true&tlsmin=1.2
Usare il principio dei privilegi minimi
Usa utenti con ambito delimitato al database
Creare utenti di database indipendenti limitati a un singolo database anziché account di accesso a livello di server che consentono l'accesso a più database.
-- 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];
Connessioni separate di lettura e scrittura
Se la tua app ha percorsi distinti di lettura e scrittura, crea utenti separati del database con permessi appropriati.
// 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")
Evita di usare sa o dbo nelle applicazioni
Il sa login e l'utente dbo hanno accesso senza restrizioni. Se un'applicazione connessa a sa presenta una vulnerabilità di iniezione SQL, l'attaccante ottiene il pieno controllo sul server del database.
Proteggi le chiavi di Always Encrypted
Quando si utilizza Always Encrypted, la chiave master della colonna (CMK) protegge le chiavi di crittografia della colonna (CEK). Proteggi adeguatamente il CMK:
| Fornitore di chiave | Procedura consigliata |
|---|---|
| Certificato locale (PFX) | Memorizza il file PFX fuori dalla cartella applicativa. Imposta permessi di file restrittivi. Usa una variabile di ambiente per la password. |
| Archivio certificati Windows | Usare CurrentUser\My per account di servizio. Imposta i permessi dei certificati usando certutil. |
| Azure Key Vault (Archivio chiavi di Azure) | Usa l'identità gestita per l'accesso. Imposta le politiche di accesso al caveau con il minor privilegio. Abilitare l'eliminazione temporanea e la protezione dalla rimozione definitiva. |
Per la configurazione del provider di chiavi, vedi Sempre criptato.
Registrare in sicurezza
Non registrare stringhe di connessione o credenziali
Le stringhe di connessione possono contenere password che finiscono nei file di log:
// 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)
Se devi registrare i dettagli della connessione per la diagnostica, maschera prima la stringa di connessione:
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()
}
Registra il valore redatto solo quando è necessario per attività diagnostiche di breve durata. Preferisci registrare separatamente il nome del server, il nome del database e la modalità di autenticazione.
Non registrare i parametri sensibili delle query
Evita di registrare i valori dei parametri che contengono dati personali o sensibili:
// 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")
Usa con attenzione i flag di registro in produzione
Il parametro del log driver può fornire istruzioni SQL e valori di parametro:
| Flag | Rischio | Produzione sicura? |
|---|---|---|
1 (errori) |
Low | Sì |
2 (messaggi) |
Low | Sì |
4 (righe) |
Medium, espone i dati | No |
8 (SQL) |
Medium, espone le query | Conditional |
16 (params) |
Alto, espone i valori dei parametri | No |
32 (transazioni) |
Low | Sì |
64 (debug) |
Alto, rivela i dettagli del protocollo | No |
Per la produzione, usa log=1 (solo errori) oppure log=3 (errori + messaggi).
Elenco di controllo della sicurezza
| Area | Raccomandazione |
|---|---|
| Attacco di tipo SQL injection | Usa query parametrizzate per tutti gli input dell'utente. Validare gli identificatori dinamici rispetto a una lista di permessi. |
| Credentials | Usa Microsoft Entra managed identity quando possibile. Non inserire mai le password direttamente nel codice sorgente. |
| Encryption | Impostare encrypt=true o encrypt=strict per tutte le connessioni di produzione. Imposta tlsmin=1.2. |
| Certificates | Non usare TrustServerCertificate=true nell'ambiente di produzione. Valida i certificati del server. |
| Privilegio minimo | Crea utenti database specifici per applicazioni con solo i permessi richiesti. |
| Secrets | Archivia le stringhe di connessione nelle variabili di ambiente, in Key Vault o negli archivi dei segreti della piattaforma. |
| Logging | Non registrare stringhe di connessione, password o parametri sensibili. |
| Controllo del codice sorgente | Aggiungi .env, *.pfx, e *.pem a .gitignore. |