vb.net securestring

vb.net - securestring c#



¿Es posible obtener de forma segura un valor SecureString de VB.NET? (3)

Se muestra finalmente el enlace a "Marshaling SecureString Passwords to String - Mark Nicholson" en http://dotnet.org.za/markn/archive/2008/10/04/handling-passwords.aspx .

Por lo tanto, su afirmación, "C # está utilizando su característica de código no seguro para realizar manipulaciones de puntero", parece abordarse con "CÓMO: Ajustar una secuencia UCOMIS en una clase Stream en Visual Basic .NET" en http://support.microsoft.com / kb / 321695 . (Busqué en "AddrOfPinnedObject").

No leí toda su pregunta (el enlace a la publicación siempre se agotó), pero ¿ son útiles estas clases y el código de prueba? La contraseña nunca vive como un System.String; por lo tanto, necesita una implementación de SecureStringTextBox como se indica en su pregunta.

No me gusta agregar todo este código. Avíseme qué código es útil y editaré la respuesta para mantener solo las cosas útiles.

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

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

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

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

Siempre he sentido que SecureString era un poco extraño, pero supuse que la mayoría de mis problemas se debían a problemas de seguridad que no entendía. Hoy decidí sentarme y enseñarme sobre ello, pero he atacado lo que parece ser un obstáculo fatal.

El escenario que imagino es "el usuario ingresa la contraseña en el cuadro de texto, esa contraseña se codifica y se compara con un hash almacenado". Al principio me preocupaba que el cuadro de texto contuviera la cadena, pero luego me di cuenta de que podías hacer rodar un cuadro de texto personalizado que usa SecureString como su tienda. Guay. Es la parte "esa contraseña es hash y comparada ..." que me está causando problemas.

Mi primer ataque al problema en VB .NET fue ingenuo e incorrecto:

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

Esto simplemente incluye la contraseña en una cadena normal y, en primer lugar, frustra el propósito de usar SecureString. Así que seguí buscando y encontré una publicación en el blog que resuelve el problema muy bien en C #: la cadena se convierte en un BSTR, copiado en una cadena anclada, luego tanto el BSTR como la cadena fijada se ponen a cero después de su uso. Esto parece una idea mucho mejor porque minimiza la cantidad de tiempo que la cadena insegura está en la memoria. Sin embargo, no parece que haya una forma de lograr esto en VB .NET. C # está utilizando su característica de código inseguro para realizar manipulaciones de punteros, pero VB .NET no puede hacer esto. Eché un vistazo a Marhsall.Copy (), pero parece que está orientado hacia las matrices. Pensé en intentar convertir las variables de IntPtr para el objeto y BSTR en Cadenas, pero eso me dejó usando un método como String.Replace (), que creará una nueva cadena.

¿No es posible hacer esto desde VB .NET en absoluto, o hay algo que me falta?

editar Estoy aceptando la respuesta de AMissico con solo ligeras reservas. El método Marshal.ReadByte () copiará un byte de la memoria no administrada y creará un byte en la memoria no administrada. Esto produce la pequeña posibilidad de que un atacante pueda encontrar los caracteres individuales de la contraseña. Creo que eso es mucho menos que las posibilidades de encontrar una cadena completa, pero el C # en el (al parecer difunto) artículo al que hice referencia fue capaz de usar código inseguro para evitar esto cuidadosamente. El proceso de pensamiento fue que utilizó a GCHandle para fijar una cadena en la memoria, y luego usó un código inseguro para evitar la inmutabilidad de las cadenas .NET. Truco inteligente que parece imposible en VB .NET. Trataré de volver con el código C # mismo.


Todos los ejemplos que he visto no tienen en cuenta el hecho de que el tipo de inicio de sesión no es una solución única para todos.

Por ejemplo, esto solo funcionará si el usuario al que está suplantando tiene permiso para iniciar sesión en el sistema de destino. No siempre es el caso cuando se accede a un cuadro de SQL Server remoto. LOGON32_LOGON_INTERACTIVE

NetworkClearText es el único que funciona de forma consistente para su uso con conexiones de SQL Server. - Ningún texto claro no significa que está pasando credenciales de una manera insegura.

Cuando está en un grupo de trabajo y necesita suplantar a un usuario de dominio, NewCredentials es el que funciona. (no probado con conexiones de SQL Server)


Solo una corrección al código de arriba:

la línea

Dim iLength As Integer = oCopyA.Length

es icorrecto Como está escrito, la función IsEqual probará solo la mitad + 1 de los caracteres en las cuerdas.

La línea correcta debería ser:

Dim iLength As Integer = oCopyA.Length*2-1

ya que BSTR usa 2 bytes por carácter, y el índice comienza en cero, como de costumbre. Length da la cantidad de caracteres, no bytes.

De lo contrario, parece funcionar.

cignaciob

Saludos