FileSystemObject 的文件权限 - CScript.exe 说的是一回事,经典 ASP 说的是另一回事

发布于 2024-08-31 08:52:42 字数 1004 浏览 4 评论 0原文

我有一个经典的 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 认为它是?

I have a classic ASP page - written in JScript - that's using Scripting.FileSystemObject to save files to a network share - and it's not working. ("Permission denied")

The ASP page is running under IIS using Windows authentication, with impersonation enabled.

If I run the following block of code locally via 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");
}

I get the (expected) output:

MY_COMPUTER
dylan.beattie
MYDOMAIN
Yes

If I run the same code as part of a .ASP page, substituting Response.Write for WScript.Echo I get this output:

MY_COMPUTER
dylan.beattie
MYDOMAIN
No

Now - my understanding is that the WScript.Network object will retrieve the current security credentials of the thread that's actually running the code. If this is correct - then why is the same user, on the same domain, getting different results from CScript.exe vs ASP? If my ASP code is running as dylan.beattie, then why can't I see the network share? And if it's not running as dylan.beattie, why does WScript.Network think it is?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

画骨成沙 2024-09-07 08:52:42

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

  1. 将用户的模拟令牌转换为具有模拟委托级别的令牌或新的主要令牌。您可以针对 DuplicateTokenDuplicateTokenEx 执行此操作。

  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 中编写一个小型 COM 组件,它使用您需要的 API。例如,您可以使用 http://support.microsoft.com/kb/248187/ 中的代码zh-cn。但你应该在里面做一些其他的事情。所以我现在解释一下哪个 Win32 API 可以帮助您使用令牌和模拟完成您需要的一切。

首先对模仿进行一个简单的解释。一切都很容易。进程运行时始终有一个主要令牌。可以向任何线程分配另一个令牌(线程令牌)。为此,需要拥有用户 hUserToken 的令牌并调用 API ImpersonateLoggedOnUser(hUserToken);

要返回到原始进程令牌(仅适用于当前线程),您可以调用 RevertToSelf() 函数。 IIS 将接收并模拟您的用户令牌,因为您是这样配置您的网站的。要返回到原始进程令牌,您应该在自定义 COM 组件中实现对函数 RevertToSelf() 的调用。也许,如果您不需要在 ASP 页面中执行任何其他操作,那就足够了,但我建议您更加小心,并在对文件进行操作之前将当前用户令牌保存在变量中。然后,您对文件系统进行所有操作,最后将用户令牌重新分配回当前线程。您可以通过 SetThreadToken(NULL,hUserToken); 将模拟令牌分配给线程。要提供(保存)当前线程令牌(在您的情况下为用户令牌),您可以使用 OpenThreadToken API。它必须有效。

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

在C#中创建一个类型为“Class Library”的新项目,名称为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 互操作”中的检查项目属性中。在项目的“签名”部分中,选中对程序集进行签名,然后在“选择强名称密钥文件”中选择 ,然后键入任何文件名和密码(或选中“保护我的钥匙......”)。最后,您应该在项目的“属性”部分中修改 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 

Your problem is clear. In the current implementation you have only impersonation of users and no delegation. I don't want to repeat information already written by Stephen Martin. I only want to add at least three solutions. The classical way of delegation which Stephen Martin suggests is only one way. You can read some more ways here: http://msdn.microsoft.com/en-us/library/ff647404.aspx#paght000023_delegation. I see three practical ways of you solving your problem:

  1. Convert the impersonation token of the user to a token with delegation level of impersonation or to a new primary token. You can do this with respect of DuplicateToken or DuplicateTokenEx.

  2. Use S4U2Self (see http://msdn.microsoft.com/en-us/magazine/cc188757.aspx and http://msdn.microsoft.com/en-us/library/ms998355.aspx) to receive a new token from the old one with respect of one simple .NET statement WindowsIdentity wi = new WindowsIdentity(identity);

  3. You can access another server with respect of one fixed account. It can be a computer account on an account of the application pool of the IIS. It can be another fixed defined account which one will only use for access to the file system.

It is important to know which version of Windows Server you have on the server where IIS is running and which Domain Function Level you have in Active Directory for your Domain (you see this in "Active Directory Domain and Trusts" tool if you select your domain and choose "Raise Domain Functional Level"). It is also interesting to know under which account the application pool of the IIS runs.

The first and the third way will always work. The third way can be bad for your environment and for the current permission in the file system. The second one is very elegant. It allows control of which servers (file server) are accessed from IIS. This way has some restrictions and it needs some work to be done in Active Directory.

Because you use classic ASP, a small scriptable software component must be created to support your implementation.

Which way do you prefer?

UPDATED based on the question from comment: Because you use classic ASP you can not use a Win32 API directly, but you can write a small COM component in VB6 or in .NET which use APIs which you need. As an example you can use code from http://support.microsoft.com/kb/248187/en. But you should do some other things inside. So I explain now which Win32 API can help you to do everything what you need with tokens and impersonation.

First of all a small explanation about impersonation. Everything works very easy. There are always one primary token under which the process runs. To any thread another token (thread token) can be assigned. To do this one needs to have a token of a user hUserToken and call API ImpersonateLoggedOnUser(hUserToken);.

To go back to the original process token (for the current thread only) you can call RevertToSelf() function. The token of user will be received and already impersonated for you by IIS, because you so configured your Web Site. To go back to the original process token you should implement calling of the function RevertToSelf() in your custom COM component. Probably, if you need to do nothing more in the ASP page, it will be enough, but I recommend you be more careful and save current users token in a variable before operation with files. Then you make all operations with file system and at the end reassign users token back to the current thread. You can assign an impersonation token to a thread with respect of SetThreadToken(NULL,hUserToken);. To give (save) current thread token (user token in your case) you can use OpenThreadToken API. It must work.

UPDATED 2: Probably the usage of RevertToSelf() function at the end of one ASP page would be already OK for you. The corresponding C# code can be so:

Create a new Project in C# of the type "Class Library" with the name LoginAdmin. Paste the following code inside

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();
        }
    }
}

Check in project properties in "Build" part "Register for COM interop". In "Signing" part of the project check Sign the assembly and in "Choose a strong name key file" choose <New...>, then type any filename and password (or check off "protect my key..."). At the end you should modify a line from AssemblyInfo.cs in Properties part of the project:

[assembly: ComVisible (true)]

After compiling this project you get two files, LoginAdmin.dll and LoginAdmin.tlb. The DLL is already registered on the current computer. To register if on the other computer use RegAsm.exe.

To test this COM DLL on a ASP page you can do following

<%@ 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>

If the account used to run the IIS application pool has access to the corresponding network share, the output will be look like following

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 
可遇━不可求 2024-09-07 08:52:42

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

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

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

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

Under impersonation you can only access securable resources on the local computer you cannot access anything over the network.

On Windows when you are running as an impersonated user you are running under what is called a Network token. This token has the user's credentials for local computer access but has no credentials for remote access. So when you access the network share you are actually accessing it as the Anonymous user.

When you are running a process on your desktop (like CScript.exe) then you are running under an Interactive User token. This token has full credentials for both local and remote access, so you are able to access the network share.

In order to access remote resources while impersonating a Windows user you must use Delegation rather then Impersonation. This will involve some changes to your Active directory to allow delegation for the computer and/or the users in your domain. This can be a security risk so it should be reviewed carefully.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文