Code Access Security in ASP.NET 4 Applications

Code access security (CAS) is the .NET Framework security mechanism (the "sandbox") that is used by ASP.NET to enforce constraints on the ability to execute code. ASP.NET has implemented code access security since ASP.NET 1.1 by using the concept of trust levels.

This topic describes how CAS works in ASP.NET 4, with a particular emphasis on how it has changed since earlier versions. If you are new to ASP.NET, the changes are probably not of interest to you. However, this topic includes information about how CAS works in the current version of ASP.NET, which can affect how you design new applications.

This topic discusses the following subjects:

  • Why CAS policy levels are no longer used, and how this affects typical scenarios such as enabling partial-trust ASP.NET applications to run from UNC shares.

  • How to customize CAS behavior in ASP.NET 4.

  • Compatibility issues when you enable trusted code to work with CAS in ASP.NET 4.

This topic assumes that you understand .NET Framework CAS and ASP.NET trust levels. For information about these topics, see the following documents:

Overview of the ASP.NET 4 Code Access Security Model

In ASP.NET 4, there have been several fundamental changes to CAS from ASP.NET 3.5 and earlier versions, including the following:

  • By default, ASP.NET 4 partial-trust application domains are homogeneous. This results in a constrained set of possible permission sets for code that is running in a partial-trust application domain. It also means that the application domain trust boundary is itself associated with a partial-trust grant set. In homogeneous application domains, security policy does not intersect with machine-level, user-level, or enterprise-level CAS policies.

    Note

    The new homogeneous application domain model requires a slightly different set of declarative ASP.NET trust policy files than the policy files used in earlier versions of ASP.NET. As a result, after you install ASP.NET 4, the computer contains two sets of ASP.NET partial-trust policy files. The new CAS model uses one set, and the other set is used when applications are configured to use the pre-ASP.NET 4 CAS model.

  • In earlier versions of ASP.NET, the AspNetHostingPermission attribute was used on almost all public ASP.NET-related classes in order to prevent ASP.NET types from being used in non-Web partial-trust environments. For example, the presence of the AspNetHostingPermission attribute prevented the use of most ASP.NET classes in a partial-trust ClickOnce application. (For information about CAS in ClickOnce applications, see Code Access Security for ClickOnce Applications.) Instead of relying on the AspNetHostingPermission attribute, ASP.NET 4 uses a different CAS technology that is referred to as conditional APTCA (based on the AllowPartiallyTrustedCallersAttribute type) to accomplish the same result. As a result, the AspNetHostingPermission attribute has been removed from most ASP.NET types and members.

  • In ASP.NET 4, many of the system assemblies have been updated to use the CLR security transparency model. The transparency model in ASP.NET 4 is very similar to the transparency model that is used in Silverlight. For more information about code transparency, see Security-Transparent Code.

You will see the effects of these changes if you relied on customized CLR CAS policies that were created by using tools like caspol.exe. The changes will also affect partial-trust Web applications that rely on assemblies that are deployed in the GAC in order to carry out privileged operations, when only ASP.NET or .NET Framework code was active on a call stack. The following sections discuss the CAS changes.

Homogeneous Application Domains

This section describes homogeneous application domains. It discusses the behavior changes since ASP.NET 2.0 that result for homogeneous application domains. It also discusses compatibility options that you can set and code changes that you can make in order to run code in homogeneous application domains.

Reduced Number of Possible Permission Sets

Homogeneous application domains are partial-trust application domains that define a shared permission set for running code. In a homogeneous application domain hosted in ASP.NET 4, code that can be loaded is associated with one of two permission sets. Code either runs with full trust (GAC code always runs in full trust), or code runs with the partial-trust permission set that is defined by the current trustLevel setting. (For more information, see trustLevel Element for securityPolicy (ASP.NET Settings Schema).)

Note

ASP.NET 4 application domains default to full trust. The homogeneous behavior in ASP.NET 4 takes effect only after the trustLevel element's name attribute is set to a value other than Full.

This behavior is different from partial-trust applications in earlier versions of ASP.NET. In earlier versions, you could create multiple permission sets that each had different grant sets and different membership conditions. Homogeneous application domains were introduced in the .NET Framework 4 because of the difficulty of handling mixed permission scenarios like these. It was difficult to create multiple permission sets that each had different levels of permissions, and then to prove that the different permissions levels were actually being enforced, after taking into account all the conditions under which code could run. For example, code could run under reflection, full-trust code could run other full-trust code on behalf of a partial-trust caller, and so on. The permission set had to account for all such conditions. Homogeneous application domains vastly simplify CAS decisions by reducing the possible outcomes. Code either has full trust or it has a single, well-defined partial-trust permissions set. For ASP.NET, the well-defined partial-trust permission set is what applies to a specified ASP.NET trust level.

There is a third possible state for code that attempts to load in a homogeneous application domain. (This is not considered a separate permission set by the CLR.) The third permission set is the empty permission set, which is defined as the Nothing permission set in all ASP.NET partial-trust configuration files. All code that evaluates to the Nothing permission set is considered unloadable. As a result, any attempt to load assemblies that have the Nothing permission set into a homogeneous application domain results in a SecurityException exception.

Only ASP.NET Partial-Trust Policy Applies

The partial-trust permission set of a homogeneous application domain is established only by the host that is responsible for creating the application domain. In ASP.NET 4, this means that the partial-trust permission set is defined only by the contents of a partial-trust configuration file found in the CONFIG subdirectory of the .NET Framework installation. By default, ASP.NET policy information for ASP.NET 4 partial-trust application domains no longer overlaps with enterprise, machine, or user CAS policy settings.

