Owe*_*enP 5 vb.net securestring
我一直觉得SecureString有点奇怪,但我认为我的大多数问题是由于我不明白的安全问题.今天我决定坐下来自学一下,但我已经看到了一个似乎是致命的障碍.
我设想的场景是"用户在文本框中输入密码,密码被哈希并与存储的哈希进行比较".起初我担心文本框包含字符串,但后来我意识到你可以滚动一个使用SecureString作为其存储的自定义文本框.凉.这是"密码被哈希并进行比较......"这一部分给了我麻烦.
我对VB .NET中的问题的第一次破解是天真和错误的:
Dim passwordHandle As IntPtr
Dim insecurePassword As String = Nothing
Try
passwordHandle = Marshal.SecureStringToBSTR(_password)
insecurePassword = Marshal.PtrToStringBSTR(passwordHandle)
Catch ex As Exception
Finally
If passwordHandle <> IntPtr.Zero Then
Marshal.ZeroFreeBSTR(passwordHandle)
End If
End Try
If insecurePassword <> Nothing Then
' Do hash and comparison
End If
Run Code Online (Sandbox Code Playgroud)
这只是将密码填入常规字符串中,并且首先违背了使用SecureString的目的.所以我一直在搜索并找到一篇博文,在C#中很好地解决了这个问题:将字符串制作成BSTR,复制到固定字符串中,然后BSTR和固定字符串在使用后都归零.这似乎是一个更好的主意,因为它最大限度地减少了不安全字符串在内存中的时间.但是,它似乎没有办法在VB .NET中实现这一点.C#正在使用其不安全的代码功能来执行指针操作,但VB .NET无法执行此操作.我看了一下Marhsall.Copy(),但它看起来像是面向数组.我想过尝试将对象的IntPtr变量和BSTR转换为字符串,但这仍然让我使用像String.Replace()这样的方法,它将创建一个新的字符串.
根本不可能从VB .NET做到这一点,还是有什么我想念的?
编辑 我接受AMissico的回答只有轻微的保留.Marshal.ReadByte()方法将从非托管内存中复制一个字节,并在非托管内存中创建一个字节.这使得攻击者很难找到密码的各个字符.我认为这远远低于找到整个字符串的几率,但我引用的(显然已经不存在的)文章中的C#能够使用不安全的代码来避免这种情况.思考过程是使用GCHandle将字符串固定在内存中,然后使用不安全的代码来绕过.NET字符串的不变性.聪明的技巧在VB .NET中似乎是不可能的.我会尝试使用C#代码本身.
最终显示了http://dotnet.org.za/markn/archive/2008/10/04/handling-passwords.aspx上的“将 SecureString 密码编组为字符串 - Mark Nicholson”的链接。
因此,您的声明“C# 正在使用其不安全的代码功能来执行指针操作”似乎可以通过http://support.microsoft.com上的“如何:在 Visual Basic .NET 中的流类中包装 UCOMIStream”来解决/kb/321695。(我搜索了“AddrOfPinnedObject”。)
我没有阅读您的整个问题(帖子链接总是超时),但是这些类和测试代码有帮助吗?密码永远不会以 System.String 形式存在;因此,您需要一个 SecureStringTextBox 实现,如问题中所述。
我不喜欢添加所有这些代码。让我知道哪些代码有用,我将编辑答案以仅保留有用的内容。
Imports System.Security
Imports System.Security.Principal
Imports System.Security.Permissions
Imports System.Runtime.InteropServices
''' <summary>
''' Helper class to programmatically impersonate a user, load and unload a user's profile, and perform other maintenance-related tasks for impersonating a user.
''' </summary>
Public Class ImpersonationHelper
Implements IDisposable
#Region " IDisposable Implementaton "
Private _disposed As Boolean
Protected Overrides Sub Finalize()
Dispose(False)
MyBase.Finalize()
End Sub
''' <summary>
''' Implementation of the <b>IDisposable</b> interface.
''' </summary>
''' <remarks>This method calls <see>Undo</see> if impersonation is still being performed. This method calls the common language runtime version of the Dispose method.</remarks>
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
System.GC.SuppressFinalize(Me)
End Sub
''' <summary>
''' Implementation of the <b>IDisposable</b> interface.
''' </summary>
''' <param name="disposing">If <b>true</b>, the object to be disposed is finalized and collected by the garbage collector; otherwise, <b>false</b>.</param>
''' <remarks>This method calls Undo if impersonation is still being performed. This method calls the common language runtime version of the Dispose method.</remarks>
Protected Overloads Sub Dispose(ByVal disposing As Boolean)
If Not _disposed Then
If disposing Then
If Not IsNothing(_impersonationContext) Then
_impersonationContext.Undo()
_impersonationContext.Dispose()
End If
End If
_impersonationContext = Nothing
End If
_disposed = True
End Sub
#End Region
'2009.02.12 AMJ
' Modified From:
' How to implement impersonation in an ASP.NET application (KB306158)
' http://support.microsoft.com/kb/306158
' Implemented IDisposable based on ImpersonationHelper class of
' Namespace: Microsoft.Office.Excel.Server.Addins.ComputeCluster.Security
' Assembly: Microsoft.Office.Excel.Server.Addins.ComputeCluster (in microsoft.office.excel.server.addins.computecluster.dll)
Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Const LOGON32_LOGON_BATCH As Integer = 4
Const LOGON32_LOGON_SERVICE As Integer = 5
Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Const LOGON32_PROVIDER_WINNT35 As Integer = 1
Private Enum SECURITY_IMPERSONATION_LEVEL
SecurityAnonymous = 0
SecurityIdentification = 1
SecurityImpersonation = 2
SecurityDelegation = 3
End Enum
Private Declare Auto Function LogonUser Lib "advapi32.dll" ( _
ByVal username As String, _
ByVal domain As String, _
ByVal password As IntPtr, _
ByVal logonType As Integer, _
ByVal logonProvider As Integer, _
ByRef token As IntPtr) As Boolean
Private Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
ByVal ExistingTokenHandle As IntPtr, _
ByVal ImpersonationLevel As SECURITY_IMPERSONATION_LEVEL, _
ByRef DuplicateTokenHandle As IntPtr) As Integer
Private Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
Private Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long
Dim _impersonationContext As WindowsImpersonationContext
Dim _domain As String
Dim _login As String
Dim _password As SecureString
#Region " Standard Constructor & Properties "
''' <summary>
''' Initializes a new instance of the ImpersonationHelper class.
''' </summary>
''' <param name="domain">The domain or computer name of the user to impersonate.</param>
''' <param name="userName">The user name of the user to impersonate.</param>
''' <param name="password">The secure string password of UserName. For more information about secure strings, see the <see cref="System.Security.SecureString">SecureString</see> class.</param>
<DebuggerNonUserCode()> _
Public Sub New(ByVal domain As String, ByVal userName As String, ByVal password As SecureString)
Me.Domain = domain
Me.Login = userName
Me.Password = password
End Sub
''' <summary>
''' Do not allow a new instance of the ImpersonationHelper class without credentials.
''' </summary>
Private Sub New()
End Sub
''' <summary>
''' Gets or sets the domain of the user to impersonate.
''' </summary>
''' <value>The domain of the user.</value>
<DebuggerNonUserCode()> _
Public Property Domain() As String
Get
Return _domain
End Get
Set(ByVal value As String)
_domain = value
End Set
End Property
''' <summary>
''' Gets or sets the user name of the user to impersonate.
''' </summary>
''' <value>The user name.</value>
<DebuggerNonUserCode()> _
Public Property Login() As String
Get
Return _login
End Get
Set(ByVal value As String)
_login = value
End Set
End Property
''' <summary>
''' Sets the encrypted password of the user to impersonate.
''' </summary>
''' <value>The encrypted password.</value>
<DebuggerNonUserCode()> _
Public WriteOnly Property Password() As SecureString
Set(ByVal value As SecureString)
_password = value
End Set
End Property
#End Region
''' <summary>
''' Performs the impersonation of the user based on the parameters provided in the constructor.
''' </summary>
''' <remarks>
''' <para>If logon fails using the supplied credentials, an exception is thrown. The exception is thrown because this method is unable to duplicate the logged-on user's token for purposes of impersonation or is unable to create a Windows identity from the user's impersonated token.</para>
''' <para>For details about the direct cause of the impersonation failure, you can inspect the inner exception.</para>
''' </remarks>
<PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
Public Sub ImpersonateUser()
Dim fResult As Boolean = False 'assume impersonation failed
Dim hPassword As IntPtr = IntPtr.Zero
Dim hToken As IntPtr = IntPtr.Zero
Dim hTokenDuplicate As IntPtr = IntPtr.Zero
Dim oException As ImpersonationException = Nothing
If RevertToSelf <> 0 Then
hPassword = Marshal.SecureStringToGlobalAllocUnicode(_password)
If LogonUser(Me.Login, Me.Domain, hPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken) Then
If DuplicateToken(hToken, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, hTokenDuplicate) <> 0 Then
_impersonationContext = New WindowsIdentity(hTokenDuplicate).Impersonate()
If Not _impersonationContext Is Nothing Then
fResult = True
End If
End If
Else
oException = New ImpersonationException(Me.Login, Me.Domain)
End If
If hPassword.Equals(IntPtr.Zero) = False Then
Marshal.ZeroFreeGlobalAllocUnicode(hPassword)
End If
End If
If Not hTokenDuplicate.Equals(IntPtr.Zero) Then
CloseHandle(hTokenDuplicate)
End If
If Not hToken.Equals(IntPtr.Zero) Then
CloseHandle(hToken)
End If
If Not (oException Is Nothing) Then
Throw oException
End If
End Sub
''' <summary>
''' Undoes the impersonation of the user, if it is impersonated.
''' </summary>
''' <remarks>Use this method to free the objects associated with impersonation.</remarks>
<PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
<DebuggerNonUserCode()> _
Public Sub Undo()
_impersonationContext.Undo()
_impersonationContext = Nothing
End Sub
Public Shared Function InvokeAsUser(ByVal userName As String, ByVal domain As String, ByVal password As SecureString, ByVal methodToCall As [Delegate], ByVal ParamArray parameters() As Object) As Object
Dim oResult As Object = Nothing
Using oImpersonation As New ImpersonationHelper(domain, userName, password)
oImpersonation.ImpersonateUser()
oResult = methodToCall.DynamicInvoke(parameters)
End Using
Return oResult
End Function
End Class
Public Class ImpersonationException
Inherits System.Exception
Public ReadOnly Login As String
Public ReadOnly Domain As String
Public Sub New(ByVal userName As String, ByVal domain As String)
MyBase.New(String.Format("Impersonation failure: {1}\{0}", userName, domain), New System.ComponentModel.Win32Exception)
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
Imports Missico.Personal
Imports System.Security
Imports Microsoft.VisualStudio.TestTools.UnitTesting
<TestClass()> _
Public Class ImpersonationHelperTest
Private testContextInstance As TestContext
Public Property TestContext() As TestContext
Get
Return testContextInstance
End Get
Set(ByVal value As TestContext)
testContextInstance = value
End Set
End Property
<TestMethod()> _
Public Sub ImpersonationHelperTest()
'testing only, never initialize the characters of the password in this fashion
'replace with valid password
Dim oPassword As New System.Security.SecureString
oPassword.AppendChar("o"c)
oPassword.AppendChar("o"c)
oPassword.AppendChar("p"c)
oPassword.AppendChar("s"c)
oPassword.AppendChar("!"c)
oPassword.AppendChar(" "c)
oPassword.AppendChar("n"c)
oPassword.AppendChar("o"c)
oPassword.AppendChar(" "c)
oPassword.AppendChar("p"c)
oPassword.AppendChar("a"c)
oPassword.AppendChar("s"c)
oPassword.AppendChar("s"c)
oPassword.AppendChar("w"c)
oPassword.AppendChar("o"c)
oPassword.AppendChar("r"c)
oPassword.AppendChar("d"c)
Using oImpersonation As New ImpersonationHelper("ANTHONY", "amissico", oPassword)
oImpersonation.ImpersonateUser()
'...
End Using
Try
Using oImpersonation As New ImpersonationHelper("INVALID", "amissico", oPassword)
oImpersonation.ImpersonateUser()
'...
End Using
Catch ex As ImpersonationException
'expected
' due to invalid domain
End Try
Try
Using oImpersonation As New ImpersonationHelper("ANTHONY", "INVALID", oPassword)
oImpersonation.ImpersonateUser()
'...
End Using
Catch ex As ImpersonationException
'expected
' due to invalid user
End Try
Try
oPassword.AppendChar(" "c) 'invalidate password
Using oImpersonation As New ImpersonationHelper("ANTHONY", "amissico", oPassword)
oImpersonation.ImpersonateUser()
'...
End Using
Catch ex As ImpersonationException
'expected
' due to invalid password
End Try
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
Imports System.Security
Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices
Public Module SecureStringExtensions
''' <summary>
''' Determines whether the specified <see cref="System.Security.SecureString">System.Security.SecureString</see> instances are considered equal.
''' </summary>
''' <param name="valueA">The first <see cref="System.Security.SecureString">System.Security.SecureString</see> to compare.</param>
''' <param name="valueB">The second <see cref="System.Security.SecureString">System.Security.SecureString</see> to compare.</param>
''' <returns>True if valueA is equal to valueB; otherwise, False.</returns>
<Extension()> _
Public Function Equals(ByVal valueA As SecureString, ByVal valueB As SecureString) As Boolean
Return IsEqual(valueA, valueB)
End Function
''' <summary>
''' Determines whether the specified <see cref="System.Security.SecureString">System.Security.SecureString</see> instances are considered equal.
''' </summary>
''' <param name="valueA">The first <see cref="System.Security.SecureString">System.Security.SecureString</see> to compare.</param>
''' <param name="valueB">The second <see cref="System.Security.SecureString">System.Security.SecureString</see> to compare.</param>
''' <returns>True if valueA is equal to valueB; otherwise, False.</returns>
''' <remarks>Comparison loop based on Microsoft souce code for String.EqualsHelper method.</remarks>
<Extension()> _
Public Function IsEqual(ByVal valueA As SecureString, ByVal valueB As SecureString) As Boolean
Dim fResult As Boolean = False 'assume failure
'short-circuit if lengths are not the same
If valueA.Length <> valueB.Length Then
'cannot be the same value
Return False
End If
Using oCopyA As SecureString = valueA.Copy, oCopyB As SecureString = valueB.Copy
Dim iLength As Integer = oCopyA.Length
Dim oPtrA As IntPtr = Marshal.SecureStringToBSTR(oCopyA)
Dim oPtrB As IntPtr = Marshal.SecureStringToBSTR(oCopyB)
Try
Do While (iLength > 0)
If Marshal.ReadByte(oPtrA, iLength) <> Marshal.ReadByte(oPtrB, iLength) Then
Exit Do
End If
iLength -= 1
Loop
fResult = (iLength <= 0)
Finally
Marshal.ZeroFreeBSTR(oPtrA)
Marshal.ZeroFreeBSTR(oPtrA)
End Try
End Using
Return fResult
End Function
End Module
Run Code Online (Sandbox Code Playgroud)
Imports System.Security
Imports System.Diagnostics
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports Missico.Security.SecureStringExtensions
<TestClass()> _
Public Class SecureStringExtensionsFixture
#Region " TestContext "
Private testContextInstance As TestContext
Public Property TestContext() As TestContext
Get
Return testContextInstance
End Get
Set(ByVal value As TestContext)
testContextInstance = value
End Set
End Property
#End Region
<TestMethod()> _
Public Sub EqualsTest()
Dim oValueA As New SecureString
Dim oValueB As New SecureString
oValueA.AppendChar("p"c)
oValueA.AppendChar("a"c)
oValueA.AppendChar("s"c)
oValueA.AppendChar("s"c)
oValueA.AppendChar("w"c)
oValueA.AppendChar("o"c)
oValueA.AppendChar("r"c)
oValueA.AppendChar("d"c)
oValueB.AppendChar("p"c)
oValueB.AppendChar("a"c)
oValueB.AppendChar("s"c)
oValueB.AppendChar("s"c)
oValueB.AppendChar("w"c)
oValueB.AppendChar("o"c)
oValueB.AppendChar("r"c)
oValueB.AppendChar("d"c)
'The Object.Equal method does not work because you cannot compare to secure strings.
If oValueA.Equals(oValueB) Then
'expected, but does not work
'you cannot compare two secure strings
Else
'always fails
End If
'Using the fully-qualified path to the Equal extension method.
If Missico.Security.SecureStringExtensions.Equals(oValueA, oValueB) Then
'expected
Else
Assert.Fail("SecureString values are not equal, which is not expected.")
End If
'Using the IsEqual extension method that does not conflict with the Object.Equal method.
If oValueA.IsEqual(oValueB) Then
'expected
Else
Assert.Fail("SecureString values are not equal, which is not expected.")
End If
'change the second value
oValueB.AppendChar(" "c)
If oValueA.IsEqual(oValueB) Then
Assert.Fail("SecureString values are equal, which is not expected.")
Else
'expected
End If
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5744 次 |
| 最近记录: |