go-mssqldb ile güvenlik en iyi uygulamaları

Bu makale, sürücü kullanılarak SQL Server'a go-mssqldb bağlanan Go uygulamaları için güvenlik uygulamalarını ele almaktadır. SQL enjeksiyon önleme, kimlik bilgileri yönetimi, şifreleme yapılandırması ve en az ayrıcalık ilkesini ele alır.

SQL enjeksiyonunu önleme

SQL enjeksiyonu, en yaygın veritabanı güvenlik açığıdır. Sürücü, go-mssqldb SQL kodunu veri değerlerinden ayıran parametreli sorgular sağlar. Kullanıcı tarafından sağlanan giriş için her zaman parametreler kullanın.

Parametreli sorguları kullanma

Parametreler SQL metninden ayrı gönderilir, bu nedenle kullanıcı girdisi asla SQL kodu olarak yorumlanamaz:

// 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))

Kullanıcı girişini SQL ile asla birleştirme

Dizi birleştirme, saldırganların keyfi SQL enjekte etmesine olanak tanır:

// 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;--" ...

Dinamik tablo veya sütun adları

Tablo isimleri, sütun adları veya diğer tanımlayıcılar için parametreli sorgular kullanamazsınız. Uygulamanız dinamik tanımlayıcılar gerektiriyorsa, bunları izin listesine göre doğrulayın:

// 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))
}

Dikkat

Kullanıcıdan alınan girdiyi, köşeli ayraçlarla kaçış uygulansa bile, asla doğrudan SQL tanımlayıcılarında kullanmayın. Her zaman izin verilen değerler listesine göre doğrulama yapın.

Depolanmış prosedürler ve SQL enjeksiyonu

Kayıtlı prosedürler otomatik olarak SQL enjeksiyonunu engellemez. Prosedür, kendi içinde birleştirilmiş dizelerle EXECUTE veya sp_executesql kullanıyorsa yine de güvenlik açığına sahip olabilir. Parametrizlenmiş çağrılar kullanın:

// Parameters are passed safely to the procedure.
_, err := db.ExecContext(ctx, "dbo.SearchEmployees",
    sql.Named("searchTerm", userInput))

Bağlantı dizesi gizli dizilerini yönetin

Bağlantı dizeleri şifreler, istemci sırları ve sertifika yolları içerebilir. Bunları asla kaynak kodunda depolamayın.

Ortam değişkenlerini kullanma

Bir ortam değişkeninden tam bağlantı dizesi'i okuyun:

connString := os.Getenv("MSSQL_CONNECTION_STRING")
if connString == "" {
    log.Fatal("MSSQL_CONNECTION_STRING environment variable is required")
}
db, err := sql.Open("sqlserver", connString)

Bireysel sırlardan bağlantı dizileri oluşturun

Her bileşen için ayrı ortam değişkenlerinden bağlantı dizesi'i derleyin:

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())

Şifreleri tamamen kaldırın

En güvenli bağlantı dizesi'in hiç şifresi yoktur. Microsoft Entra ID doğrulamasını yönetilen kimlik ile kullanın:

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")

Gizli depolama seçenekleri

Çevre Önerilen depolama alanı Notlar
Yerel kalkınma Ortam değişkenleri veya kullanıcı sırları dosyası .env dosyalarını kaynak denetimine eklemeyin.
Azure App Service / Container Apps Uygulama ayarları veya Key Vault referansları Key Vault referansları, çalışma zamanında sırları ortam değişkenleri olarak enjekte eder.
Kubernetes Kubernetes Secrets veya CSI sürücüsü ile Azure Key Vault Sırlar dosya veya ortam değişkeni olarak monte edilir.
CI/CD boru hatları Boru hattı sırları / GitHub Actions sırları Azure SQL için ActiveDirectoryServicePrincipal veya ActiveDirectoryAzurePipelines kullanın.

Important

.env, *.pfx ve *.pem öğelerini .gitignore dosyanıza ekleyin. Sırları yanlışlıkla bir depoya yerleştirmek, kimlik bilgisi sızıntılarının en yaygın kaynaklarından biridir.

Şifrelemeyi yapılandırma

Tüm uzak bağlantılar için şifreleme kullanın

Tüm uzak bağlantılar için encrypt=true ayarlayın. Sadece sunucu tarafından gerekli olan şifreleme, ortadaki düşman saldırılarına karşı savunmasızdır. İstemci şifrelemeyi zorunlu kılmalıdır. Azure SQL varsayılan olarak şifrelemeyi etkinleştirir:

sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true

En güçlü şifreleme için TDS 8.0 kullanın

TDS 8.0 (katı mod), herhangi bir TDS protokol verisi alışverişinden önce TLS el sıkışmasını gerçekleştirir ve protokol düşürme saldırılarını önler:

sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=strict

TDS 8.0 için SQL Server 2022 veya Azure SQL gereklidir.

Üretimde TrustServerCertificate kullanmayın