Global CAS policy information (the policy that developers previously managed using tools like caspol.exe or the Mscorcfg.msc MMC configuration tool) no longer has any influence on ASP.NET 4 homogeneous application domains. (You can configure ASP.NET to use the earlier CAS model, in which ASP.NET settings intersect with enterprise, machine, and user policy. This legacy behavior is explained in a later section.)

The most obvious change is for UNC-hosted partial-trust ASP.NET 4 applications. In previous releases, you had to use caspol.exe to elevate UNC shares to full trust in order to allow ASP.NET partial-trust policies to take effect. This was because in earlier versions of ASP.NET, the default machine-level CLR CAS policy took effect first. As a result, UNC-hosted applications had a constrained permission set that was associated with the Intranet zone. Because ASP.NET 4 partial-trust application domains establish policy only from ASP.NET policy files, the physical location of a Web application no longer has any influence on the permission set that is associated with a partial-trust application.

A side effect of this change is illustrated by the scenario where an administrator wants to lock a Web server down to deny execute permissions to all managed code by default, and then to grant execute rights for selected ASP.NET applications. In previous versions of ASP.NET, this required a little-known workaround as documented in the KB article FIX: Error message when you try to run an ASP.NET 2.0 Web application if you do not associate the My_Computer_Zone code group with the "full trust" permission set: "Server Application Unavailable. In ASP.NET 4, an administrator can lock down a Web server to deny or grand execute permissions by following these steps:

  1. Create a custom ASP.NET trust level whose policy file maps all code to the Nothing permission set (an empty permission set), and then configure all ASP.NET applications to use that trust level by default. (This is done in the root Web.config file.)

  2. Selectively associate individual ASP.NET applications with either built-in or custom trust levels that grant execute permissions (and whatever other permission are needed) to managed code. For machine-wide enforcement, you can selectively assign trust levels using location elements in the root Web.config file.

Location and Naming Conventions for Trust Policy Files

The location and naming convention of CAS policy files is the same as in previous versions of ASP.NET. The default trust levels are Full, High, Medium, Low, and Minimal. The policy files that define the partial-trust permission sets for High through Minimal are all located in the CONFIG subdirectory of the .NET Framework installation directory.

The policy files are named using the following pattern:

web_[trustlevelname]trust.config

For example, the partial-trust permission set for Medium trust is in the file named web_mediumtrust.config.

Changes in Trust Policy Files for ASP.NET 4

For the most part, the information in ASP.NET 4 CAS policy files is the same as the information found in policy files in previous releases. However, there were minor additions for both .NET Framework 3.5 and .NET Framework 4 functionality. The name of the partial-trust permission set that is associated with a homogenous application domain is ASP.Net. In addition, by default, all code that is located in either a Web application directory structure or in the code-generation directory structure is granted permissions from the named ASP.Net permission set.

There are two changes from previous versions of the partial-trust policy files:

  • At the end of each ASP.NET 4 CAS policy file, there is no longer a CodeGroup element that maps full trust to a Microsoft signing key and to the ECMA signing key. These entries were removed in ASP.NET 4 because the entries were a legacy from earlier versions when the GAC was not always implicitly assumed to have full trust.

  • The Assertion part of the SecurityPermission attribute has been removed from all ASP.NET 4 CAS policy files. A fundamental change made by the CLR in the .NET Framework 4 is that partial-trust code cannot assert permissions. This means partial-trust code will fail even if it attempts to assert permissions that it already has.

Scope of Partial Trust

Homogeneous application domains mean that ASP.NET 4 application-domain boundaries are partially trusted. When an application runs in partial trust, security demands result in a stack walk. All code on the stack is evaluated against the demanded permissions. In application domains for ASP.NET 3.5 and earlier, it was common for code paths to result in a stack walk all the way up to the application-domain boundary. Because application-domain boundaries in earlier versions of ASP.NET 4 were implicitly full trust, the stack walks for some code paths would succeed. In ASP.NET 4 homogeneous application domains, any stack walks that reach the application-domain boundary will evaluate against the partial-trust permission set that is currently in effect for the application domain.

The fact that the application-domain boundary is now itself partially trusted is the most common CAS change that typically requires you to change full-trust code in order to make it work in ASP.NET 4. For example, the ASP.NET development team had to add targeted security asserts on numerous internal code paths in order to suppress security demands and prevent them from bubbling up to the application-domain boundary. If the development team had not done that, basic tasks like page compilation would have failed because the security demands for such operations (for example, file I/O permissions for compilation) would fail when compared to the partial-trust permission set for trust levels like Medium.

There are extensibility points within ASP.NET that can lead to fully trusted code being loaded and run when only ASP.NET code is on the stack. In these scenarios, only ASP.NET code is initially on the stack when ASP.NET calls into a custom type that implements some kind of extensibility point. If the custom type is fully trusted (which should occur only if the type is deployed in the GAC), the result is that the entire call stack consists of fully trusted code. In a homogeneous application domain, if any code in a fully trusted extensibility type triggers a security demand, that demand will eventually reach the application-domain boundary. It will then fail when the security check occurs against the partial-trust permission set.

The following is a list of some ASP.NET extensibility points where this situation is possible:

  • Custom HTTP handler. A custom handler is invoked during the handler execution phase of the pipeline.

  • Custom HTTP module. A custom HTTP module is invoked during whatever pipeline events the module was registered for.

  • Custom build providers and expression builders. These types will be invoked by ASP.NET when it parses and compiles executable content such as .aspx files.

  • Role manager provider. A custom provider can be invoked during the AuthorizeRequest event in the pipeline.

  • Profile provider. A custom provider can be invoked to save profile data automatically during the EndRequest event.

  • Health monitoring provider. A custom provider can be invoked at arbitrary times to store accrued health-monitoring data.

