65.9K
CodeProject 正在变化。 阅读更多。
Home

托管平台的可安全中立的互斥类

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.93/5 (6投票s)

2006年5月5日

4分钟阅读

viewsIcon

39897

downloadIcon

174

一篇关于可在任何托管平台上使用的安全中立互斥体类的文章。

引言

本文旨在向您介绍一个新的托管互斥体类 MutexSecurityNeutral。现有的 .NET 框架提供的 System.Threading.Mutex 类存在一个限制,即只能使用一个安全上下文,也就是创建它的那个上下文。一旦在特定的安全上下文中创建,就无法在不同的用户/安全上下文中打开/创建它。这里提供了一个针对此问题的解决方案——MutexSecurityNeutral 类。

基本思想是通过指定具有空 DACL 的安全描述符来创建一个 Win32 内核互斥体对象。安全描述符中的空 DACL 意味着 **“所有人均可访问”**。好吧,我承认使用空 DACL 创建内核对象确实可能成为拒绝服务 (DoS) 攻击的入口点。是否使用此方法完全取决于您和您系统的安全关键性。

背景

不久前,我的一位朋友在 Web 应用程序中创建互斥体对象时遇到了“访问被拒绝”的错误。这个 ASP.NET – C# Web 应用程序使用的是 Windows 身份验证,并且 `Impersonation` 设置为 true。如果 `Impersonation` 设置为 false,则相同的代码可以正常工作。

问题原因分析

当 ASP.NET 应用程序在 Windows 身份验证下运行,并且 `Impersonation` 设置为 true 时,ASP.NET 工作进程 aspnet_wp.exe 将以登录用户的上下文运行。但如果 `Impersonation` 设置为 false,则被模拟的用户上下文将始终是系统用户 ASPNET

因此,在 `Impersonation` 设置为 true 的 Windows 身份验证场景中,当第一个请求来自用户(例如 domain1\user1)时,互斥体对象将在 domain1\user1 的上下文中创建。如果下一个请求(发送到 inetinfo.exe,然后是 aspnet_wp.exe)来自同一用户,则不会有问题,因为上下文相同。但如果请求来自另一用户(例如 domain1\user2),则问题会升级。在这种情况下,.aspx 页面将在 domain1\user2 的上下文中运行。然后,互斥体对象的创建将失败,因为已存在的互斥体对象关联的安全描述符属于 domain\user1。IIS、ASP.NET 和 Windows 安全模型的详细阐述超出了本文档的范围。CP 上有许多关于此主题的文章。此外,我正在编写一系列关于 Windows 安全模型和互联网的文章。完成后,我会在此处更新。

尽管 .NET 2.0 提供了 `System.Security.AccessControl.MutexSecurity` 等一套新的 Win32 安全模型封装类,但其对象模型不允许将空的 ACE 添加到 DACL 中。其设计目标之一是防止用户创建不安全的内核对象。这时 MutexSecurityNeutral 这个安全中立的托管类型就派上用场了。由于安全描述符使用空 DACL 进行初始化,因此它可以在任何用户上下文中创建。

作为替代方案,如果不需要进程间同步,我们也可以使用 .NET 的 lock 块。

使用代码

MutexSecurityNeutral 类可以使用方式与 .NET 的 Mutex 类相同。在项目中引用 MutexSecurityNeutral.dll。下面是一个同步共享资源的 C# 示例代码。

    SecUtil.MutexSecurityNeutral mutexsecurityneutral = 
             new SecUtil.MutexSecurityNeutral("yourmutexname");

    mutexsecurityneutral.WaitOne();
    nSharedResource++;
    mutexsecurityneutral.Done();

下面展示了 MutexSecurityNeutral 的实现。

