2013-04-04 64 views
2

這讓我瘋狂。我的.NET應用程序調用存儲過程從數據庫retreive存儲的哈希密碼:SQL Server存儲過程無法正確設置正確的上下文信息

ALTER PROCEDURE [dbo].[sGetHashedPW] 
    -- Add the parameters for the stored procedure here 
    @UserName varchar(32) = N'' 
AS 
DECLARE @ContextInfo varbinary(128) 
BEGIN 
    SET NOCOUNT ON; 

    --Set Context Info for this session to Username requested 
    SELECT @ContextInfo = CAST(@Username AS varbinary(128)); 
    SET CONTEXT_INFO @ContextInfo; 

    --Return Hashed User Password to Application 
    SELECT TOP 1 [Password] as PWHash FROM [dbo].[vSecurity] WHERE [UserName] = @UserName; 
END 

應用驗證密碼,並運行它在session表中創建一個行中的第二個存儲過程,返回一個字符串(GUID )添加到應用程序,並將CONTEXT_INFO設置爲該字符串的值。

現在,只要應用程序打開一個連接,它會調用sSessionStart並傳遞登錄期間收到的字符串。如果連接在一個新的會話打開時,PROC應該設置CONTEXT_INFO它傳遞的驗證字符串值:

ALTER PROCEDURE [dbo].[sStartSession] 
    @token varchar(128)=NULL 
AS 
DECLARE @GUID uniqueidentifier 
BEGIN 
    SET NOCOUNT ON; 
    IF @token IS NULL --Token not passed from the application 
     BEGIN 
     BEGIN TRANSACTION; 
     BEGIN TRY 
      SELECT @GUID = personnel_token 
       FROM t300_Personnel INNER JOIN vSecurity ON t300_Personnel.personnel_user_name = vSecurity.UserName 
       WHERE personnel_user_name = CAST(CONTEXT_INFO() as varchar(128)); 
      SET @GUID = ISNULL(@GUID,NewID()); 
      INSERT INTO dbo.t900_UserSession 
      (session_token,session_dbuser,session_id) 
      OUTPUT CAST(@GUID as varchar(128)) 
      SELECT @GUID, [DB_User], @@SPID 
       FROM t300_Personnel INNER JOIN 
        vSecurity ON t300_Personnel.personnel_user_name = vSecurity.UserName 
       WHERE [UserName] = CAST(CONTEXT_INFO() as varchar(128)); 

      UPDATE dbo.t300_Personnel SET [personnel_token]= @GUID 
       WHERE [personnel_user_name] = CAST(CONTEXT_INFO() as varchar(128)); 

      --Change Context Info to the GUID 
      SET CONTEXT_INFO @GUID; 
     END TRY 

     BEGIN CATCH 
      IF @@TRANCOUNT > 0 
       ROLLBACK TRANSACTION; 
      THROW 51000, N'Session could not be opened',1; 
     END CATCH 

     IF @@TRANCOUNT > 0 
      COMMIT TRANSACTION; 
     END 

    ELSE --Token was passed from the application 
     BEGIN 
     BEGIN TRY 
      SELECT @GUID = CAST(@token as uniqueidentifier); 
      INSERT INTO dbo.t900_UserSession 
      (session_token,session_dbuser,session_id) 
      SELECT @GUID, DB_User, @@SPID 
       FROM t300_Personnel INNER JOIN 
        vSecurity ON t300_Personnel.personnel_user_name = vSecurity.UserName 
       WHERE personnel_token = @GUID; 

      IF (@@ROWCOUNT = 0) --Insert failed due to invalid token 
       SELECT CAST('INVALID TOKEN' as varchar(128)); 
      ELSE --New session was created 
       BEGIN 
        SET CONTEXT_INFO @GUID; 
        SELECT CAST(@GUID as varchar(128)); 
       END 
     END TRY 
     BEGIN CATCH 
      IF (ERROR_NUMBER() = 2627) --Token is valid but session already exists 
       SELECT CAST(@GUID as varchar(128)); 
      ELSE 
       THROW; 
     END CATCH 
     END 
END 

當我模擬這個在SSMS查詢會話這一切工作正常。應用程序遇到問題。

第一部分工作正常:

Public Sub Authenticate(username As String, password As String, connString As String) 
    Using oConn As New SqlConnection(connString) 
     'Check that connection exists and is open 
     oConn.Open() 
     If oConn.State = ConnectionState.Open Then 
      Dim sqlCmd As New SqlCommand("EXEC dbo.sGetHashedPW N'" & username & "'", oConn) 
      _pwhash = sqlCmd.ExecuteScalar 
      _authenticated = EncryptHash.VerifyHash(password, "SHA512", _pwhash) 
      If _authenticated Then 
       _username = username 
       _connString = oConn.ConnectionString 
       sqlCmd.CommandText = "EXEC dbo.sStartSession" 
       _hashToken = sqlCmd.ExecuteScalar 
      Else 
       clearVariables() 
      End If 
     End If 

    End Using 
End Sub 

一切都在這一點上的偉大工程,CONTEXT_INFO在兩個程序設置正確。該連接現在已關閉,但該應用程序具有從sStartSession過程返回的驗證字符串。

當窗體被打開時,一個SqlConnection創建並打開。窗體運行sStartSession,傳遞字符串參數:

Private Sub frmPersonnel_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load 
    Me.MdiParent = frmMain 

    'Open the form's connection and ensure the database session is logged 
    oCN = New SqlConnection(currentUser.ConnectionString) 
    oCN.Open() 
    Dim sqlCmd As New SqlCommand("EXEC dbo.sStartSession '" & currentUser.Token & "'", oCN) 
    Dim sResult = sqlCmd.ExecuteScalar 

這裏的有趣的事情... sResult返回字符串如預期,但立即CONTEXT_INFO設置爲0x00000:

SELECT 
    [session_id], [context_info], 
    CAST([context_info] as uniqueidentifier) as Context, 
    CAST([context_info] as varchar(128)) as Context2, 
    [program_name] 
FROM 
    sys.dm_exec_sessions 
WHERE 
    [program_name] = 'this application' 

ARGHHHH!

+0

爲什麼不使用SELECT CONTEXT_INFO()像http://msdn.microsoft.com/en-us/library/ms180125.aspx – 2013-04-04 11:01:32

+0

CONTEXT_INFO ()是特定於會話的,所以我不能看到什麼應用程序會話的上下文信息從SSMS使用CONTEXT_INFO() – KacireeSoftware 2013-04-04 11:28:37

+0

所以它使用的參數,以防止SQL注入我會改變VB代碼做看到的是https://依據。 github.com/FilipDeVos/5344709並在「使用」語句中正確包裝命令。 – 2013-04-09 10:44:03

回答

1

簡短的回答是所有權鏈接。測試存儲過程,我作爲SA登錄到SSMS。我模擬SSMS會話下的登錄,並且一切正常。使用具有非常有限的安全配置文件的應用程序登錄是問題發生的地方。它結束了該應用程序缺少一個功能的EXECUTE特權:(