A simple example of a custom HTTP handler illustrates the change in CAS behavior. In the following example, the handler code attempts to read a text file that is located in the root of the C:\ drive.

Public Class CustomHandler 
    Implements IHttpHandler 
    Public Sub ProcessRequest(ByVal context As HttpContext) 
        Dim data As String = File.ReadAllText("c:\testfile.txt") 
        context.Response.Write(data) 
    End Sub 
    
    Public Sub New() 
    End Sub 
    
    Public ReadOnly Property IsReusable() As Boolean 
        Get 
            Return False 
        End Get 
    End Property 
End Class

public class CustomHandler : IHttpHandler

{

public void ProcessRequest(HttpContext context)

{

string data = File.ReadAllText("c:\\testfile.txt");

context.Response.Write(data);

}

public CustomHandler() { }

public bool IsReusable { get { return false; } }

}

If the handler is signed, is marked with the AllowPartiallyTrustedCallersAttribute attribute, and is deployed in the GAC, the code will succeed when the handler is used in a Medium trust application in ASP.NET 3.5 or earlier. Medium trust is chosen for this example because in Medium trust, the partial-trust permission set allows read/write file I/O only to the application's directory structure. Fully trusted code such as the example handler is able to access other file locations in ASP.NET 3.5 and earlier. This is because when the handler runs, only fully trusted code is on the stack, and the application-domain boundary itself is fully trusted. As a result, the file I/O demand from the ReadAllText call is implicitly satisfied by the application-domain boundary being at full trust.

However, if the same handler code is used in a Medium-trust ASP.NET 4 application, it will fail, because the call to ReadAllText results in a file I/O demand for read access to the text file. The file I/O demand will result in a stack walk that ultimately reaches the application-domain boundary. In ASP.NET 4, the application-domain boundary is associated with the Medium trust permission set, and that permission set does not grant access to the root of the C:\ drive. Therefore, the file I/O demand will fail.

For ASP.NET 4, you must suppress the stack walk. To do this, you use the SecurityAction.Assert attribute of the FileIOPermission attribute on the ProcessRequest method. The following example shows how to use a FileIOPermission attribute for this purpose.

[Visual Basic]

Public Class CustomHandler 
    Implements IHttpHandler 

    <FileIOPermission(SecurityAction.Assert, Read = "c:\testfile.txt")> _ 
    Public Sub ProcessRequest(ByVal context As HttpContext) 
        Dim data As String = File.ReadAllText("c:\testfile.txt") 
        context.Response.Write(data) 
    End Sub 
    
    Public Sub New() 
    End Sub 
    Public ReadOnly Property IsReusable() As Boolean 
        Get 
            Return False 
        End Get 
    End Property 
End Class

