2009-10-13 37 views
5

私はいつもSecureStringがちょっと変わっているように感じましたが、私の問題のほとんどは私が理解していないセキュリティ問題が原因であると考えていました。今日私は座ってそれについて自分自身を教えることに決めましたが、私は致命的な障害のように思えるものを打ちました。VB.NETからSecureString値を安全に取得できますか?

私が想定しているシナリオは、「ユーザーがテキストボックスにパスワードを入力すると、そのパスワードはハッシュされ、格納されたハッシュと比較されます。最初は、テキストボックスに文字列が含まれていることを心配しましたが、SecureStringをそのストアとして使用するカスタムテキストボックスを表示できることに気付きました。クール。それは "パスワードがハッシュされ、比較されている..."という部分が私に問題を与えている。

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 

これは普通の文字列にパスワードを詰め込むと、最初の場所でSecureStringを使用しての目的に反し:VB .NETの問題で

私の最初のハックはナイーブと間違っていました。だから私は検索を続けて、C#で問題をうまく解決するa blog postを見つけました。文字列がBSTRに作られ、固定された文字列にコピーされ、BSTRと固定文字列の両方が使用後にゼロになります。これは、安全でない文字列がメモリ内にある時間を最小限に抑えるので、はるかに良いアイデアのようです。しかし、VB .NETでこれを取り除く方法はないようです。 C#は安全でないコード機能を使用してポインタ操作を行っていますが、VB .NETではこれを行うことができません。私はMarhsall.Copy()を見ましたが、配列に向いているようです。私は、オブジェクトとBSTRのIntPtr変数をStringsにキャストしようと考えましたが、それでも新しい文字列を作成するString.Replace()のようなメソッドを使用して私を残しました。

VB .NETからこれを行うことはできませんか、それとも何か不足していますか?

編集 私はわずかなご予約でAMissicoの回答を受け入れています。 Marshal.ReadByte()メソッドは、アンマネージメモリからバイトをコピーし、アンマネージメモリにバイトを作成します。これにより、攻撃者がパスワードの個々の文字を見つけることができる可能性が小さくなります。私はそれが文字列全体を見つける確率よりもはるかに小さいと思うが、私が参照した(明らかに機能していない)記事のC#は、これをきちんと避けるために安全でないコードを使用することができた。思考プロセスはGCHandleを使用してメモリ内の文字列を固定し、次に安全でないコードを使用して.NET文字列の不変性を回避しました。賢明なトリックは、VB .NETでは不可能と思われます。私はC#コードそのものを返そうとします。

+0

それはVB.NETで書かれなければならないという理由はありますか? C#で必要なものがより良くサポートされていれば、C#で解決策を書くことになります。 –

+1

理由は、VB .NETで最も快適な人に見せたいものですからです。それでも、これは答えではありません。 VB.NETで書かれた10万行のエンタープライズアプリケーションがあるとします。これをサポートするために1 C#ファイル用のクラスライブラリをロールするのは本当に意味がありますか? これは.NET Frameworkの一部であり、比較的一般的な使用例にとって重要なクラスです。主要な.NET言語の1つがそれを使用できない場合、それは非常に大きな問題です。 – OwenP

+0

* shrugs * - あなたがリンクした記事から、問題はSecureStringが比較的新しいと思われ、フレームワークの残りの部分が更新されると、それをStringにキャストせずに使用でき、すべてが細かい(VBとC#で)。個人的には、何かをしなければならない場合は、たとえそれが別の言語で小さなクラスライブラリを使用しているとしても、それをやり遂げます。 –

答えて

3

"Secure StringString to String - Mark Nicholson "へのリンクがhttp://dotnet.org.za/markn/archive/2008/10/04/handling-passwords.aspxに表示されました。

ステートメント "C#は安全でないコード機能を使用してポインタ操作を行っています"は、http://support.microsoft.com/kb/321695にある「HOW TO:Visual Basic .NETのストリームクラスでUCOMIStreamをラップする」と対処されているようです。 (私は "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 

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 
+0

私はそうは思いませんが、多くの方法のコードを記載していません。それでもパスワードは_password文字列にコピーされています。これはGCの対象となり、必要以上に長くなります。リンクされたコードは、メモリ内の管理された文字列をピンに固定するため、移動されず、パスワードがコピーされ、実行されると文字列のメモリはゼロになります。文字列はまだGCがそれを収集するまで固執しますが、今はゼロになっています。あなたのコードはそれをしません、アンマネージド文字列だけをゼロにします。 – OwenP

+0

"Secure String String to String - Mark Nicholson"のリンクが最終的に表示されます。 – AMissico

+0

すみません、私は早くこれに戻りませんでした。それは私が忘れたものです。元のページにもう読み込むことができません。私はそれがいくつかの追加の懸念を持っていたと思うので残念です。それが立てば、あなたのコードは、VB.NETのように見えるC#の例のすべてに最も近いように見えます。私は私の質問に編集する一つの懸念がありますが、誰かがより良い解決策を考え出すまで、あなたは信用を得るべきだと思います。 – OwenP

0

私が見てきた例はすべてのアカウントにログオンタイプはワンサイズフィットではないという事実を取ることができませんすべてのソリューション。

たとえば、これは偽装しているユーザーがターゲットシステムにログオンする権限を持っている場合にのみ機能します。リモートSQL Serverボックスにアクセスする場合は必ずしもそうではありません。 LOGON32_LOGON_INTERACTIVE

NetworkClearTextは、SQL Server接続で使用するために一貫して動作する唯一のものです。 - クリアテキストは、資格情報を安全でない方法で渡しているわけではありません。

ワークグループでドメインユーザーを偽装する必要がある場合、NewCredentialsは動作するものです。上記のコードに

+2

ようこそ!複数の質問に定型文/逐語答をコピーして貼り付けるときには注意が必要です。これらはコミュニティによって「スパム」と表示される傾向があります。あなたがこれをやっているなら、それは通常質問が重複していることを意味します。 – Kev

0

だけ補正を(SQL Server接続をテストしていません):

ライン

Dim iLength As Integer = oCopyA.Length 

はicorrectです。書かれているように、関数IsEqualは、文字列の半分+ 1文字だけをテストします。

正しい行がなければならない:

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

BSTR文字につき2つのバイトを使用し、インデックスはいつものように、ゼロから始まり以来。 Lengthはバイト数ではなく文字数を示します。

それ以外の場合は、うまくいくようです。

cignaciob

よろしく

関連する問題