Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Dit artikel behandelt beveiligingspraktijken voor Go-applicaties die verbinding maken met SQL Server via de go-mssqldb driver. Het behandelt SQL-injectiepreventie, credentialbeheer, encryptieconfiguratie en het principe van het minste privilege.
SQL-injectie voorkomen
SQL-injectie is de meest voorkomende beveiligingskwetsbaarheid in databases. De go-mssqldb driver biedt geparametriseerde queries die SQL-code scheiden van datawaarden. Gebruik altijd parameters voor door de gebruiker ingevoerde invoer.
Geparameteriseerde query's gebruiken
Parameters worden apart van de SQL-tekst verzonden, zodat gebruikersinvoer nooit als SQL-code kan worden geïnterpreteerd:
// 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))
Verbind gebruikersinvoer nooit met SQL
Stringconcatenatie stelt aanvallers in staat om willekeurige SQL te injecteren:
// 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;--" ...
Dynamische tabel- of kolomnamen
Je kunt geen geparametriseerde queries gebruiken voor tabelnamen, kolomnamen of andere identificaties. Als je applicatie dynamische identificaties vereist, valideer deze dan met een toestemmingslijst:
// 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))
}
Let op
Plaats nooit door de gebruiker ingevoerde invoer direct in SQL-identificaties, zelfs niet met haakjesontsnapping. Valideer altijd tegen een toestaanlijst van toegestane waarden.
Opgeslagen procedures en SQL-injectie
Stored procedures voorkomen SQL-injectie niet automatisch. Als de procedure intern gebruikmaakt van EXECUTE of van sp_executesql met samengevoegde tekenreeksen, kan deze nog steeds kwetsbaar zijn. Gebruik geparametriseerde aanroepen:
// Parameters are passed safely to the procedure.
_, err := db.ExecContext(ctx, "dbo.SearchEmployees",
sql.Named("searchTerm", userInput))
Geheimen van de verbindingsreeks beheren
Verbindingsstrings kunnen wachtwoorden, clientgeheimen en certificaatpaden bevatten. Sla ze nooit op in de broncode.
Omgevingsvariabelen gebruiken
Lees de volledige verbindingsreeks van een omgevingsvariabele:
connString := os.Getenv("MSSQL_CONNECTION_STRING")
if connString == "" {
log.Fatal("MSSQL_CONNECTION_STRING environment variable is required")
}
db, err := sql.Open("sqlserver", connString)
Stel verbindingsreeksen samen uit afzonderlijke geheimen
Stel de verbindingsreeks samen uit aparte omgevingsvariabelen voor elke component:
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())
Verwijder wachtwoorden volledig
De meest veilige verbindingsreeks heeft helemaal geen wachtwoord. Gebruik Microsoft Entra ID-authenticatie met beheerde identiteit:
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")
Geheime opslagopties
| Milieu | Aanbevolen opslag | Aantekeningen |
|---|---|---|
| Lokale ontwikkeling | Omgevingsvariabelen of gebruikersgeheimenbestand | Check .env-bestanden niet in bij broncodebeheer. |
| Azure App Service / Container Apps | App-instellingen of Key Vault-referenties | Key Vault-referenties injecteren geheimen als omgevingsvariabelen tijdens runtime. |
| Kubernetes | Kubernetes Secrets eller Azure Key Vault with CSI driver | Geheimen worden gemonteerd als bestanden of omgevingsvariabelen. |
| CI/CD-pijplijnen | Pijplijngeheimen / GitHub Actions-geheimen | Gebruik ActiveDirectoryServicePrincipal of ActiveDirectoryAzurePipelines voor Azure SQL. |
Important
Voeg .env, *.pfx en *.pem toe aan je .gitignore-bestand. Het per ongeluk vastleggen van geheimen in een repository is een van de meest voorkomende bronnen van lekken van inloggegevens.
Versleuteling configureren
Gebruik encryptie voor alle externe verbindingen
Stel encrypt=true in voor alle externe connecties. Versleuteling die alleen door de server nodig is, is kwetsbaar voor aanvallen van het tegenstander in het midden. De client moet encryptie afdwingen. Azure SQL maakt standaard encryptie mogelijk:
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true
Gebruik TDS 8.0 voor de sterkste encryptie
TDS 8.0 (strict mode) voert de TLS-handshake uit voordat TDS-protocolgegevens worden uitgewisseld, waardoor protocoldowngrade-aanvallen worden voorkomen:
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=strict
TDS 8.0 vereist SQL Server 2022 of Azure SQL.
Gebruik TrustServerCertificate niet in productie
Door TrustServerCertificate=true in te stellen, wordt de certificaatvalidatie uitgeschakeld, waardoor adversary-in-the-middle-aanvallen mogelijk worden:
// DEVELOPMENT ONLY. Never deploy to production.
connString := "sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&TrustServerCertificate=true"
Voor productie configureer je de juiste certificaatvalidatie. Zie Versleuteling en certificaten.
Stel een minimale TLS-versie in
Handhaaf TLS 1.2 of later om protocoldowngrade-aanvallen te voorkomen.
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true&tlsmin=1.2
Het principe van minimale bevoegdheden toepassen
Gebruik gebruikers met databaserestrictie
Creëer afgesloten databasegebruikers die beperkt zijn tot één enkele database in plaats van serverniveau-logins die toegang krijgen tot meerdere databases.
-- 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];
Afzonderlijke lees- en schrijfverbindingen
Als je app aparte lees- en schrijfpaden heeft, maak dan aparte databasegebruikers met de juiste rechten.
// 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")
Vermijd het gebruik van SA of DBO in applicaties
De sa login en dbo gebruiker hebben onbeperkte toegang. Als een applicatie die als sa is verbonden een SQL-injectiekwetsbaarheid heeft, krijgt de aanvaller volledige controle over de databaseserver.
Beveilig Always Encrypted-sleutels
Wanneer je Always Encrypted gebruikt, beschermt de kolomhoofdsleutel (CMK) de kolomencryptiesleutels (CEK). Beveilig de CMK op de juiste manier:
| Sleutelleverancier | Aanbevolen procedure |
|---|---|
| Lokaal certificaat (PFX) | Sla het PFX-bestand buiten de applicatiemap op. Stel beperkende bestandsrechten in. Gebruik een omgevingsvariabele voor het wachtwoord. |
| Windows-certificaatopslag | Gebruik CurrentUser\My voor serviceaccounts. Stel certificaatmachtigingen in door te gebruiken certutil. |
| Azure Key Vault | Gebruik managed identity voor toegang. Stel toegangsbeleid voor sleutelkluis in met het minste privilege. Schakel zacht verwijderen en beveiliging tegen opschonen in. |
Voor sleutelproviderconfiguratie, zie Altijd Versleuteld.
Log veilig
Log geen verbindingsstrings of inloggegevens
Verbindingsstrings kunnen wachtwoorden bevatten die in logbestanden terechtkomen:
// 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)
Als je verbindingsdetails moet opnemen voor diagnostiek, redacteer dan eerst de verbindingsreeks:
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()
}
Log de gecensureerde waarde alleen als je hem nodig hebt voor kortstondige diagnostiek. Geef de voorkeur aan het afzonderlijk loggen van de servernaam, databasenaam en authenticatiemodus.
Log geen gevoelige queryparameters
Vermijd het loggen van parameterwaarden die persoonlijke of gevoelige gegevens bevatten:
// 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")
Gebruik logvlaggen zorgvuldig in productie
De parameter van log de driver kan SQL-statements en parameterwaarden uitvoeren:
| Flag | Risico | Productieveilig? |
|---|---|---|
1 (fouten) |
Low | Ja |
2 (berichten) |
Low | Ja |
4 (rijen) |
Medium, stelt gegevens bloot | No |
8 (SQL) |
Medium, stelt queries bloot | Conditional |
16 (params) |
Hoog, stelt parameterwaarden bloot | No |
32 (transacties) |
Low | Ja |
64 (debug) |
Hoog, maakt details van het protocol zichtbaar | No |
Voor productie gebruik log=1 (alleen fouten) of log=3 (fouten + berichten).
Controlelijst voor beveiliging
| Area | Recommendation |
|---|---|
| SQL-injectie | Gebruik geparametriseerde queries voor alle gebruikersinvoer. Valideer dynamische identificaties aan de hand van een toestemmingslijst. |
| Credentials | Gebruik Microsoft Entra managed identity waar mogelijk. Neem wachtwoorden nooit hardcoded op in de broncode. |
| Encryption | Stel voor alle productieverbindingen encrypt=true of encrypt=strict in. Stel tlsmin=1.2in. |
| Certificates | Gebruik niet TrustServerCertificate=true in productie. Valideer servercertificaten. |
| Minimale bevoegdheid | Maak applicatie-specifieke databasegebruikers aan met alleen de vereiste rechten. |
| Secrets | Sla verbindingsstrings op in omgevingsvariabelen, Key Vault of platformgeheime opslagen. |
| Logging | Log geen verbindingsstrings, wachtwoorden of gevoelige parameterwaarden. |
| Bronbeheer | Voeg .env, *.pfx en *.pem toe aan .gitignore. |