HOW TO:在沙箱中執行部分信任的程式碼How to: Run Partially Trusted Code in a Sandbox

警告

程式碼存取安全性和部分信任的程式碼Code Access Security and Partially Trusted Code

.NET Framework 提供一個稱為程式碼存取安全性 (CAS) 的機制,可對在同一個應用程式中執行的不同程式碼強制執行各種信任層級。The .NET Framework provides a mechanism for the enforcement of varying levels of trust on different code running in the same application called Code Access Security (CAS). .NET Framework 中的程式碼存取安全性不應該用作一種機制,以根據程式碼來源或其他身分識別層面的來強制安全性界限。Code Access Security in .NET Framework should not be used as a mechanism for enforcing security boundaries based on code origination or other identity aspects. 我們正在更新指南,以反映程式碼存取安全性與安全性透明的程式碼,將不會如同部分程式碼受信任的安全性界限般受到支援,特別是來源不明的程式碼。We are updating our guidance to reflect that Code Access Security and Security-Transparent Code will not be supported as a security boundary with partially trusted code, especially code of unknown origin. 建議不要載入及執行未知來源的程式碼,如此便不需要使用替代的安全措施。We advise against loading and executing code of unknown origins without putting alternative security measures in place.

這項原則適用於所有 .NET Framework 版本,但不適用於 Silverlight 隨附的 .NET Framework。This policy applies to all versions of .NET Framework, but does not apply to the .NET Framework included in Silverlight.

沙箱是指在受限制的安全性環境中執行程式碼的做法,這會限制授與程式碼的存取權限。Sandboxing is the practice of running code in a restricted security environment, which limits the access permissions granted to the code. 例如,如果您有來自不完全信任來源的 Managed 程式庫,則不應該以完全信任的方式執行。For example, if you have a managed library from a source you do not completely trust, you should not run it as fully trusted. 相反地,您應該將程式碼放在沙箱,限制其權限為您所預期它會需要的權限 (例如,Execution 權限)。Instead, you should place the code in a sandbox that limits its permissions to those that you expect it to need (for example, Execution permission).

您也可以使用沙箱來測試您將散發的程式碼,該程式碼將在部分信任環境中執行。You can also use sandboxing to test code you will be distributing that will run in partially trusted environments.

AppDomain 是提供沙箱給 Managed 應用程式的有效方式。An AppDomain is an effective way of providing a sandbox for managed applications. 用來執行部分信任程式碼的應用程式定義域具有權限,於 AppDomain 中執行時,會定義可以使用的受保護資源。Application domains that are used for running partially trusted code have permissions that define the protected resources that are available when running within that AppDomain. AppDomain 內部執行的程式碼會由與 AppDomain 相關聯的權限繫結,並且僅允許存取指定的資源。Code that runs inside the AppDomain is bound by the permissions associated with the AppDomain and is allowed to access only the specified resources. AppDomain 也包含 StrongName 陣列,用來識別要以完全信任方式載入的組件。The AppDomain also includes a StrongName array that is used to identify assemblies that are to be loaded as fully trusted. 這可讓 AppDomain 的建立者啟動新的沙箱定義域,可允許特定協助程式組件變成完全信任。This enables the creator of an AppDomain to start a new sandboxed domain that allows specific helper assemblies to be fully trusted. 另一個以完全信任方式載入組件的選項為將它們放在全域組件快取中;不過,這會在該電腦上建立的應用程式定義域中以完全信任方式載入所有組件。Another option for loading assemblies as fully trusted is to place them in the global assembly cache; however, that will load assemblies as fully trusted in all application domains created on that computer. 強式名稱的清單支援針對每個 AppDomain 的決策,這會提供更嚴格的判斷。The list of strong names supports a per-AppDomain decision that provides more restrictive determination.