[C#]

public class CustomHandler : IHttpHandler

{

[FileIOPermission(SecurityAction.Assert, Read = "c:\\testfile.txt")]

public void ProcessRequest(HttpContext context)

{

string data = File.ReadAllText("c:\\testfile.txt");

context.Response.Write(data);

}

public CustomHandler() { }

public bool IsReusable { get { return false; } }

}

You can use either declarative asserts (as shown in the example) or programmatic asserts. It is a best practice to declaratively assert the narrowest permissions that are required in order to get a block of code working. Although it might seem like a simple solution to add unrestricted security asserts everywhere, that approach should not be used. The security failures caused by the new homogeneous application domain behavior where designed to require you to analyze full-trust code, and to understand which privileged operations the full-trust code requires. You can then selectively assert the narrowest set of permissions that are required in order to re-enable full-trust code.

Configuring ASP.NET 4 Applications to Use the ASP.NET 2.0 CAS Model

It is possible to configure ASP.NET 4 applications to use ASP.NET 1.0 and ASP.NET 2.0 CAS behaviors. In ASP.NET 4, the trust element provides a new legacyCasModel attribute, which is set to false by default. Setting this attribute to true configures an ASP.NET 4 application to use most (although not all) the ASP.NET CAS behavior from earlier versions.

When the LegacyCasModel attribute is set to true, the following behaviors occur:

  • Partial-trust application domain boundaries use full trust. This means that scenarios where full-trust code executes with only full-trust code on the stack, you do not have to use asserts in order to suppress security demands.

  • Settings for enterprise, machine, and user CAS policies that are defined for the .NET Framework 4 intersect with ASP.NET CAS policy. This means that any custom permissions that are created by using the .NET Framework 4 caspol.exe or Mscorcfg.msc take effect.

    Note

    Because the base security policy files and the caspol.exe tool for the .NET Framework 4 are in a different directory than those for the .NET Framework 2.0, any custom security policy that was created for the .NET Framework 2.0 must be re-created for the .NET Framework 4 by using the .NET Framework 4 version of caspol.exe.

  • You can specify multiple custom permission sets that apply to different assemblies.

The following CAS-related behaviors do not change even in legacy CAS mode:

  • ASP. NET 4 assemblies are still marked as conditional APTCA. (Conditional APTCA is described later in this topic.) Conditional APTCA cannot be reverted to legacy mode because it would involve removing the AspNetHostingPermission attribute from most public ASP.NET 4 APIs. There is no efficient way to have that permission apply to ASP.NET public APIs when they are running in legacy CAS mode, but not apply when the assemblies run in the new CAS model.

  • Partial-trust code is no longer allowed to assert any permission. Code that was previously granted partial trust could call the Assert method and successfully assert any permission that partial-trust code was already granted. In the .NET Framework 4, regardless of the CAS model that is in effect for an ASP.NET application, partial-trust code is no longer allowed to perform security asserts.

In order to distinguish the permission sets that apply in the legacy CAS model from the single permission set that applies in the new CAS model, when the LegacyCasModel attribute of the trust element is set to true, ASP.NET 4 reads CAS policy from a different set of partial-trust configuration files. For every trust policy file that exists for the ASP.NET built-in trust levels, two versions of the file exist. ASP.NET reads one version for the new CAS model, and it reads the other version for the legacy CAS model. For example, for Medium trust, when ASP.NET 4 runs in legacy mode, it reads the policy file that is named legacy.web_mediumtrust.config. Notice that the beginning of the file name is "legacy". ASP.NET 4 uses the same naming convention for all CAS policy files as for the built-in ASP.NET trust levels. The primary difference between the legacy and non-legacy CAS policy files is that the legacy files include the CodeGroup definition that references the Microsoft signing key and ECMA signing key.

Because you can configure an application to use the old CAS model, for existing applications, you might want to set the LegacyCasModel option to true and therefore avoid making any changes. However, it is important to understand that the legacy option exists primarily to ease the transition of existing applications to the ASP.NET 4 CAS model. In the future, both the CLR and ASP.NET teams will focus on designing and coding with the new CAS model.

Silverlight 2 was the first feature area of the .NET Framework that moved to the new model. The goal for the .NET Framework is to move all desktop and server partial-trust scenarios to run on the new CAS model. As a result, we recommend that you invest in the effort to re-enable applications to work in the CAS model. Similarly, administrators who previously relied on caspol.exe and Mscorcfg.msc should rely instead on customizing the ASP.NET partial-trust policy files and permission assignments.

Customizing Permission Set Assignment in the ASP.NET 4 CAS Model

Although ASP.NET 4 homogeneous application domains constrain code to either full trust or to the named ASP.NET partial-trust permission set, developers and administrators can influence the process by which a permission set is associated with an assembly. The following approaches let you customize the process of associating a permission set with a piece of running code:

  • You can customize the partial-trust policy file for an individual trust level. (This approach was possible in earlier versions of ASP.NET.)

  • You can statically configure ASP.NET 4 full-trust assemblies.

  • You can use the ASP.NET 4 HostSecurityPolicyResolver type to access the functionality of the CLR HostSecurityManager class in a constrained manner.

The first two of these approaches let you make customizations declaratively, and the third option lets you make customizations in code.

Customizing Policy Files for a Trust Level

The first approach to modifying ASP.NET partial-trust policy files is the same as in earlier versions of ASP.NET: you can modify the set of permissions in the ASP.NET named permission set. You can also add more CodeGroup definitions that have custom membership conditions. As noted earlier, the new CAS customizations must be made in partial-trust policy files like web_mediumtrust.config. The files that start with "legacy" in their files names are parsed and used when the trust element LegacyCasModel attribute is set to true.

For ASP.NET 4, all custom CodeGroup definitions must map to one of three possible permission sets: FullTrust, ASP.Net (that is, the partial-trust permission set), or Nothing. Because ASP.NET 4 partial-trust application domains are homogeneous by default, the custom policy entries must evaluate to a constrained set of permission sets. Although it might appear that you can define different named permission sets when you use the ASP.NET 4 CAS model, any code that evaluates to a permission set other than FullTrust, ASP.Net, or Nothing will result in a run-time SecurityException exception. This indicates that the CLR did not recognize the evaluated permission set.

The FullTrust permission set indicates that code runs at full trust. The ASP.Net permission set is the named partial-trust permission set that is typically used for partial-trust application domains. As described earlier, Nothing is not an actual permission set recognized by the CLR; instead, it is the empty permission set. If the CLR determines that an assembly is associated with an empty permission set, the CLR throws a SecurityException exception, and the CLR will not load the assembly.

In addition, ASP.NET 4 allows you to change the name of the ASP.Net permission set using the PermissionSetName attribute of the trust element. You can set a different name for the PermissionSetName attribute. At run time, ASP.NET 4 will search the partial-trust policy file for a PermissionSet element of the same name. That named permission set will then be used as the partial-trust permission set for a homogeneous application domain. It is unlikely that you would have to do this. However, the ability to change the name of the partial-trust permission set to something other than ASP.Net was added to accommodate hosting environments like SharePoint that define their own named permission set as an entity distinct from the ASP.NET default permission set. (Remember that in the new CAS model, it is no longer possible to have multiple named permission sets that define partial-trust permissions.) Although you can change the name of the partial-trust permission set to something other than ASP.Net, there can still be only one partial-trust permission set in effect for an application.

Specifying Assemblies That Will Be Granted Full Trust

The second declarative policy customization is new in ASP.NET 4 and enables you to explicitly establish a list of assembly identities that will always be granted full trust. The securityPolicy configuration contains a new child fullTrustAssemblies configuration section. The FullTrustAssembliesSection section is a standard collection that supports add, remove, and clear operations, and where you can specify one or more assembly identities that will be granted full trust at run time. The following example shows a fullTrustAssemblies configuration section.

<system.web>

<securityPolicy>

<fullTrustAssemblies>

<add assemblyName="MyCustomAssembly"

version="1.0.0.0"

publicKey="a 320 hex character representation

of the public key blob used with a

signed assembly"

/>

</fullTrustAssemblies>

</securityPolicy>

</system.web>

Each entry in the fullTrustAssemblies element identifies an assembly by its assembly name and assembly version, and by a 320-character string that is the hexadecimal character representation of the public half of the signing key.

Note

In the future, new .NET Framework assemblies might use 2048-bit signing keys. If new assemblies are released that use 2048-bit signing keys, that will result in a 576-character long hexadecimal strings.

No assembly location is specified in the definition. It is up to the individual hosting environment (such as ASP.NET 4) to find and load assemblies. If a loaded assembly matches the information that is contained in one of the add elements in fullTrustAssemblies, the assembly is granted full trust.

You should use fullTrustAssemblies for assemblies that are not deployed in the GAC, but that are intended to always run in full trust. Because assemblies listed in fullTrustAssemblies can be customized at any point in the configuration hierarchy (from the root Web.config file to individual application-level Web.config files), using this setting is easier and more flexible for granting full trust than using a membership condition and code group in a partial-trust policy file. You can customize the fullTrustAssemblies lists for individual applications by specifying different information for different applications. This can be done either in application-level Web.config files, or in the root Web.config file by using location elements.

The set of full-trust assemblies is established immediately at the time a partial-trust application domain is created. As a result, if a partial-trust policy file includes information that results in a different grant set for an assembly that is listed in the fullTrustAssemblies element, the information is ignored and the assembly is granted full trust.

Customizing Permissions Programmatically

You can also programmatically change the association of a permission set to an assembly by creating a custom implementation of the ASP.NET 4 HostSecurityPolicyResolver type. At run time, ASP.NET 4 uses its own implementation of the CLR HostSecurityManager type. A HostSecurityManager object is called by the CLR every time an assembly is loaded. One of the functions of the HostSecurityManager property is to return a PermissionSet object that should be associated with a specified assembly and for a set of evidence. ASP.NET 4 lets you customize this process by calling a custom HostSecurityPolicyResolver object every time the CLR asks ASP.NET 4 for a permission-set decision.

You can configure a custom HostSecurityPolicyResolver object using the HostSecurityPolicyResolverType attribute of the trust element. If ASP.NET 4 determines that a custom HostSecurityPolicyResolver object is configured for an application, it calls the ResolvePolicy method of the custom resolver each time the CLR asks for a permission-set decision. However, unlike a HostSecurityManager object, a HostSecurityPolicyResolver object can return only a constrained set of possible decisions to ASP.NET 4. The ResolvePolicy method return value must be one of the following values from the HostSecurityPolicyResults enumeration:

  • DefaultPolicy. This specifies that ASP.NET 4 should use its own logic for determining the appropriate permission set for the assembly. You should return DefaultPolicy for assemblies where you do not want the HostSecurityPolicyResolver object to make a decision about the permission set. Returning DefaultPolicy causes ASP.NET to determine an assembly's permission grant set based on the declarative code groups and membership conditions that are defined in the partial-trust policy file for the current ASP.NET trust level.

  • FullTrust. The assembly should be granted full trust.

  • AppDomainTrust. The assembly should be granted the partial-trust permission set that is associated with the application domain. Usually this means that the assembly will be granted the permissions that are defined in the named ASP.Net permission set.

  • None. The permission set for the assembly will be set to the Nothing permissions set, which is an

Because the base HostSecurityPolicyResolver class has an inheritance demand for unrestricted security permission, and a custom HostSecurityPolicyResolver object has to be loadable without requiring another HostSecurityPolicyResolver object to establish full trust, concrete implementations of a HostSecurityPolicyResolver class should always be signed and deployed in the GAC.

The following example shows a custom HostSecurityPolicyResolver object that grants full trust to all assemblies that are loaded from a specific directory. This might be a scenario for organizations that add compiled assemblies in a specific location on disk (not the GAC) and that want all files from that location to automatically run with full trust. In order for ASP.NET applications to be able to load assemblies from outside the directory structure of a Web application, you must add explicit assembly-binding redirects that associate assembly identities with different physical disk locations.

[Visual Basic]

Public Class MyCustomResolver 
    Inherits HostSecurityPolicyResolver 
    Public Overloads Overrides Function ResolvePolicy(ByVal evidence _
            As Evidence) As HostSecurityPolicyResults 
        Dim urlEvidence As Url = evidence.GetHostEvidence(Of Url)() 
        
        If (urlEvidence IsNot Nothing) AndAlso _
            (urlEvidence.Value.StartsWith("file:///C:/FullTrustExample_HSPR.dll")) Then 
            Return HostSecurityPolicyResults.FullTrust 
        End If 
        
        ' Specify that ASP.NET should perform its own logic.
        Return HostSecurityPolicyResults.DefaultPolicy 
    End Function 
End Class
public class MyCustomResolver : HostSecurityPolicyResolver
{ 
  public override HostSecurityPolicyResults 
                              ResolvePolicy(Evidence evidence)
  {
            Url urlEvidence = evidence.GetHostEvidence<Url>();
            if ( (urlEvidence != null)  && 
                 (urlEvidence.Value.StartsWith("file:///C:/FullTrustExample_HSPR.dll"))
               )
                return HostSecurityPolicyResults.FullTrust;

            // Specify that ASP.NET should perform its own logic.
            return HostSecurityPolicyResults.DefaultPolicy;
  }
}

Conditional APTCA

In versions of the .NET Framework earlier than version 4, many fully trusted assemblies (including ASP.NET assemblies) were marked with the AllowPartiallyTrustedCallersAttribute (APTCA) attribute. This attribute gives partial-trust callers access to public types and members that are defined in assemblies that are marked in this way. In the .NET Framework 4, the CLR includes a variation of APTCA that is referred to as conditional APTCA. (The shorthand notation for conditional APTCA is C-APTCA.) Conditional APTCA enables assemblies that are marked with the APTCA attribute to retain APTCA characteristics in only certain hosted environments. As a result, conditional APTCA makes it easier for ASP.NET 4 to control in exactly which partial-trust host environments partial-trust callers will be able to successfully call public ASP.NET 4 APIs.

How Conditional APTCA Works

Both partial-trust host environments and fully trusted assemblies play a part in making conditional APTCA work. Partial-trust host environments where permissions sets are important can provide to the CLR a list of assemblies that should always have their APTCA setting honored. Fully trusted assemblies that should have their APTCA characteristics enabled only in certain host environments indicate this by using the following variation of the assembly level APTCA attribute:

[assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel=NotVisibleByDefault)]

