Windows和.NET中的凭证链序列问题

时间:2011-05-10 20:20:06

标签: c# sql-server-2008 windows-authentication powershell-v2.0

问题摘要

在批处理模式下运行时,我的用户身份会丢失,但是(就像一个非常好的悬念)直到最后一刻才会丢失。

问题详情

我在WinXpSp3上运行PowerShell脚本,该脚本在远程计算机上作为特定测试用户(通过-Session参数)在后台运行脚本块(通过Invoke-Command)(通过-AsJob参数)。会话使用以下内容创建:

New-PSSession -computername $myServer -credential $myCredential

脚本块执行许多操作,最终运行NUnit测试框架。正在测试的C#代码记录了“myTestUser”用户名(通过Environment.UserName),因此PowerShell提供的凭据已正确接收到目前为止。 Process Explorer进一步确认了这一点:检查运行批处理测试的nunit-console的属性显示它由myTestUser拥有。

该测试包括访问Sql Server 2008 R2数据库;连接字符串是通过new SqlConnection(connectionString)调用设置的。连接字符串已设置为Windows身份验证并使用以下格式:

Data Source=<my_db_server_name>;Initial Catalog=<my_db_name>;Integrated Security=True;Persist Security Info=True

即使我最终将myTestUser凭据一直推送到测试中的C#代码,但数据库访问尝试没有看到这些凭据,导致此错误:用户'NT AUTHORITY \ ANONYMOUS LOGON登录失败“

一些补充信息:

  1. 我已经确认测试用户(myTestUser)具有数据库权限,并且NUnit测试能够访问数据库:当我以myTestUser身份手动运行NUnit测试(通过NUnit GUI)时,测试工作正常并且SqlProfiler清楚地显示此活动,myTestUser出现在NTUserName列中。
  2. 如果我在本地运行而不是在远程计算机上运行,​​则会发生同样的错误。
  3. 如果我在本地计算机上以自己身份运行(即省略-credential参数),则会出现同样的错误。
  4. 问题

    如何从毁灭的边缘拯救myTestUser并让他访问数据库?


    2011.05.16更新

    这是一个展示相同问题的简化示例。

    首先,我的测试程序DBFetchVersion打印当前用户的名称和简单查询的结果:

    class Program
    {
        const string connString = ...your connection string here... ;
        const string query = "SELECT getdate() [Date], substring(@@version,1,charindex('-',@@version)-1) +convert(varchar(100),SERVERPROPERTY('edition'))+ ' ' +convert(varchar(100),SERVERPROPERTY('productlevel')) [SQL Server Version], @@servicename [Service Name], @@servername [Server Host], db_name() [Database], user_name() [User], host_name() [Client]";
    
        static void Main(string[] args)
        {
            DataView dataView;
            using (var connection = new SqlConnection(connString))
            {
                Console.WriteLine("user = " + Environment.UserName);
                using (var dataAdapter = new SqlDataAdapter(query, connection))
                {
                    var dataSet = new DataSet();
                    try
                    {
                        connection.Open();
                        dataAdapter.SelectCommand.CommandType = CommandType.Text;
                        dataAdapter.Fill(dataSet, query);
                    }
                    finally { if (connection.State == ConnectionState.Open) connection.Close(); }
                    dataView = dataSet.Tables[0].DefaultView;
                }
                foreach (var item in dataView.Table.Rows[0].ItemArray)
                    Console.WriteLine(item);
            }
        }
    }
    

    以下是调用上述程序的Powershell脚本。

    $scriptBlock = {
        & "...path to my executable...\DBFetchVersion\bin\Debug\DBFetchVersion.exe"
    }
    
    $serverName = ... my server name ...
    $username = "testuser"
    $password = ... my user password ...
    $adjPwd = $password | ConvertTo-SecureString -asPlainText -Force
    $testCred = (New-Object System.Management.Automation.PSCredential($username,$adjPwd))    
    $mySession = New-PSSession -computername $serverName  -credential $testCred 
    
    # Test Scenarios:
    Invoke-Command $scriptBlock 
    #Invoke-Command $scriptBlock -computername $serverName 
    #Invoke-Command $scriptBlock -computername $serverName  -credential $testCred 
    #Invoke-Command $scriptBlock -Session $mySession 
    

    在最后的四个测试方案列表中,未注释的方案可以正常工作,打印我的用户名和查询结果。 DBFetchVersion仍然报告我是第二行的用户,但数据库连接失败,并显示“用户登录失败'NT AUTHORITY \ ANONYMOUS LOGON'”错误。 其余两行报告“testuser”用户名,但两者都报告了数据库连接的相同登录失败。

    这个孤立的例子告诉我的并不是我认为有关Powershell,.NET或我的代码的任何错误,但是由于指定另一台计算机或会话,我还有一些我不了解的认证机制两者都涉及一条在某种意义上应该有更强保护的道路。

    2011.08.03更新 - 尤里卡!

    好吧,Matt将双跃点问题确定为罪魁祸首,并将 CredSSP 身份验证作为解决方案。不幸的是,正如我很快发现的那样,CredSSP需要Windows 7,因此我开始将几个虚拟机设置为沙箱。然而,CredSSP不是轻易放弃其秘密的人(至少对我而言),正如我在ServerFault上的这篇文章中所详述的那样:Cannot get CredSSP authentication to work in PowerShell

    我终于能够获得CredSSP身份验证,因此我可以回到我在这个帖子中提出的问题。作为测试,我使用插入上面提供的PowerShell脚本的这3个脚本块:

    $scriptBlockA = {
        Write-Host ("hello, world: {0}, {1}" -f $env:USERNAME, (hostname))
    }
    
    # Simple DB test, but requires SqlServer installed!
    $scriptBlockB = {
        if (! (Get-PSSnapin | ? { $_.name -eq "SqlServerCmdletSnapin100" } ) )
        { Add-PSSnapin SqlServerCmdletSnapin100; }
        Invoke-Sqlcmd -Query "SELECT getdate() as [Now]" -ServerInstance CinDevDB5
    }
    
    # Indirect DB test; requires .NET but not SqlServer,
    # plus DBFetchVersion in home dir for targeted user.
    $scriptBlockC = {
        & ".\DBFetchVersion.exe"
    }
    

    Block A使用或不使用CredSSP,因为没有双跳。块B和C仅适用于CredSSP,因为它们都尝试访问远程数据库。 QED。

1 个答案:

答案 0 :(得分:0)

最初我读过这篇文章并想到了“双跳”问题,但补充信息可能是我的问题。

当您在本地(作为您自己或testuser)运行它时,您使用哪些命令?这样:

& "...path to my executable...\DBFetchVersion\bin\Debug\DBFetchVersion.exe"

也可以从本地计算机(作为您自己或用户)执行此操作:

Add-PSSnapin SqlServerCmdletSnapin100;
Invoke-Sqlcmd -Query "SELECT getdate()" -ServerInstance Server

你还在使用什么操作系统?如果它是Windows 2008并且问题是双跳,您可以让CredSSP避免它。