您可以使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法多載來指定在沙箱中執行的應用程式之權限集。You can use the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to specify the permission set for applications that run in a sandbox. 這個多載可讓您指定您想要的程式碼存取安全性之確切層級。This overload enables you to specify the exact level of code access security you want. 使用這個多載來載入至 AppDomain 的組件可以只具有指定的授權集,或者可以是完全信任的。Assemblies that are loaded into an AppDomain by using this overload can either have the specified grant set only, or can be fully trusted. 如果組件位於全域組件快取中或在 fullTrustAssemblies (StrongName) 陣列參數中列出,則會授與組件完全信任。The assembly is granted full trust if it is in the global assembly cache or listed in the fullTrustAssemblies (the StrongName) array parameter. 只應加入已知為完全信任的組件至 fullTrustAssemblies 清單。Only assemblies known to be fully trusted should be added to the fullTrustAssemblies list.

此多載具有下列簽章:The overload has the following signature:

AppDomain.CreateDomain( string friendlyName,  
                        Evidence securityInfo,  
                        AppDomainSetup info,  
                        PermissionSet grantSet,  
                        params StrongName[] fullTrustAssemblies);  

CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法多載的參數會指定 AppDomain 的名稱、AppDomain 的辨識項、會指定沙箱之應用程式基底的 AppDomainSetup 物件、要使用的授權集,以及完全信任組件的強式名稱。The parameters for the CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload specify the name of the AppDomain, the evidence for the AppDomain, the AppDomainSetup object that identifies the application base for the sandbox, the permission set to use, and the strong names for fully trusted assemblies.

基於安全性理由,info 參數中指定的應用程式基底不能為裝載應用程式的應用程式基底。For security reasons, the application base specified in the info parameter should not be the application base for the hosting application.

對於 grantSet 參數,您可以指定已明確建立的權限集合或 GetStandardSandbox 方法所建立的標準權限集。For the grantSet parameter, you can specify either a permission set you have explicitly created, or a standard permission set created by the GetStandardSandbox method.

不同於大部分 AppDomain 的載入,AppDomain 辨識項 (由 securityInfo 參數所提供) 不用來為部分信任的組件判斷授權集。Unlike most AppDomain loads, the evidence for the AppDomain (which is provided by the securityInfo parameter) is not used to determine the grant set for the partially trusted assemblies. 相反地,它由 grantSet 參數獨立指定。Instead, it is independently specified by the grantSet parameter. 不過,辨識項可以用於其他用途,例如判斷隔離儲存區的範圍。However, the evidence can be used for other purposes such as determining the isolated storage scope.

