6

我有一个经典的 ASP 页面 - 用 JScript 编写 - 使用 Scripting.FileSystemObject 将文件保存到网络共享 - 但它不起作用。(“没有权限”)

ASP 页面使用 Windows 身份验证在 IIS 下运行,并启用了模拟。

如果我通过 CScript.exe 在本地运行以下代码块:

var objNet = new ActiveXObject("WScript.Network");
WScript.Echo(objNet.ComputerName);
WScript.Echo(objNet.UserName);
WScript.Echo(objNet.UserDomain);

var fso = new ActiveXObject("Scripting.FileSystemObject");
var path = "\\\\myserver\\my_share\\some_path";
if (fso.FolderExists(path)) {
    WScript.Echo("Yes");
} else {
    WScript.Echo("No");
}

我得到(预期的)输出:

MY_COMPUTER
dylan.beattie
MYDOMAIN
Yes

如果我将相同的代码作为 .ASP 页面的一部分运行,将 Response.Write 替换为 WScript.Echo,我会得到以下输出:

MY_COMPUTER
dylan.beattie
MYDOMAIN
No

现在 - 我的理解是 WScript.Network 对象将检索实际运行代码的线程的当前安全凭证。如果这是正确的 - 那么为什么同一个用户,在同一个域上,从 CScript.exe 与 ASP 得到不同的结果?如果我的 ASP 代码作为dylan.beattie运行,那么为什么我看不到网络共享?如果它不是作为 dylan.beattie 运行的,为什么 WScript.Network 会认为它是?

4

2 回答 2

4

在模拟下,您只能访问本地计算机上的安全资源,而无法通过网络访问任何内容。

在 Windows 上,当您作为模拟用户运行时,您是在所谓的网络令牌下运行的。此令牌具有用于本地计算机访问的用户凭据,但没有用于远程访问的凭据。因此,当您访问网络共享时,您实际上是以匿名用户身份访问它。

当您在桌面上运行进程(如 CScript.exe)时,您将在交互式用户令牌下运行。此令牌具有本地和远程访问的完整凭据,因此您可以访问网络共享。

为了在模拟 Windows 用户时访问远程资源,您必须使用委派而不是模拟。这将涉及对您的 Active Directory 进行一些更改,以允许对您的域中的计算机和/或用户进行委派。这可能存在安全风险,因此应仔细审查。

于 2010-05-10T10:19:18.623 回答
4

你的问题很清楚。在当前的实现中,您只有模拟用户,没有委托。我不想重复斯蒂芬马丁已经写过的信息。我只想添加至少三个解决方案。Stephen Martin 建议的经典委托方式只是一种方式。您可以在这里阅读更多方式:http: //msdn.microsoft.com/en-us/library/ff647404.aspx#paght000023_delegation。我看到了解决问题的三种实用方法:

  1. 将用户的模拟令牌转换为具有模拟授权级别的令牌或新的主令牌。您可以针对DuplicateTokenor执行此操作DuplicateTokenEx

  2. 使用S4U2Self(请参阅http://msdn.microsoft.com/en-us/magazine/cc188757.aspxhttp://msdn.microsoft.com/en-us/library/ms998355.aspx)从关于一个简单的 .NET 语句的旧版本WindowsIdentity wi = new WindowsIdentity(identity);

  3. 您可以使用一个固定帐户访问另一台服务器。它可以是 IIS 应用程序池帐户上的计算机帐户。它可以是另一个固定定义的帐户,仅用于访问文件系统。

重要的是要知道您在运行 IIS 的服务器上拥有哪个版本的 Windows Server,以及您在 Active Directory 中为您的域拥有的域功能级别(如果您选择您的域,您会在“Active Directory 域和信任”工具中看到这一点)并选择“提高域功能级别”)。知道 IIS 的应用程序池在哪个帐户下运行也很有趣。

第一种和第三种方法总是有效的。第三种方式可能对您的环境和文件系统中的当前权限不利。第二个非常优雅。它允许控制从 IIS 访问哪些服务器(文件服务器)。这种方式有一些限制,需要在 Active Directory 中完成一些工作。

因为您使用经典的 ASP,所以必须创建一个小的可编写脚本的软件组件来支持您的实现。