namespace SecUtil
{
    public __gc class MutexSecurityNeutral
    {

    private:

        CMutexSecurityNeutralUnmanaged* 
               m_CMutexSecurityNeutralUnmanaged;
        String __gc* m_MutexName;

    public:

        MutexSecurityNeutral(String __gc* MutexName): 
                              m_MutexName(MutexName){    
            m_CMutexSecurityNeutralUnmanaged = NULL;
        }
        ~MutexSecurityNeutral(){
            delete m_CMutexSecurityNeutralUnmanaged;
        }

        bool WaitOne(){                
            char __nogc* szMutexname = 
               static_cast<CHAR *>(Marshal::StringToHGlobalAnsi(
               m_MutexName).ToPointer());
            m_CMutexSecurityNeutralUnmanaged =  new                
              CMutexSecurityNeutralUnmanaged(szMutexname);
            bool  bRtn= m_CMutexSecurityNeutralUnmanaged->WaitOne();
            Marshal::FreeHGlobal( IntPtr((void*)szMutexname));
            return bRtn;        

        } bool
            Done(){ returnm_CMutexSecurityNeutralUnmanaged->Done(); 
        }
    };
}

SecurityNeutralUnmanaged 有两个方法。

  • WaitOne()

    wait() 方法中,它创建 CMutexSecurityNeutralUnmanaged 对象,并将互斥体名称传递给非托管的 wait() 方法。

  • Done()

    调用非托管的 CMutexSecurityNeutralUnmanaged->Done() 方法。此 CMutexSecurityNeutralUnmanagedMutexSecurityNeutral.h 中声明。

class CMutexSecurityNeutralUnmanaged
{

private:
    const char* m_szMutexName;
    HANDLE m_hMutex;

public:
    CMutexSecurityNeutralUnmanaged(const char*  
              szMutexName):m_szMutexName(szMutexName){
        m_hMutex = NULL;
    }

    ~CMutexSecurityNeutralUnmanaged(){
    }

     bool WaitOne(){
        bool rtn = false;
        SECURITY_DESCRIPTOR sd;
        SECURITY_ATTRIBUTES sa;
        try{
            if (!InitializeSecurityDescriptor(&sd, 
                         SECURITY_DESCRIPTOR_REVISION))
                return FALSE;            
            if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE))
                return FALSE;

            sa.nLength = sizeof(sa);
            sa.lpSecurityDescriptor = &sd;
            sa.bInheritHandle = FALSE;
            m_hMutex = CreateMutex(&sa, true, m_szMutexName);
            WaitForSingleObject( m_hMutex, 10000L);
            
            rtn = true;
        }
        catch(...){
            rtn = false;
        }
        return rtn;
     }

     bool Done(){
         bool rtn = false;
         try{
            ReleaseMutex(m_hMutex);
            CloseHandle(m_hMutex);
            rtn = true;
        }
        catch(...){
            rtn = false;
        }
        return rtn;
     }

};

SecurityNeutralUnmanaged 类有两个方法。

  • WaitOne()

    此方法创建 SECURITY_DESCRIPTOR 变量,并通过调用 SetSecurityDescriptorDacl 为其关联一个空 DACL。然后,它调用 CreateMutex 来创建互斥体对象,并使用 WaitForSingleObject 等待互斥体句柄。

  • Done()

    此方法仅释放互斥体并关闭句柄。

摘要

所有 Win32 内核对象都与特定的用户/安全上下文相关联。因此,对于在特定用户安全上下文中创建的 Win32 互斥体对象,它无法在不同的用户上下文中重新创建/打开。换句话说,这些内核对象具有用户亲和性。在桌面应用程序场景中,这可能不是问题,因为所有程序通常都在登录用户的上下文中运行,除非通过编程进行模拟。但如果我们 Web 应用程序场景中使用互斥体之类的内核对象,并且 `Impersonation` 设置为 true,情况就不同了。这确实会带来麻烦。

那么,作为 Web 开发人员,这个内核对象的“用户亲和性”对您有什么影响?.NET 提供了(1.1 和 2.0 版本都有)System.Threading.Mutex 类,但它不能在具有 Windows 身份验证且 `Impersonation` 设置为 true 的 Web 应用程序中使用。对象创建将以“访问被拒绝”错误失败。作为替代方案,您可以使用 MutexSecurityNeutral 类,但它存在使用空 DACL 的安全漏洞。我再次强调,是否使用此类完全取决于您自己决定。

修订历史

  • 2006 年 5 月 5 日 - 版本 1.0 - 首次发布。
© . All rights reserved.