在沙箱中執行應用程式To run an application in a sandbox

  1. 建立要授與給不受信任的應用程式之授權集。Create the permission set to be granted to the untrusted application. 您可以授與的最小權限是 Execution 權限。The minimum permission you can grant is Execution permission. 您也可授與其他您認為對不受信任的程式碼可能安全的權限;例如 IsolatedStorageFilePermissionYou can also grant additional permissions you think might be safe for untrusted code; for example, IsolatedStorageFilePermission. 下列程式碼會建立新的權限集合,其中只有 Execution 權限。The following code creates a new permission set with only Execution permission.

    PermissionSet permSet = new PermissionSet(PermissionState.None);  
    permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));  
    

    或者,您可以使用現有的具名使用權限集合,例如網際網路。Alternatively, you can use an existing named permission set, such as Internet.

    Evidence ev = new Evidence();  
    ev.AddHostEvidence(new Zone(SecurityZone.Internet));  
    PermissionSet internetPS = SecurityManager.GetStandardSandbox(ev);  
    

    GetStandardSandbox 方法會根據在辨識項中的區域傳回 Internet 權限集合或 LocalIntranet 權限集合。The GetStandardSandbox method returns either an Internet permission set or a LocalIntranet permission set depending on the zone in the evidence. 對於某些做為參考傳遞的辨識項物件,GetStandardSandbox 也會建構其識別權限。GetStandardSandbox also constructs identity permissions for some of the evidence objects passed as references.

  2. 簽署包含裝載類別 (在此範例中名為 Sandboxer) 的組件,該類別會呼叫不受信任的程式碼。Sign the assembly that contains the hosting class (named Sandboxer in this example) that calls the untrusted code. 加入 StrongName,這會用來簽署組件給 CreateDomain 呼叫的 fullTrustAssemblies 參數之 StrongName 陣列。Add the StrongName used to sign the assembly to the StrongName array of the fullTrustAssemblies parameter of the CreateDomain call. 裝載的類別必須以完全信任方式執行,以啟用部分信任程式碼的執行,或提供服務給部分信任應用程式。The hosting class must run as fully trusted to enable the execution of the partial-trust code or to offer services to the partial-trust application. 這就是您讀取組件 StrongName 的方式。This is how you read the StrongName of an assembly:

    StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();  
    

    .NET Framework 組件,例如 mscorlib 和 System.dll 就不必加入完全信任清單,因為它們會以完全信任方式從全域組件快取中載入。.NET Framework assemblies such as mscorlib and System.dll do not have to be added to the full-trust list because they are loaded as fully trusted from the global assembly cache.

  3. 初始化 CreateDomain 方法的 AppDomainSetup 參數。Initialize the AppDomainSetup parameter of the CreateDomain method. 使用這個參數,您可以控制許多新的 AppDomain 設定。With this parameter, you can control many of the settings of the new AppDomain. ApplicationBase 屬性是重要的設定,而且應該不同於裝載應用程式的 AppDomainApplicationBase 屬性。The ApplicationBase property is an important setting, and should be different from the ApplicationBase property for the AppDomain of the hosting application. 如果 ApplicationBase 設定相同,則部分信任應用程式可取得裝載的應用程式,以載入 (以完全信任方式) 它所定義的例外狀況,因而加以利用。If the ApplicationBase settings are the same, the partial-trust application can get the hosting application to load (as fully trusted) an exception it defines, thus exploiting it. 這是為什麼不建議使用 catch (例外狀況) 的另一個原因。This is another reason why a catch (exception) is not recommended. 將主機的應用程式基底設定為不同於沙箱應用程式的應用程式基底,可減少惡意探索的風險。Setting the application base of the host differently from the application base of the sandboxed application mitigates the risk of exploits.

    AppDomainSetup adSetup = new AppDomainSetup();  
    adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);  
    
  4. 呼叫 CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法多載來使用我們已指定的參數建立應用程式定義域。Call the CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to create the application domain using the parameters we have specified.

    此方法的簽章是:The signature for this method is:

    public static AppDomain CreateDomain(string friendlyName,   
        Evidence securityInfo, AppDomainSetup info, PermissionSet grantSet,   
        params StrongName[] fullTrustAssemblies)  
    

    其他資訊:Additional information:

    • 這是採用 PermissionSet 做為參數的 CreateDomain 方法之唯一多載,因此也是讓您在部分信任設定中載入應用程式的唯一多載。This is the only overload of the CreateDomain method that takes a PermissionSet as a parameter, and thus the only overload that lets you load an application in a partial-trust setting.

    • evidence 參數不用來計算權限集合;它由 .NET Framework 的其他功能用來進行識別。The evidence parameter is not used to calculate a permission set; it is used for identification by other features of the .NET Framework.

    • 設定 info 參數的 ApplicationBase 屬性對於這個多載而言是強制的。Setting the ApplicationBase property of the info parameter is mandatory for this overload.

    • fullTrustAssemblies 參數具有 params 關鍵字,這表示不需要建立 StrongName 陣列。The fullTrustAssemblies parameter has the params keyword, which means that it is not necessary to create a StrongName array. 允許將 0、1 或更多的強式名稱當做參數傳遞。Passing 0, 1, or more strong names as parameters is allowed.

    • 若要建立應用程式定義域的程式碼是:The code to create the application domain is:

    AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);  
    
  5. 將程式碼載入您所建立的沙箱 AppDomain 中。Load the code into the sandboxing AppDomain that you created. 有兩種方法可以達到這個目的:This can be done in two ways:

    第二種方法更合適,因為它讓您更輕鬆地將參數傳遞給新的 AppDomain 執行個體。The second method is preferable, because it makes it easier to pass parameters to the new AppDomain instance. CreateInstanceFrom 方法提供兩個重要功能:The CreateInstanceFrom method provides two important features:

    • 您可使用會指向不包含組件位置的程式碼基底。You can use a code base that points to a location that does not contain your assembly.

    • 您可以在 Assert 之下建立完全信任 (PermissionState.Unrestricted),這可讓您建立關鍵類別的執行個體。You can do the creation under an Assert for full-trust (PermissionState.Unrestricted), which enables you to create an instance of a critical class. (每當您的組件不具有透明度標記,且以完全信任方式載入時就會發生)。因此您應特別小心,只能建立您信任此函式的程式碼,我們建議您在新的應用程式定義域中只建立完全信任類別的執行個體。(This happens whenever your assembly has no transparency markings and is loaded as fully trusted.) Therefore, you have to be careful to create only code that you trust with this function, and we recommend that you create only instances of fully trusted classes in the new application domain.

    ObjectHandle handle = Activator.CreateInstanceFrom(  
    newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,  
           typeof(Sandboxer).FullName );  
    

    請注意若要在新的定義域中建立類別的執行個體,此類別必須擴充 MarshalByRefObject 類別Note that in order to create an instance of a class in a new domain, the class has to extend the MarshalByRefObject class

    class Sandboxer:MarshalByRefObject  
    
  6. 解除包裝新定義域之執行個體至此定義域的參考。Unwrap the new domain instance into a reference in this domain. 這個參考用來執行不受信任的程式碼。This reference is used to execute the untrusted code.

    Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();  
    
  7. 請在您剛建立的 Sandboxer 類別之執行個體中呼叫 ExecuteUntrustedCode 方法。Call the ExecuteUntrustedCode method in the instance of the Sandboxer class you just created.

    newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);  
    

    這個呼叫會在沙箱應用程式定義域中執行,具有受限制的使用權限。This call is executed in the sandboxed application domain, which has restricted permissions.

    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)  
    {  
        //Load the MethodInfo for a method in the new assembly. This might be a method you know, or   
        //you can use Assembly.EntryPoint to get to the entry point in an executable.  
        MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);  
        try  
        {  
            // Invoke the method.  
            target.Invoke(null, parameters);  
        }  
        catch (Exception ex)  
        {  
        //When information is obtained from a SecurityException extra information is provided if it is   
        //accessed in full-trust.  
            new PermissionSet(PermissionState.Unrestricted).Assert();  
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());  
            CodeAccessPermission.RevertAssert();  
            Console.ReadLine();  
        }  
    }  
    

    System.Reflection 用來在部分信任組件中取得方法的控制代碼。System.Reflection is used to get a handle of a method in the partially trusted assembly. 控制代碼可用來以安全的方式搭配最小權限執行程式碼。The handle can be used to execute code in a safe way with minimum permissions.

    在先前的程式碼中,請先注意完全信任權限的 Assert,然後才列印 SecurityExceptionIn the previous code, note the Assert for the full-trust permission before printing the SecurityException.

    new PermissionSet(PermissionState.Unrestricted).Assert()  
    

    完全信任的判斷提示用來從 SecurityException 取得擴充的資訊。The full-trust assert is used to obtain extended information from the SecurityException. 在沒有 Assert 的情況下,SecurityExceptionToString 方法會在堆疊上探索部分信任程式碼,並且限制傳回的資訊。Without the Assert, the ToString method of SecurityException will discover that there is partially trusted code on the stack and will restrict the information returned. 如果部分信任程式碼可能讀取該資訊,則這可能會造成安全性問題,但不授與 UIPermission 可降低風險。This could cause security issues if the partial-trust code could read that information, but the risk is mitigated by not granting UIPermission. 應該謹慎使用完全信任的判斷提示,而且僅當您確定不允許部分信任程式碼提升為完全信任時才能使用。The full-trust assert should be used sparingly and only when you are sure that you are not allowing partial-trust code to elevate to full trust. 在相同的函式中,以及為了完全信任而呼叫判斷提示之後,通常請不要呼叫您不信任的程式碼。As a rule, do not call code you do not trust in the same function and after you called an assert for full trust. 當您使用完畢後,一律將判斷提示還原會是最佳的做法。It is good practice to always revert the assert when you have finished using it.

