Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln behandlar säkerhetsrutiner för Go-applikationer som ansluter till SQL Server via drivrutinengo-mssqldb. Den behandlar SQL-injektionsprevention, legitimationshantering, krypteringskonfiguration och principen om minsta privilegium.
Förhindra SQL-inmatning
SQL-injektion är den vanligaste säkerhetssårbarheten i databaser. Drivrutinen go-mssqldb tillhandahåller parameteriserade frågor som separerar SQL-kod från datavärden. Använd alltid parametrar för användargenererad inmatning.
Använda parametriserade frågor
Parametrar skickas separat från SQL-texten, så användarinmatning kan aldrig tolkas som SQL-kod:
// 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))
Koppla aldrig ihop användarens inmatning i SQL
Strängkonkatenering tillåter angripare att injicera godtycklig SQL:
// 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;--" ...
Dynamiska tabell- eller kolumnnamn
Du kan inte använda parametriserade frågor för tabellnamn, kolumnnamn eller andra identifierare. Om din applikation kräver dynamiska identifierare, validera dem mot en tillåtslista:
// 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))
}
Försiktighet
Placera aldrig inmatning från användaren direkt i SQL-identifierare, inte ens om den omges med hakparenteser. Validera alltid mot en tillåten lista över tillåtna värden.
Lagrade procedurer och SQL-injektion
Lagrade procedurer förhindrar inte automatiskt SQL-injektion. Om proceduren använder EXECUTE eller sp_executesql med sammanfogade strängar internt kan den fortfarande vara sårbar. Använd parameteriserade samtal:
// Parameters are passed safely to the procedure.
_, err := db.ExecContext(ctx, "dbo.SearchEmployees",
sql.Named("searchTerm", userInput))
Hantera hemligheter för anslutningssträngar
Anslutningssträngar kan innehålla lösenord, klienthemligheter och certifikatvägar. Spara dem aldrig i källkoden.
Använda miljövariabler
Läs hela reťazec pripojenia från en miljövariabel:
connString := os.Getenv("MSSQL_CONNECTION_STRING")
if connString == "" {
log.Fatal("MSSQL_CONNECTION_STRING environment variable is required")
}
db, err := sql.Open("sqlserver", connString)
Skapa anslutningssträngar från enskilda hemligheter
Sätt ihop reťazec pripojenia från separata miljövariabler för varje komponent:
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())
Eliminera lösenord helt
Den säkraste anslutningssträngen har inget lösenord alls. Använd Microsoft Entra ID-autentisering med hanterad identitet:
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")
Hemliga lagringsalternativ
| Miljö | Rekommenderad lagring | Notes |
|---|---|---|
| Lokal utveckling | Miljövariabler eller användarhemlighetsfil | Kontrollera .env inte filer i versionshanteringen. |
| Azure App Service / Container Apps | Appinställningar eller referenser till Key Vault | Key Vault-referenser infogar hemligheter som miljövariabler vid körning. |
| Kubernetes | Kubernetes Secrets eller Azure Key Vault med CSI-drivrutin | Hemligheter monteras som filer eller miljövariabler. |
| CI/CD-pipelines | Pipeline-hemligheter / GitHub Actions-hemligheter | Använd ActiveDirectoryServicePrincipal eller ActiveDirectoryAzurePipelines för Azure SQL. |
Viktigt!
Lägg till .env, *.pfx, och *.pem i din .gitignore fil. Att av misstag sätta in hemligheter i ett arkiv är en av de vanligaste källorna till läckor av inloggningsuppgifter.
Konfigurera kryptering
Använd kryptering för alla fjärranslutningar
Ställ encrypt=true in för alla fjärranslutningar. Kryptering som endast krävs av servern är sårbar för attacker från angriparen i mitten. Klienten måste upprätthålla kryptering. Azure SQL möjliggör kryptering som standard:
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true
Använd TDS 8.0 för den starkaste krypteringen
TDS 8.0 (strikt läge) utför TLS-handskakning innan några TDS-protokolldata utbyts, vilket förhindrar protokollnedgraderingsattacker:
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=strict
TDS 8.0 kräver SQL Server 2022 eller Azure SQL.
Använd inte TrustServerCertificate i produktion
Inställning TrustServerCertificate=true inaktiverar certifikatvalidering, vilket möjliggör attacker mot angriparen i mitten:
// DEVELOPMENT ONLY. Never deploy to production.
connString := "sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&TrustServerCertificate=true"
För produktion, konfigurera korrekt certifikatvalidering. Se Kryptering och certifikat.
Sätt en minsta TLS-version
Upprätthåll TLS 1.2 eller senare för att förhindra protokollnedgraderingsattacker.
sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true&tlsmin=1.2
Tillämpa principen om lägsta behörighet
Använd databasbegränsade användare
Skapa användare av en begränsad databas som är begränsade till en enda databas istället för servernivåinloggningar som får tillgång till flera databaser.
-- 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];
Separata läs- och skrivanslutningar
Om din app har tydliga läs- och skrivvägar, skapa separata databasanvändare med lämpliga behörigheter.
// 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")
Undvik att använda SA eller DBO i applikationer
Inloggningen sa och användaren dbo har obegränsad åtkomst. Om en applikation som är ansluten som sa är sårbar för SQL-injektion får angriparen full kontroll över databasservern.
Skydda alltid krypterade nycklar
När du använder Always Encrypted skyddar kolumnens huvudnyckel (CMK) kolumnkrypteringsnycklarna (CEK). Skydda CMK på lämpligt sätt:
| Nyckelleverantör | Bästa praxis |
|---|---|
| Lokalt certifikat (PFX) | Lagra PFX-filen utanför applikationskatalogen. Sätt restriktiva filbehörigheter. Använd en miljövariabel för lösenordet. |
| Windows certifikatlager | Använd CurrentUser\My för tjänstekonton. Sätt certifikatbehörigheter genom att använda certutil. |
| Azure 密钥保管库 | Använd managed identity för åtkomst. Sätt nyckelvalvsåtkomstpolicyer med minsta privilegium. Aktivera mjuk borttagning eller rensningsskydd. |
För nyckelleverantörskonfiguration, se Alltid krypterat.
Logga säkert
Logga inte anslutningssträngar eller inloggningsuppgifter
Anslutningssträngar kan innehålla lösenord som hamnar i loggfiler:
// 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)
Om du måste registrera anslutningsdetaljer för diagnostik, redigera reťazec pripojenia först:
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()
}
Logga det redigerade värdet endast när du behöver det för kortlivade diagnoser. Föredra att logga servernamn, databasnamn och autentiseringsläge separat.
Logga inte känsliga frågeparameter.
Undvik att logga parametervärden som innehåller personuppgifter eller känsliga data:
// 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")
Använd loggflaggor noggrant i produktionen
Drivrutinens log-parameter kan mata ut SQL-satser och parametervärden:
| Flag | Risk | Säker för produktion? |
|---|---|---|
1 (fel) |
Låg | Ja |
2 (meddelanden) |
Låg | Ja |
4 (rader) |
Medium, visar data | No |
8 (SQL) |
Medium, exponerar frågor | Villkorlig |
16 (parametrar) |
Hög, exponerar parametervärden | No |
32 (transaktioner) |
Låg | Ja |
64 (felsökning) |
Hög, exponerar protokolldetaljer | No |
För produktion, använd log=1 (endast fel) eller log=3 (fel + meddelanden).
Checklista för säkerhet
| Area | Recommendation |
|---|---|
| SQL-injektion | Använd parameteriserade frågor för all användarinmatning. Validera dynamiska identifierare mot en tillåtslista. |
| Credentials | Använd Microsoft Entra managed identity när det är möjligt. Koda aldrig lösenord i källkoden. |
| Encryption | Ange encrypt=true eller encrypt=strict för alla produktionsanslutningar. Ange tlsmin=1.2. |
| Certificates | Använd inte TrustServerCertificate=true i produktion. Validera servercertifikat. |
| Minsta privilegium | Skapa applikationsspecifika databasanvändare med endast nödvändiga behörigheter. |
| Secrets | Lagra anslutningssträngar i miljövariabler, Key Vault eller plattformars hemlighetslager. |
| Logging | Logga inte anslutningssträngar, lösenord eller känsliga parametervärden. |
| Källkontroll | Lägg till .env, *.pfx, och *.pem till .gitignore. |