【问题标题】:How to Start/Stop a Windows Service from an ASP.NET app - Security issues如何从 ASP.NET 应用程序启动/停止 Windows 服务 - 安全问题
【发布时间】:2010-10-23 13:00:19
【问题描述】:

这是我的 Windows/.NET 安全堆栈:

  • 在 Windows Server 2003 机器上作为 LocalSystem 运行的 Windows 服务。
  • 一个 .NET 3.5 网站在同一机器上运行,在“默认”生产服务器 IIS 设置下(所以可能作为 NETWORKSERVICE 用户?)

在我的默认 VS2008 DEV 环境中,我有一个方法,它从 ASP.NET 应用程序中调用,效果很好:

private static void StopStartReminderService() {

    ServiceController svcController = new ServiceController("eTimeSheetReminderService");

    if (svcController != null) {
        try {
            svcController.Stop();
            svcController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10));
            svcController.Start();
        } catch (Exception ex) {
            General.ErrorHandling.LogError(ex);
        }
    }
}

当我在生产服务器上运行它时,我从 ServiceController 收到以下错误:

来源:System.ServiceProcess -> System.ServiceProcess.ServiceController -> IntPtr GetServiceHandle(Int32) -> System.InvalidOperationException 消息: 无法在计算机“。”上打开 eTimeSheetReminderService 服务。

为什么会发生这种情况,我该如何解决?

编辑:

答案如下,主要在 cmets 中,但要澄清一下:

  1. 问题与安全相关,因为 NETWORKSERVICE 帐户没有足够的权限来启动/停止服务
  2. 我创建了一个本地用户帐户,并将其添加到 PowerUsers 组(该组几乎具有管理员权限)
  3. 我不希望我的整个 Web 应用程序一直模拟该用户,所以我只在我操作服务的方法中模拟。我通过使用以下资源来帮助我在代码中做到这一点:

MS KB articlethis, just to get a better understanding

注意:我不通过 web.config 进行模拟,而是在代码中进行模拟。请参阅上面的 MS 知识库文章。

【问题讨论】:

  • 这是什么类型的异常? 'System.InvalidOperationException'?
  • @Phaedrus:嘿,是的,是的。我在编辑中添加了更多错误信息。欢呼

标签: asp.net windows-services impersonation security


【解决方案1】:

授予 IIS 启动/停止特定服务的权限:

  • 下载并安装Subinacl.exe。 (一定要获取最新版本!某些资源包中分发的早期版本不起作用!
  • 发出类似于:subinacl /service {yourServiceName} /grant=IIS_WPG=F 的命令

这会将对该特定服务的完整服务控制权限授予内置 IIS_WPG 组。 (这适用于 IIS6 / Win2k3。)YMMV 用于更新版本的 IIS。)

【讨论】:

  • 完美,为我成功了,我什至不必在我的 web.config 中添加模拟。干杯!
  • 我认为这是更好的解决方案。谢谢 Martin_ATS。
  • 使用此工具授予用户权限,即您使用模拟来模拟有权启动和停止服务的用户。
【解决方案2】:

尝试将此添加到您的 Web.Config。

<identity impersonate="true"/>

【讨论】:

  • IIS 中是否启用了匿名访问?
  • 是的,“启用匿名访问”已打勾
  • 禁用它,启用集成 Windows 身份验证。
  • 酷,我试试看。这将如何影响对网站的访问?这是一个公共网站。此外,是否可以仅针对该一种方法进行模拟?无论如何,我会尝试一下,看看它是否有效,谢谢,非常感谢。
  • 啊哈!我们正在取得进展。好的,那行得通。但是,我需要打开匿名访问,我宁愿只为需要它的代码部分提供特殊权限。有任何想法吗?干杯!!
【解决方案3】:

这是一个很好的问题,也引起了我的兴趣......

所以这是我为解决这个问题所做的:

  • 第 1 步:在本地计算机上创建一个具有最低权限的 Windows 用户帐户。
  • 第 2 步:授予此用户通过 subinacl.exe 启动和停止服务的权限
  • 即subinacl.exe /service WindowsServiceName /GRANT=PCNAME\TestUser=STOE
  • 从以下位置下载:http://www.microsoft.com/en-za/download/details.aspx?id=23510
  • 第 3 步:使用 Impersonation 模拟第 1 步中创建的使用来启动和停止服务

    public const int LOGON32_PROVIDER_DEFAULT = 0;
    
    WindowsImpersonationContext _impersonationContext;
    
    [DllImport("advapi32.dll")]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);
    
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern bool RevertToSelf();
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern bool CloseHandle(IntPtr handle);
    
    private bool _impersonate;
    
    public bool ImpersonateValidUser(String userName, String domain, String password)
    {
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;
    
        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    _impersonationContext = tempWindowsIdentity.Impersonate();
                    if (_impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        _impersonate = true;
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        _impersonate = false;
        return false;
    }
    
    #region Implementation of IDisposable
    
    
    
    
    #endregion
    
    #region Implementation of IDisposable
    
    private void Dispose(bool dispose)
    {
        if (dispose)
        {
            if (_impersonate)
                _impersonationContext.Undo();
            _impersonationContext.Dispose();
        }
    }
    
    public void Dispose()
    {
        Dispose(true);
    }
    #endregion
    
    public static void StartStopService(bool startService, string serviceName)
    {
        using (var impersonateClass = new Impersonation())
        {
            impersonateClass.ImpersonateValidUser(Settings.Default.LocalUsername, Settings.Default.Domain, Settings.Default.Password);
            using (var sc = new ServiceController(serviceName))
            {
                if (startService)
                    sc.Start();
                else if (sc.CanStop)
                    sc.Stop();
            }
    
        }
    }
    

【讨论】:

【解决方案4】:

IIS 8 更新(可能还有一些稍早的版本)

用户组 IIS_WPG 不再存在。它已更改为 IIS_IUSRS

此外,要启动停止服务,无需授予完全权限 (F)。启动、停止和暂停服务 (TOP) 的权限就足够了。因此,命令应该是:

subinacl /service {yourServiceName} /grant=IIS_IUSRS=TOP

请注意,在运行此命令之前,您需要将命令提示符(最好提升为以管理员身份运行)指向C:\Windows\System32 文件夹。

如果出现错误,还请确保您已将 subinacl.exe 文件从安装目录复制到 C:\Windows\System32

【讨论】:

    【解决方案5】:

    只是一种预感,但在我看来,错误不一定与安全有关。您是否在生产服务器上为该服务提供了相同的名称?

    【讨论】:

    • @cdonner:是的,我也怀疑过,但不知道如何测试它?是的,名称是相同的,它在代码中定义,在 ServiceInstaller 组件中。关于如何获得关于它是否安全的明确答案的任何想法?欢呼
    • 您可以使用 NET START/STOP 从命令行启动和停止它吗?
    • 嘿伙计,是的,在命令提示符下运行“NET START eTimeSheetReminderService”成功启动服务
    【解决方案6】:

    如果您的 Web 应用程序有数据库并且 windows 服务可以访问它,您只需使用数据库中的标志来重新启动服务。在服务中,您可以读取此标志并在不忙时重新启动等。仅在您可以修改服务代码的情况下。 如果它是第三方服务,您可以创建自己的 Windows 服务并使用数据库配置来控制(重新启动)服务。这是一种安全的方式,并为您提供更多的灵活性和安全性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多