At run time, when the CLR is asked to load an assembly that is marked as conditionally APTCA, the CLR will check the list of valid conditional-APTCA assemblies supplied by the host environment. If the assembly is in the list, the CLR will treat all publicly exposed code in the assembly as if the assembly had been marked with the APTCA attribute in earlier versions of the .NET Framework.

If a conditional APTCA assembly is not on the host environment's list of assemblies that should be treated as APTCA, the assembly still is loaded, but without its APTCA characteristics. The actual availability of public APIs to partial-trust user code in such an assembly will vary depending on whether the assembly is 100% security transparent or not. That is, it depends on whether the assembly is marked with an assembly-level SecurityTransparentAttribute attribute. (Security transparency in ASP.NET 4 is described in a later section of this topic.)

In summary, public APIs in ASP.NET 4 can behave in one of the following ways:

  • For most ASP.NET assemblies, all the public APIs become unavailable to partial-trust callers. In effect, this prevents the majority of public APIs in ASP.NET 4 from being used in any partial-trust environments other than Web applications.

  • A handful of ASP.NET assemblies that are marked as 100% security transparent are still callable from partial-trust callers. However, if code paths in those assemblies eventually reach into the rest of the ASP.NET codebase, the calls fail. The result is the same behavior as in previous versions of ASP.NET releases, with the minor difference that calls to APIs in ASP.NET 4 assemblies will progress further before failing.

    Note the following about assemblies that are marked security transparent:

    • There are only two ASP.NET assemblies that are marked security transparent at the assembly level: System.Web.DynamicData.dll and System.Web.RegularExpressions.dll.

    • System.Web.Routing.dll is not considered 100% security transparent in ASP.NET 4 because all the types that were defined in that assembly in earlier versions of ASP.NET were moved into System.Web.dll. In effect, in ASP.NET 4, System.Web.Routing.dll is a metadata-only assembly.