你更喜欢哪种方式?

根据评论中的问题更新:因为您使用经典 ASP,所以您不能直接使用 Win32 API,但您可以在 VB6 或 .NET 中编写一个使用您需要的 API 的小型 COM 组件例如,您可以使用来自http://support.microsoft.com/kb/248187/en的代码。但是你应该在里面做一些其他的事情。所以我现在解释哪个 Win32 API 可以帮助您使用令牌和模拟来完成您需要的一切。

首先是关于模仿的一个小解释。一切都很容易。总有一个主令牌在其下运行进程。可以为任何线程分配另一个令牌(线程令牌)。为此,需要拥有用户令牌hUserToken并调用 API ImpersonateLoggedOnUser(hUserToken);

要返回原始进程令牌(仅适用于当前线程),您可以调用RevertToSelf()function. 用户的令牌将被 IIS 接收并已为您模拟,因为您如此配置了您的网站。要返回原始进程令牌,您应该RevertToSelf()在自定义 COM 组件中实现该函数的调用。可能,如果您不需要在 ASP 页面中执行更多操作,就足够了,但我建议您在操作文件之前更加小心,并将当前用户令牌保存在变量中。然后您对文件系统进行所有操作,最后将用户令牌重新分配回当前线程。您可以将模拟令牌分配给与 相关的线程SetThreadToken(NULL,hUserToken);。要提供(保存)当前线程令牌(在您的情况下为用户令牌),您可以使用OpenThreadTokenAPI。它必须工作。

更新2:可能在一个 ASP 页面末尾RevertToSelf()使用函数对您来说已经可以了。对应的 C# 代码可以是这样的:

在 C# 中创建一个名为“类库”的新项目LoginAdmin。将以下代码粘贴到里面

using System;
using System.Runtime.InteropServices;

namespace LoginAdmin {
    [InterfaceTypeAttribute (ComInterfaceType.InterfaceIsDual)]
    public interface IUserImpersonate {
        [DispId(1)]
        bool RevertToSelf ();
    }

    internal static class NativeMethods {
        [DllImport ("advapi32.dll", SetLastError = true)]
        internal static extern bool RevertToSelf ();
    }

    [ClassInterface (ClassInterfaceType.AutoDual)]
    public class UserImpersonate : IUserImpersonate {
        public UserImpersonate () { }

        public bool RevertToSelf () {
            return NativeMethods.RevertToSelf();
        }
    }
}

在“构建”部分“注册 COM 互操作”中检查项目属性。在项目的“签名”部分检查签署程序集并在“选择强名称密钥文件”中选择<New...>,然后键入任何文件名和密码(或选中“保护我的密钥...”)。最后,您应该在项目的 Properties 部分修改 AssemblyInfo.cs 中的一行:

[assembly: ComVisible (true)]

编译此项目后,您将获得两个文件,LoginAdmin.dll 和 LoginAdmin.tlb。DLL 已在当前计算机上注册。要在另一台计算机上注册,请使用RegAsm.exe

要在 ASP 页上测试此 COM DLL,您可以执行以下操作

<%@ Language="javascript" %>
<html><body>
    <% var objNet = Server.CreateObject("WScript.Network");
       Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
       Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");

       var objLoginAdmin = Server.CreateObject("LoginAdmin.UserImpersonate");
       var isOK = objLoginAdmin.RevertToSelf();
       if (isOK)
              Response.Write("RevertToSelf return true<br/>");
       else
              Response.Write("RevertToSelf return false<br/>");
       Response.Write("One more time after RevertToSelf()<br/>");
       Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
       Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");

       var fso = Server.CreateObject("Scripting.FileSystemObject");
       var path = "\\\\mk01\\C\\Oleg";
       if (fso.FolderExists(path)) {
          Response.Write("Yes");
       } else {
          Response.Write("No");
       }%>
</body></html>

如果用于运行 IIS 应用程序池的帐户可以访问相应的网络共享,则输出将如下所示

Current user: Oleg
Current user's domain: WORKGROUP
RevertToSelf return true
One more time after RevertToSelf()
Current user: DefaultAppPool
Current user's domain: WORKGROUP
Yes 
于 2010-05-10T14:34:11.663 回答