Parallel.ForEach()更改模拟上下文
今天,我们将新创建的ASP.NET应用程序部署到服务器,很快我们意识到存在一个奇怪的安全相关问题,导致应用程序崩溃。这是一个内部应用程序,我们使用模拟来管理用户访问资源的方式。但是,应用程序在用户试图访问完全控制的文件夹时会抛出“拒绝访问”异常。Parallel.ForEach()更改模拟上下文
唯一的例外是实际上是一个AggregateException
并在使用Parallel.ForEach
枚举了一个列表,并在身体内部,它试图访问该文件夹的方法是被抛出,但在这一点上模拟环境得到改变,工人线程作为应用程序池的标识运行,该标识无法访问文件夹,因此是异常。
为了证实这一点,我看着进程标识之前和Parallel.ForEach
体内:
string before = WindowsIdentity.GetCurrent().Name;
Debug.WriteLine("Before Loop: {0}", before);
Parallel.ForEach(myList, currentItem =>
{
string inside = WindowsIdentity.GetCurrent().Name;
Debug.WriteLine("Inside Loop: {0} (Worker Thread {1})", inside, Thread.CurrentThread.ManagedThreadId);
});
当我运行应用程序,这就是被打印出来:
Before Loop: MyDomain\ImpersonatedUser
Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 8)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 6)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7)
Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 9)
Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 10)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 6)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7)
由于你可以看到,一些线程作为模拟身份运行,另一些作为应用程序池运行(在这种情况下,LocalSystem
),并且似乎没有模式。 Call Stack
窗口中的前一帧也会转到非托管kernel32.dll
,这使我认为CLR在将其委托给操作系统之前未验证上下文。
任何想法为什么会发生这种情况?这是一个已知的问题/错误?
不像Task
类,Parallel
似乎并没有被捕获ExecutionContext
您当前运行(该系统在转弯捕捉其持有WindowsIdentity
的SecurityContext
)。它使用当前线程内的一个可用值。
你必须明确地捕捉到所需的环境下:
IntPtr token = WindowsIdentity.GetCurrent().Token;
Parallel.ForEach(myList, currentItem =>
{
using (WindowsIdentity.Impersonate(token))
{
string inside = WindowsIdentity.GetCurrent().Name;
Debug.WriteLine("Inside Loop: {0} (Worker Thread {1})", inside, Thread.CurrentThread.ManagedThreadId);
}
});
Windows中的整个模拟的概念是每个线程的概念。 您会看到,当创建新线程时,它会继承进程的权限令牌。 但是线程不像进程,也有一个模拟令牌。当您调用WindowsIdentity.Impersonate(令牌)时,您在调用线程上设置了模拟令牌。
另外,当您调用模拟时,您将线程的主令牌指针设置为指向模拟令牌,而不是进程的主令牌,这是默认值。
在WinAPI中多一点理解和了解会导致您知道在您的服务中发生的事情是预期的行为。
少一点snark让世界变得更美好,应该是预期的行为。 – Loofer 2015-02-13 15:02:38
但为什么它确实似乎不是一种模式? – abatishchev 2014-09-26 17:53:23
我不太确定。我以前见过有关这种方式的问题。看起来像*应该*被实现的特征,因为它是*期望*行为。 – 2014-09-26 18:00:37
是的,我使用'WindowsIdentity.GetCurrent()。Impersonate()'作为解决方法,但我仍然不明白为什么线程之间存在差异。如果'SecurityContext'没有被捕获,那么一些线程仍然作为模拟身份运行?我觉得还有更多。 – PoweredByOrange 2014-09-26 18:02:41