Ayar, TrustServerCertificate=true sertifika doğrulamasını devre dışı bırakır ve bu da ortada düşman saldırılarına izin verir:

// DEVELOPMENT ONLY. Never deploy to production.
connString := "sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&TrustServerCertificate=true"

Üretim için, uygun sertifika doğrulamasını yapılandırın. Şifreleme ve sertifikalar bölümünü incele.

Minimum TLS sürümü ayarlayın

Protokol düşürme saldırılarını önlemek için TLS 1.2 veya daha sonrasını uygulayın.

sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true&tlsmin=1.2

En az ayrıcalık ilkesini uygulama

Veritabanı kapsamındaki kullanıcıları kullanın

Birden çok veritabanına erişen sunucu düzeyindeki oturum açma hesapları yerine, tek bir veritabanıyla sınırlı içerilen veritabanı kullanıcıları oluşturun.

-- 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];

Ayrı okuma ve yazma bağlantıları

Uygulamanızın farklı okuma ve yazma yolları varsa, uygun izinlere sahip ayrı veritabanı kullanıcıları oluşturun.

// 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")

Uygulamalarda sa veya dbo kullanmaktan kaçının

sa oturum açma hesabı ve dbo kullanıcı sınırsız erişime sahiptirler. Bağlanmış bir uygulamada sa SQL enjeksiyonu açığı varsa, saldırgan veritabanı sunucusu üzerinde tam kontrol sahibi olur.

Her Zaman Şifrelenmiş Anahtarları Koruyun

Always Encrypted kullandığınızda, sütun ana anahtarı (CMK) sütun şifreleme anahtarlarını (CEK) korur. CMK'yı uygun şekilde güvence altına alın:

Anahtar sağlayıcı En iyi uygulama
Yerel sertifika (PFX) PFX dosyasını uygulama dizininin dışında sakla. Kısıtlayıcı dosya izinleri belirleyin. Şifre için bir ortam değişkeni kullanın.
Windows Sertifika Deposu Hizmet hesapları için kullanın CurrentUser\My . Sertifika izinlerini kullanarak certutilayarlayın.
Azure Key Vault Erişim için yönetilen kimlik kullanın. Anahtar kasası erişim politikalarını en az ayrıcalıklı olarak ayarlayın. Geçici silmeyi ve temizleme korumasını etkinleştirin.

Anahtar sağlayıcı yapılandırması için Always Encrypted sayfasına bakınız.

Güvenli oturum açın

Bağlantı dizileri veya kimlik bilgilerini kaydetme

Bağlantı dizeleri, günlük dosyalarında yer alabilen parolalar içerebilir:

// 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)

Tanı için bağlantı detaylarını kaydetmek zorundaysanız, önce bağlantı dizesi'i sansürleyin:

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()
}

Sansürlenmiş değeri sadece kısa süreli tanı için ihtiyacınız olduğunda kaydedin. Sunucu adı, veritabanı adı ve kimlik doğrulama modunu ayrı ayrı kaydetmeyi tercih edin.

Hassas sorgu parametrelerini günlüğe kaydetmeyin

Kişisel veya hassas veri içeren parametre değerlerini kaydetmekten kaçının:

// 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")

Üretim sırasında log bayraklarını dikkatlice kullanın

Sürücünün parametresi log SQL ifadeleri ve parametre değerleri verebilir:

Flag Risk Üretim güvenli mi?
1 (hatalar) Low Evet
2 (mesajlar) Low Evet
4 (satırlar) Medya, verileri açığa çıkarır Hayır
8 (SQL) Medium, sorguları açığa çıkarır Conditional
16 (parametreler) Yüksek değerde, parametre değerlerini açığa çıkarır Hayır
32 (işlemler) Low Evet
64 (hata ayıklama) Yüksek, protokol ayrıntılarını açığa çıkarır Hayır

Üretim için, log=1 (yalnızca hatalar) veya log=3 (hatalar + mesajlar) kullanın.

Güvenlik denetim listesi

Area Recommendation
SQL enjeksiyonu Tüm kullanıcı girdileri için parametreli sorgular kullanın. Dinamik tanımlayıcıları izin listesine göre doğrulayın.
Credentials Mümkün olduğunda Microsoft Entra yönetilen kimliğini kullanın. Kaynak kodunda asla şifre kodlamayın.
Encryption Tüm üretim bağlantıları için encrypt=true veya encrypt=strict olarak ayarlayın. tlsmin=1.2ayarlayın.
Sertifikalar Üretimde kullanmayın TrustServerCertificate=true . Sunucu sertifikalarını doğrulayın.
En az ayrıcalık Sadece gerekli izinlere sahip uygulama özel veritabanı kullanıcıları oluşturun.
Secrets Bağlantı dizelerini ortam değişkenlerinde, Key Vault'ta veya platform gizli depolarında sakla.
Logging Bağlantı dizilerini, şifreleri veya hassas parametre değerlerini kaydetmeyin.
Kaynak denetimi .gitignore öğesine .env, *.pfx ve *.pem ekleyin.