範例Example

下列範例會實作上一節中的程序。The following example implements the procedure in the previous section. 在此範例中,Visual Studio 方案裡名為 Sandboxer 的專案還包含一個名為 UntrustedCode 的專案,這會實作 UntrustedClass 類別。In the example, a project named Sandboxer in a Visual Studio solution also contains a project named UntrustedCode, which implements the class UntrustedClass. 此案例假定您已下載包含方法的程式庫組件,該方法預期會傳回 truefalse 來指出您提供的數字是否為費式數列。This scenario assumes that you have downloaded a library assembly containing a method that is expected to return true or false to indicate whether the number you provided is a Fibonacci number. 相反地,該方法會嘗試從您的電腦讀取檔案。Instead, the method attempts to read a file from your computer. 下列範例顯示未受信任的程式碼。The following example shows the untrusted code.

using System;  
using System.IO;  
namespace UntrustedCode  
{  
    public class UntrustedClass  
    {  
        // Pretend to be a method checking if a number is a Fibonacci  
        // but which actually attempts to read a file.  
        public static bool IsFibonacci(int number)  
        {  
           File.ReadAllText("C:\\Temp\\file.txt");  
           return false;  
        }  
    }  
}  

下列範例顯示執行不受信任程式碼的 Sandboxer 應用程式之程式碼。The following example shows the Sandboxer application code that executes the untrusted code.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.IO;  
using System.Security;  
using System.Security.Policy;  
using System.Security.Permissions;  
using System.Reflection;  
using System.Runtime.Remoting;  
  