In ASP.NET 4, the conditional APTCA attribute variation is found on the following assemblies:

  • System.Web.dll

  • System.Web.Extensions.dll

  • System.Web.DynamicData.dll

  • System.Web.DataVisualization.dll

  • System.ComponentModel.DataAnnotations.dll

  • System.Web.ApplicationServices.dll. This assembly is new in ASP.NET 4.

  • System.Web.Abstractions.dll. Types in this assembly were moved to System.Web.dll for ASP.NET 4.

  • System.Web.Routing.dll. Types in this assembly were moved to System.Web.dll for ASP.NET 4.

Conditional APTCA versus ASP.NET Hosting Permission Attributes

Conditional APTCA has made it practical to remove the AspNetHostingPermission attribute from 99% of the public APIs in ASP.NET 4. The AspNetHostingPermission attribute is still used in some places in ASP.NET 4, but only in those places where the permission's intent really makes sense. Everywhere else, the two following uses of AspNetHostingPermission attribute have disappeared:

<AspNetHostingPermission(SecurityAction.LinkDemand,
                         Level=AspNetHostingPermissionLevel.Minimal)>
<AspNetHostingPermission(SecurityAction.InheritanceDemand,
                         Level=AspNetHostingPermissionLevel.Minimal)>
[AspNetHostingPermission(SecurityAction.LinkDemand,
                         Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand,
                         Level=AspNetHostingPermissionLevel.Minimal)]

These permission definitions were used in earlier versions of ASP.NET to prevent ASP.NET assemblies from being loaded in non-Web partial-trust environments. The biggest environment of concern was partially trusted managed controls and managed applications that were being loaded into browsers like Microsoft Internet Explorer and Mozilla Firefox. By using conditional APTCA, effectively the same protections are enforced because ClickOnce applications and browser-based managed controls do not define any conditional-APTCA assemblies for treatment as full APTCA.

Customizing the ASP.NET 4 Conditional APTCA List

As mentioned earlier, individual host environments can supply to the CLR a list of conditional-APTCA assemblies whose APTCA characteristics should be honored. ASP.NET 4 supplies a hard-coded list to the CLR that contains all the ASP.NET 4 assemblies. If ASP.NET 4 did not do this, Web applications would fail immediately when the first line of ASP.NET internal code tried to run in a partial-trust application domain.

In the .NET Framework 4, conditional APTCA is a new CAS concept that has not yet been implemented in other parts of the .NET Framework. Therefore, it is likely that future versions of the .NET Framework will include more conditional-APTCA assemblies. In addition, as you become aware of conditional APTCA and use it for your own full-trust assemblies, the set of conditional-APTCA assemblies will grow. Because it is impossible for ASP.NET 4 to know in advance all possible conditional-APTCA assemblies, ASP.NET 4 includes a configuration section where conditional-APTCA assemblies can be added.

