在 Winforms 应用程序中从 app.config 加载后如何在内存中保护连接字符串
How to secure a connection string in memory after loading it from app.config in a Winforms application
我有一个遗留应用程序使用 SQL 服务器身份验证连接字符串连接到本地或基于 Intranet 的 SQL 服务器实例。它目前使用 System.Configuration.ConfigurationManager 从 app.config 文件中获取连接字符串。但是,一旦从 app.config 文件中读取该连接字符串,它的值就会加载到内存中,并且可以使用 Process Hacker 等工具来查看应用程序内存。我目前有一个模块,其方法 returns 存储在 SecureString 对象中的连接字符串的值。连接字符串值在创建 ConnectionStringSection 对象时加载到内存中。 app.config 连接字符串 xml 通过 Microsoft 文档
中给出的说明进行加密
我了解使用集成安全性是最佳做法,在这种情况下我们的连接必须使用 SQL 服务器身份验证。有没有办法消除或最小化连接字符串在应用程序内存中的暴露?
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
memory dump after first call to get connection string
这是我用来将密码以纯文本形式保存在内存之外的工作流程。
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 密码
Dim SqlCredential = New SqlCredential(GetUserName(), GetPassword())
4) 从这里您可以使用仅包含连接字符串的 "Initial Catalog=;Data Source=;" 部分和 SQLCredential 对象的连接字符串构造 SQLConnection 对象。 "GetConnectionString()" 将 return 上述连接字符串。
Dim connection = New SqlConnection(GetConnectionString(), SqlCredential)
5) 如果您正在使用 Entity Framework 并且需要将该连接传递给您的 DBContext 对象,您可以使用此构造函数。
https://docs.microsoft.com/en-us/ef/ef6/fundamentals/connection-management
Dim myDbContext = New DBContext(New SqlConnection(ConnectionString, SqlCredential))
我有一个遗留应用程序使用 SQL 服务器身份验证连接字符串连接到本地或基于 Intranet 的 SQL 服务器实例。它目前使用 System.Configuration.ConfigurationManager 从 app.config 文件中获取连接字符串。但是,一旦从 app.config 文件中读取该连接字符串,它的值就会加载到内存中,并且可以使用 Process Hacker 等工具来查看应用程序内存。我目前有一个模块,其方法 returns 存储在 SecureString 对象中的连接字符串的值。连接字符串值在创建 ConnectionStringSection 对象时加载到内存中。 app.config 连接字符串 xml 通过 Microsoft 文档
中给出的说明进行加密我了解使用集成安全性是最佳做法,在这种情况下我们的连接必须使用 SQL 服务器身份验证。有没有办法消除或最小化连接字符串在应用程序内存中的暴露?
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
memory dump after first call to get connection string
这是我用来将密码以纯文本形式保存在内存之外的工作流程。
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 密码
Dim SqlCredential = New SqlCredential(GetUserName(), GetPassword())
4) 从这里您可以使用仅包含连接字符串的 "Initial Catalog=;Data Source=;" 部分和 SQLCredential 对象的连接字符串构造 SQLConnection 对象。 "GetConnectionString()" 将 return 上述连接字符串。
Dim connection = New SqlConnection(GetConnectionString(), SqlCredential)
5) 如果您正在使用 Entity Framework 并且需要将该连接传递给您的 DBContext 对象,您可以使用此构造函数。
https://docs.microsoft.com/en-us/ef/ef6/fundamentals/connection-management
Dim myDbContext = New DBContext(New SqlConnection(ConnectionString, SqlCredential))