驱动支持go-mssqldb始终加密,用于客户端加密和解密敏感数据。 启用后,驱动会自动解密加密列的数据,并加密发送到加密列的参数值。
启用始终加密
将columnencryption连接参数设置为true:
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&columnencryption=true
你还需要导入至少一个列主密钥(CMK)提供者包。 没有提供商,司机无法访问加密密钥。
列主密钥提供者
该驱动支持三个关键商店提供商。 以副作用导入的方式导入提供程序包,以注册它。
本地证书(PFX)
localcert提供者从本地文件系统上的PFX(PKCS #12)文件读取私钥。 CMK元数据中的提供者名称为 pfx。
import (
_ "github.com/microsoft/go-mssqldb"
_ "github.com/microsoft/go-mssqldb/aecmk/localcert"
)
在SQL Server中配置CMK时,将密钥路径设置为PFX文件位置:
CREATE COLUMN MASTER KEY MyCMK
WITH (
KEY_STORE_PROVIDER_NAME = 'pfx',
KEY_PATH = '/path/to/certificate.pfx'
);
如果 PFX 文件有密码保护,可以将密码设置为环境变量或通过 pfxpassword 连接参数。
Windows 证书存储
MSSQL_CERTIFICATE_STORE提供者访问Windows证书存储中的证书。 该服务商仅适用于Windows。
import (
_ "github.com/microsoft/go-mssqldb"
_ "github.com/microsoft/go-mssqldb/aecmk/localcert"
)
注释
导入 localcert 还会在 Windows 上注册 Windows 证书存储提供程序。 无需单独导入。
CMK密钥路径格式为 CurrentUser/My/<thumbprint> 或 LocalMachine/My/<thumbprint>:
CREATE COLUMN MASTER KEY MyCMK
WITH (
KEY_STORE_PROVIDER_NAME = 'MSSQL_CERTIFICATE_STORE',
KEY_PATH = 'CurrentUser/My/<CERTIFICATE_THUMBPRINT>'
);
Azure 密钥保管库
akv提供者从Azure 密钥保管库获取列主密钥。
import (
_ "github.com/microsoft/go-mssqldb"
_ "github.com/microsoft/go-mssqldb/aecmk/akv"
)
CMK 密钥路径是 Azure 密钥保管库 密钥标识符 URL:
CREATE COLUMN MASTER KEY MyCMK
WITH (
KEY_STORE_PROVIDER_NAME = 'AZURE_KEY_VAULT',
KEY_PATH = 'https://<VAULT_NAME>.vault.azure.net/keys/<KEY_NAME>/<KEY_VERSION>'
);
Azure 密钥保管库 提供程序使用 azidentity.DefaultAzureCredential 进行身份验证。 通过环境变量、管理身份、Azure CLI 或 Azure 身份库支持的其他方法配置凭证。 有关详细信息,请参阅 Microsoft Entra ID 身份验证。
查询加密列
启用始终加密并注册了服务提供者后,查询过程透明:
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/microsoft/go-mssqldb"
_ "github.com/microsoft/go-mssqldb/aecmk/localcert"
)
func main() {
db, err := sql.Open("sqlserver",
"sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&columnencryption=true")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ctx := context.Background()
// Reads automatically decrypt encrypted columns
var ssn string
err = db.QueryRowContext(ctx,
"SELECT SSN FROM Patients WHERE Id = @p1",
sql.Named("p1", 1)).Scan(&ssn)
if err != nil {
log.Fatal(err)
}
fmt.Println("SSN:", ssn)
// Parameters are automatically encrypted for encrypted columns
_, err = db.ExecContext(ctx,
"INSERT INTO Patients (Name, SSN) VALUES (@p1, @p2)",
sql.Named("p1", "Alice"),
sql.Named("p2", "123-45-6789"))
if err != nil {
log.Fatal(err)
}
}
精确匹配参数类型
始终加密的参数加密比普通参数绑定更严格。 驱动程序在发送值前会向 SQL Server 请求加密元数据,因此 Go 参数类型必须与 SQL Server 列类型高度匹配。
- 一个 Go
string参数默认会以nvarchar形式发送。 - 使用驱动程序特定的类型,如
mssql.NVarCharMax、mssql.DateTime1,或者mssql.DateTimeOffset当加密列使用更具体的 SQL Server 类型时。 - 如果参数类型与加密列类型不匹配,查询在参数加密元数据发现过程中可能会因类型不匹配而失败。
例如,如果某个加密列为 nvarchar(max),那么在需要对大型字符串进行精确匹配时,应优先使用 mssql.NVarCharMax:
_, err := db.ExecContext(ctx,
"INSERT INTO Patients (Notes) VALUES (@p1)",
sql.Named("p1", mssql.NVarCharMax("Sensitive note text")))
关于一般参数类型的指导,请参见 数据类型映射。
Limitations
- 始终加密无法用于批量复制操作。
-
azuresql驱动程序和 Always Encrypted 可以结合使用,但你必须同时导入azuread包和密钥提供程序包。 - 对
char和varchar的加密插入和更新目前受限。 对于必须使用“始终加密”的文本数据,优先使用加密的nchar列或nvarchar列。 - 不支持安全飞地。