The existing securityPolicy section has a child configuration section named partialTrustVisibleAssemblies. This is a standard collection that supports add, remove, and clear operations, and where you can specify one or more assembly identities that should be treated as APTCA (if they are also marked for conditional APTCA). The following example shows a partialTrustVisibleAssemblies section.

<system.web>
  <securityPolicy>
    <partialTrustVisibleAssemblies>

      <add assemblyName="MyCustomAssembly"
           publicKey="a 320 hex character representation 
                      of the public key blob used with a 
                      signed assembly"
      />

    </partialTrustVisibleAssemblies>
  </securityPolicy>
</system.web>

Each entry in the partialTrustVisibleAssemblies section identifies an assembly by assembly name. Each entry is also identified by a 320-character string (for example, 0x03FA4D...) that is the hexadecimal character representation of the public half of the signing key that is used on the conditional-APTCA attributed assembly. You do not have to specify a version attribute. Only the assembly name and public key token are required by the CLR.

An important performance implication when you enable a conditional APTCA assembly is that you should also enable the transitive closure of conditional-APTCA assemblies. For example, if conditional-ATPCA assembly A depends on APTCA assembly B, and APTCA assembly B in turn depends on conditional-ATPCA assembly C, when you enable conditional ATPCA for A you should also enable conditional APTCA for C. Otherwise, the performance of your application can be affected. For example, code sharing and NGen images will be disabled if the full conditional-ATPCA closure is not enabled.

How Conditional APTCA Affects Non-Web Partial-Trust Applications

In versions of ASP.NET earlier than ASP.NET 4, some types and namespaces were not marked with the AspNetHostingPermission attribute. This allowed these types to be called from non-ASP.NET partial-trust environments such as ClickOnce applications. The types and namespaces that could be called in this way were the following:

The System.Web.ClientServices types are not usable in .NET Framework 4 partial-trust environments such as ClickOnce. Because the containing assembly (System.Web.Extensions.dll) is an ASP.NET 4 assembly that is marked for conditional APTCA, and because ClickOnce does not allow APTCA for any conditional-APTCA assemblies, none of the client services types are callable from partial-trust ClickOnce applications.

There are several reasons for this behavior. First, the .NET Framework 4 has been split into a client and into an extended SKU, and the assumption is that many ClickOnce applications will target the client SKU. It would require a substantial effort to refactor ASP.NET client services types into the client SKU.

Second, it is complex to determine how to refactor the client types while maintaining the required conditional-APTCA boundaries. Therefore, in the .NET Framework 4, the client services types are available only to non-ASP.NET full-trust environments, including ClickOnce applications that are configured to run in full trust using the extended .NET Framework 4 SKU.

For the HttpUtility type, the effect of conditional APTCA depends on which methods you were using, as shown in the following scenarios:

  • Partial-trust code was calling either the HtmlEncode or HtmlDecode method of the WebUtility class. The WebUtility type contains the ASP.NET HTML encoding and decoding implementations, but these have been refactored and moved into the System.Net namespace and that is contained in System.dll. Because System.dll is available in all partial-trust host environments, there is no issue with the methods of the WebUtility type accessing non-ASP.NET partial-trust applications.

  • Partial-trust code was calling any of the other methods of the WebUtility class. In that case, the same issue as described earlier for the client services types applies. That is, WebUtility is available only to non-ASP.NET full-trust callers in the .NET Framework 4.

Security Transparency in ASP.NET 4

Security transparency lets you indicate to the CLR whether a block of code will ever carry out a security-sensitive operation. Transparent code can never assert permissions, satisfy a link demand, contain unverifiable code, call into native code, or call security-critical code. This is true regardless of whether transparent code is fully trusted (for example in the GAC) or partially trusted.

Security transparency is a powerful feature for .NET Framework features like ASP.NET. It allows ASP.NET to indicate to the CLR that portions of ASP.NET code will never assert permissions, and that code will never implement or carry out security sensitive operations such as PInvoke calls into native code. This enables .NET Framework code to substantially reduce the security exposure of a large number of public and internal APIs even though .NET Framework code is in the fully trusted GAC.

Security transparency can apply either to an entire assembly or to only a subset of the code in the assembly. Although marking an entire assembly for security transparency is the ideal case, some code in the .NET Framework code has a legitimate requirement to perform security-sensitive tasks. Assemblies that contain 100% security-transparent code are marked using an assembly-level SecurityTransparentAttribute attribute.

Assemblies that contain a mixture of transparent and non-transparent code do not have an assembly-level transparency attribute. Instead, individual classes in the assembly can be marked by using either the SecuritySafeCriticalAttributel attribute or the SecurityCriticalAttribute attribute.

The behavior of unattributed classes is complex. However, in simple terms, unattributed types in ASP.NET 4 assemblies that have adopted the new transparency model are considered security transparent. Types in ASP.NET 4 assemblies that are not attributed and have not adopted the new transparency model are considered security-safe-critical.

Security Transparency in Practice and Security RuleSets