//The Sandboxer class needs to derive from MarshalByRefObject so that we can create it in another   
// AppDomain and refer to it from the default AppDomain.  
class Sandboxer : MarshalByRefObject  
{  
    const string pathToUntrusted = @"..\..\..\UntrustedCode\bin\Debug";  
    const string untrustedAssembly = "UntrustedCode";  
    const string untrustedClass = "UntrustedCode.UntrustedClass";  
    const string entryPoint = "IsFibonacci";  
    private static Object[] parameters = { 45 };  
    static void Main()  
    {  
        //Setting the AppDomainSetup. It is very important to set the ApplicationBase to a folder   
        //other than the one in which the sandboxer resides.  
        AppDomainSetup adSetup = new AppDomainSetup();  
        adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);  
  
        //Setting the permissions for the AppDomain. We give the permission to execute and to   
        //read/discover the location where the untrusted code is loaded.  
        PermissionSet permSet = new PermissionSet(PermissionState.None);  
        permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));  
  
        //We want the sandboxer assembly's strong name, so that we can add it to the full trust list.  
        StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();  
  
        //Now we have everything we need to create the AppDomain, so let's create it.  
        AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);  
  
        //Use CreateInstanceFrom to load an instance of the Sandboxer class into the  
        //new AppDomain.   
        ObjectHandle handle = Activator.CreateInstanceFrom(  
            newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,  
            typeof(Sandboxer).FullName  
            );  
        //Unwrap the new domain instance into a reference in this domain and use it to execute the   
        //untrusted code.  
        Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();  
        newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);  
    }  
    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)  
    {  
        //Load the MethodInfo for a method in the new Assembly. This might be a method you know, or   
        //you can use Assembly.EntryPoint to get to the main function in an executable.  
        MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);  
        try  
        {  
            //Now invoke the method.  
            bool retVal = (bool)target.Invoke(null, parameters);  
        }  
        catch (Exception ex)  
        {  
            // When we print informations from a SecurityException extra information can be printed if we are   
            //calling it with a full-trust stack.  
            new PermissionSet(PermissionState.Unrestricted).Assert();  
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());  
            CodeAccessPermission.RevertAssert();  
            Console.ReadLine();  
        }  
    }  
}  

另請參閱See also