首页 > 解决方案 > 如何在 Winforms 应用程序中从 app.config 加载连接字符串后保护内存中的连接字符串

问题描述

我有一个旧应用程序,它使用 SQL Server 身份验证连接字符串连接到本地或基于 Intranet 的 SQL Server 实例。它目前使用 System.Configuration.ConfigurationManager 从 app.config 文件中获取连接字符串。但是,一旦从 app.config 文件中读取该连接字符串,它的值就会被加载到内存中,并且可以使用 Process Hacker 等工具公开以查看应用程序内存。我目前有一个带有方法的模块,该方法返回存储在 SecureString 对象中的连接字符串的值。连接字符串值在创建 ConnectionStringSection 对象时加载到内存中。app.config 连接字符串 xml 通过 microsoft 文档中给出的说明进行加密

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings-and-configuration-files

我了解使用集成安全性是最佳实践,在这种情况下,我们的连接必须使用 SQL Server 身份验证。有没有办法消除或最小化应用程序内存中连接字符串的暴露?

Public Function GetConnectionString() As SecureString
    Dim fileMap As ExeConfigurationFileMap = New ExeConfigurationFileMap
    fileMap.ExeConfigFilename = Environment.CurrentDirectory + "\app.config"
    Dim config As Configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None)
    Dim section As ConnectionStringsSection = TryCast(config.GetSection("connectionStrings"), ConnectionStringsSection)

    Dim secureString As New SecureString

    For Each character As Char In section.ConnectionStrings("ConString").ConnectionString.ToCharArray
        secureString.AppendChar(character)
    Next

    Return secureString
End Function

第一次调用以获取连接字符串后的内存转储

标签: .netvb.netconnection-stringpassword-protectionconfigurationmanager

解决方案


这是我用来以纯文本形式保存密码的工作流程。

1)使用对称加密对连接字符串本身的密码进行加密,因此当它通过调用加载到内存中时

Dim section As ConnectionStringsSection = TryCast(config.GetSection("connectionStrings"), ConnectionStringsSection)

公开的值是加密值而不是纯文本。

https://docs.microsoft.com/en-us/dotnet/standard/security/encrypting-data

2) 解密密码并将其一次添加一个字符到 SecureString 对象进行存储。

https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=netframework-4.8

Public Shared Function DecryptString(ByVal srcString As String) As SecureString

        Dim p As Byte() = Convert.FromBase64String(srcString)
        Dim rv As RijndaelManaged = New RijndaelManaged
        Dim ms As MemoryStream = New MemoryStream(p)
        Dim cs As CryptoStream = New CryptoStream(ms, rv.CreateDecryptor(keyb, ivb), CryptoStreamMode.Read)
        Dim secureString As New SecureString
        Try
            Do
                Dim character As Integer = cs.ReadByte()
                If character = -1 Then
                    Exit Do
                End If
                secureString.AppendChar(Chr(character))
            Loop
        Finally
            ms.Close()
            ms.Dispose()
            cs.Close()
            cs.Dispose()
        End Try
        secureString.MakeReadOnly()
        Return secureString
    End Function

3) 使用 SecureString 对象和用户名构造一个 SqlCredential 对象,其中“GetUserName()”从连接字符串中获取用户名,“GetPassword()”获取 SecureString 密码

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcredential.-ctor?view=netframework-4.8

Dim SqlCredential = New SqlCredential(GetUserName(), GetPassword())

4) 从这里您可以使用仅包含“Initial Catalog=;Data Source=;”的连接字符串构造一个 SQLConnection 对象 连接字符串和 SQLCredential 对象的一部分。“GetConnectionString()”将返回上述连接字符串。

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.-ctor?view=netframework-4.8#System_Data_SqlClient_SqlConnection__ctor_System_String_System_Data_SqlClient_SqlCredential_

Dim connection = New SqlConnection(GetConnectionString(), SqlCredential)

5) 如果您使用的是实体框架并且需要将该连接传递给您的 DBContext 对象,您可以使用此构造函数。

https://docs.microsoft.com/en-us/ef/ef6/fundamentals/connection-management

Dim myDbContext = New DBContext(New SqlConnection(ConnectionString, SqlCredential))

推荐阅读