Because so much of the ASP.NET 4 code base is in System.Web.dll, it was not practical to convert all ASP.NET 4 code to the new transparency model. Instead, ASP.NET 4 code can be partitioned into the following categories:

  • Code that did not adopt the new transparency model, which includes code in the following assemblies:

    • System.Web.dll

    • System.Web.ApplicationServices.dll

    • System.Web.Mobile.dll. The types in this assembly were marked as obsolete in ASP.NET 4. Even though the assembly still exists, the expectation is that you will stop using the types in this assembly over time.

  • Code that was uses the new transparency model, which includes code in the following assemblies:

    • System.Web.Extensions.dll

    • System.Web.DynamicData.dll (100% security transparent)

    • System.Web.RegularExpressions.dll (100% security transparent)

    • System.ComponentModel.DataAnnotations.dll

    • System.Web.DataVisualization.dll

  • Assemblies that are only metadata and whose types were moved into a different ASP.NET assembly, including the following assemblies:

    • System.Web.Abstractions.dll. The types that were in this assembly in earlier versions of ASP.NET were moved to System.Web.dll. As a result, Sytem.Web.Abstractions.dll is a metadata-only assembly in ASP.NET 4.

    • System.Web.Routing.dll. The types that were in this assembly in earlier versions of ASP.NET were moved to System.Web.dll. As a result, System.Web.Routing.dll is a metadata-only assembly in ASP.NET 4.

In the .NET Framework 4, the CLR introduced a new concept that is referred to as security rule set. There are two levels of SecurityRuleSet configurations, level one and level two. The SecurityRuleSet configuration for all types is specified by using the SecurityRulesAttribute assembly-level attribute. ASP.NET 4 assemblies that adopted the new transparency model are marked using the following assembly-level attribute:

System.Security.SecurityRules(RuleSet=System.Security.SecurityRuleSet.Level2)

ASP.NET 4 assemblies that use the transparency model from the .NET Framework 2.0 (which for ASP.NET effectively means no transparency, because ASP.NET prior to ASP.NET 4 never used transparency concepts) are marked using the following assembly-level attribute:

System.Security.SecurityRules(RuleSet=System.Security.SecurityRuleSet.Level1)

For ASP.NET assemblies that adopted the new transparency model (and the public types that are in the assemblies), the majority of code is considered security transparent. A small of amount of code in these assemblies performs security-sensitive operations, and that code is marked as either safe-critical or critical code.

For ASP.NET assemblies that did not adopt the new transparency model (leaving aside obsolete or type-redirected assemblies) all the public APIs can be called from partial-trust user code and can internally carry out security-sensitive operations. The combination of open access to partial-trust callers and the potential for security-sensitive operations means that older ASP.NET 4 code requires a greater degree of scrutiny. However, because most new ASP.NET features are implemented in newer assemblies like System.Web.Extensions.dll and System.Web.DynamicData.dll, or in separate releases like ASP.NET MVC, most new ASP.NET code is security transparent and therefore by default is safer than older code.

By default, the CLR treats the public APIs of all ASP.NET 4 assemblies that are marked as SecurityRuleSet.Level1 as safe-critical (that is, as equivalent to being marked with the SecuritySafeCriticalAttribute attribute) as long as the hosting environment honors the APTCA attribute. If APTCA is not honored, the CLR triggers a link demand for full trust, which will fail when there is any partial-trust user code on the stack. In other words, when APTCA is not honored for an ASP.NET assembly that is marked as SecurityRuleSet.Level1, you see the same behavior as in previous releases of the .NET Framework when partial-trust code tried to call a full-trust assembly that was not marked with the APTCA attribute.

By default, the CLR treats the public API of all ASP.NET 4 assemblies that are marked as SecurityRuleSet.Level2 as security transparent (that is, equivalent to being attributed with SecurityTransparentAttribute) as long as the hosting environment honors the APTCA attribute. Otherwise, the following behavior is defined:

  • If APTCA is not honored and an assembly that is marked as Level2 is not 100% security transparent, the CLR treats the public surface area as being security critical. As a result, any partial-trust callers that attempt to use the public surface area will likely fail with a MethodAccessException, TypeAccessException, or FieldAccessException exception.

  • If APTCA is not honored and an assembly that is marked as Level2 is 100% security transparent, partial-trust callers will be able to successfully call any of the public APIs in that assembly. In practice, this means that a SecurityException exception occurs later in the call path when the code in the 100% security-transparent assembly eventually calls into either a level-1 ASP.NET assembly or a level-2 ASP.NET assembly that is not 100% security transparent.

Transparency and ASP.NET Compilation

Output that is created by the ASP.NET compilation system is also affected by the ASP.NET 4 adoption of both the new CAS model and of the new security transparency model. This includes items like page assemblies, precompiled assemblies, and the compiled results of the App_Code directory. The compilation system varies its behavior based on the setting of the LegacyCasModel attribute of the trust element.

The following table describes how dynamically compiled objects are marked in both the legacy CAS model and in the newer CAS model.

legacyCasModel attribute setting

Web site trust level

Attributes applied to compiled assemblies

False (new CAS model)

Full trust

SecurityRules(SecurityRuleSet.Level2)

High trust or lower

SecurityRules(SecurityRuleSet.Level2)

SecurityRules(SecurityRuleSet.Level2)

True (old CAS model)

Full trust

SecurityRules(SecurityRuleSet.Level1)

High trust or lower

SecurityRules(SecurityRuleSet.Level1)

Because the ASP.NET 4 compilation system varies its behavior based on the settings of the LegacyCasModel attribute of the trust element, there can be restrictions on how you share compiled code across different partial-trust ASP.NET 4 applications. In general, you should not see any change in application behavior. However, in some scenarios, compiled artifacts that were created from a partial-trust application that has the LegacyCasModel attribute set to false (that is, that uses the new CAS model) do not work as expected when they are used with other applications. As a result, for some scenarios (for example, a shared library of compiled .ascx controls that are signed, ATPCA-attributed, and deployed in the GAC), you might have to explicitly apply the safe-critical and critical attributes to some of your code when the library is marked using Level2.

See Also

Other Resources

ASP.NET Application Security in Hosted Environments