Using Code Access Security in ASP.NET Applications

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

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

This topic describes fundamental changes in the .NET Framework 4 Beta 2 trust levels and CAS implementation as they apply to ASP.NET version 4. The following subjects are discussed:

  • 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 more 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.

  • By default ASP.NET 4 partial trust application domains are homogeneous. This results in a constrained set of possible permission sets for code 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. Lastly homogeneous application domains do not intersect security policy with machine-level, user-level or enterprise-level CAS policies.

    NoteNote

    The new homogeneous application domain model requires a slightly different set of declarative ASP.NET trust policy files. As a result after installing ASP.NET 4 there are two sets of ASP.NET partial trust policy files. One set is used by the CAS model and the other set is used when applications are configured to revert to the pre-ASP.NET 4 CAS model.

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

  • In previous releases the AspNetHostingPermission attribute was used on almost all public ASP.NET-related classes 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 on CAS in ClickOnce applications, see Code Access Security for ClickOnce Applications. ASP.NET 4 uses a different CAS technology called conditional APTCA (based on the AllowPartiallyTrustedCallersAttribute) to accomplish the same end result. As a result the AspNetHostingPermission attribute has been removed from most ASP.NET APIs.

The effects of these changes will be seen by developers who relied on customized CLR CAS policies using tools like caspol.exe. The changes will also affect partial trust web applications that rely on assemblies deployed in the GAC to carry out privileged operations, when only ASP.NET or .NET Framework code was active on a call stack. The following sections of this topic discuss the CAS changes listed above.

Homogeneous Application Domains

This section describes what homogeneous application domains are, the specific behavior changes that result for homogeneous application domains, and compatibility options and code changes that developers can follow to get code running again in homogeneous application domains.

Reduced Number of Possible Permission Sets

As the name implies, homogeneous application domains are partial trust application domains defining a CAS policy that results in common permission sets associated with running code. To be more precise, in an ASP.NET 4-hosted homogeneous application domain, all loaded code is associated with one of only two possible permission sets. Code either runs with full trust (GAC code always runs in full trust), or code runs with the partial trust permission set defined by the current trustLevel setting (see trustLevel Element for securityPolicy (ASP.NET Settings Schema)). Note that ASP.NET 4 application domains still default to full trust. The new homogeneous behavior only takes effect after the trustLevel element's name attribute is set to a value other than Full.

This behavior is different from pre-ASP.NET 4 partial trust applications where it was possible to create multiple permission sets with varying grant sets and varying membership conditions. The reason that the CLR introduced homogeneous application domains in the .NET Framework 4 Beta 2 is the difficulty of handling such scenarios in pre-ASP.NET 4 versions. It was difficult for developers to create multiple permission sets with varying levels of permissions, and then to prove that the various permissions levels were actually being enforced after taking into account all the different 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. Homogeneous application domains vastly simplify CAS decisions by reducing the possible outcomes. There is either a full trust, or a single well-defined partial trust permissions set. For ASP.NET, the well-defined partial trust permission set is what applies to a given ASP.NET trust level.

Although this is not considered a separate permission set by the CLR, there is a third possible state for code that attempts to load in a homogeneous application domain. This 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 use assemblies in the Nothing permission set now result in a CLR SecurityException, if an attempt is made to load such assemblies into a homogeneous application domain.

Only ASP.NET Partial Trust Policy Applies

The partial trust permission set of a homogeneous application domain is established solely by the host responsible for creating the application domain. In the case of ASP.NET 4 this means 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 4 partial trust application domains no longer intersect enterprise, machine, or user CAS policy settings with ASP.NET policy information.

Global CAS policy information that developers previously managed via tools like caspol.exe or the Mscorcfg.msc MMC configuration tool no longer has any influence on ASP.NET 4 homogeneous application domains. There is a way in ASP.NET to revert applications back to the pre-ASP.NET 4 CAS model, which intersects with enterprise, machine, and user policy. This legacy behavior is explained in a later section.

The most obvious example of this change is for UNC-hosted partial trust ASP.NET 4 applications. In previous releases it was necessary to use caspol.exe to elevate UNC content shares to full trust in order to allow ASP.NET partial trust policies to take effect. That occurred because in previous versions of ASP.NET the default machine-level CLR CAS policy took effect first, and as a result UNC hosted applications ended up with a constrained permission set associated with the Intranet zone. Because ASP.NET 4 partial trust application domains only establish policy from ASP.NET policy files , the physical location of a web application no longer has any influence on the permission set associated with a partial trust application.

A more subtle side effect of this change is for the scenario where an administrator wants to lock a web server down to deny all managed code execute rights by default, and then selectively grant execute rights for selected ASP.NET applications. In previous versions of ASP.NET this was either impossible or required a little known workaround as documented in http://support.microsoft.com/kb/943598. In ASP.NET 4, an administrator can accomplish this by following these steps:

  1. Use caspol.exe or Mscorcfg.msc to set a machine-wide policy that grants an empty permission set (without execute rights) to all managed code.

  2. Create a custom ASP.NET trust level whose policy file maps all code to the Nothing permission set (an empty permission set that also lacks execute rights) and configure all ASP.NET applications to use that trust level by default in the root Web.config file.

  3. Selectively associate individual ASP.NET applications to either built-in or custom trust levels that do grant execute permissions (and whatever other permission are needed) to managed code. For machine-wide enforcement the selective assignment of trust levels can be done via location elements in the root Web.config file.

The location and naming convention of ASP.NET 4's CAS policy files remains the same. The default trust levels are still 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 themselves are all named with a naming pattern such as: web_[trustlevelname]trust.config. For example the partial trust permission set for Medium trust is still found in the file named web_mediumtrust.config.

For the most part the contents of ASP.NET 4 CAS policy files mirror the information found in previous releases. However, there were minor additions for both .NET Framework 3.5 and .NET Framework 4 Beta 2 functionality. The name of the partial trust permission set associated with a homogenous application domain is still ASP.NET. And by default all code located in either a web application directory structure, or the code-gen directory structure, is granted the set of permissions from the named "ASP.Net" permission set.

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

  • At the bottom of each ASP.NET 4 CAS policy file there is no longer a CodeGroup mapping full trust to a Microsoft signing key and the ECMA signing key. These entries were removed in ASP.NET 4 since the entries were a legacy from the early days of CAS when the GAC was not always implicitly assumed to have full trust.

  • The Assertion part of 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 Beta 2 is that partial trust code can no longer assert permissions. This means partial trust code will fail even if it attempts to assert permissions that it already has.

Partial Trust Really Means Partial Trust

Another major side effect of homogeneous application domains is that ASP.NET 4 application domain boundaries are now partially trusted. When an application runs in partial trust, security demands result in a stack walk where all code on the stack is evaluated against the demanded permissions. In pre-ASP.NET 4 application domains, it was common for various code paths to result in a stack walk all the way up to the application domain boundary. Since pre-ASP.NET 4 application domain boundaries were implicitly full trust, the stack walks for certain 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 currently in effect for the application domain.

The fact that the application domain boundary is now itself partially trusted will likely be the most common CAS change that causes developers to tweak full trust code to get it running against in ASP.NET 4. For example, the ASP.NET development team had to add targeted security asserts on numerous internal code paths to suppress security demands and prevent them from bubbling up to the application domain boundary. If ASP.NET had not done that, basic tasks like page compilation would have failed because the security demands that arise from such operations (file I/O permissions in the compilation case) would fail if 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 with only ASP.NET code 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 (normally this means the type is deployed in the GAC), the end 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 and fail when the security check occurs against the partial trust permission set.

The following is a brief list of 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 while parsing and compiling executable content such as .aspx pages.

  • 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. The handler code below attempts to read a text file located in the root of the C:\ drive:

[C#]

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

}

Assuming that the handler is signed, APTCA-attributed, and deployed in the GAC, the code will succeed when the handler is used in a Medium trust ASP.NET 3.5 (or earlier) application. Medium trust is chosen for this example because in Medium trust the partial trust permission set only allows read/write file I/O to the application's directory structure. Fully trusted code like the example handler is still able to access other file locations in earlier versions of ASP.NET because at the time 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. Because 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, the file I/O demand will fail.

For ASP.NET 4, the handler code needs to have an explicit file I/O permission assert added to the ProcessRequest method to suppress the stack walk as highlighted in the following example:

[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; } }

}

Note that either declarative asserts (as shown in the example) or programmatic asserts can be used. It is a best practice to assert the narrowest permissions required to get a piece of code working again. Although at first glance just placing unrestricted security asserts everywhere might seem like a simple solution, that approach ultimately does not work. The security failures caused by the new homogeneous application domain behavior are an intentional design change intended to get developers to analyze full trust code, and to truly understand what privileged operations full trust code requires. Then a developer can judiciously assert the narrowest set of required permissions to re-enable full trust code.

Reverting to the ASP.NET 2.0 CAS Model

It is possible to switch ASP.NET 4 applications back to most of the previous ASP.NET 1.0 and 2.0 CAS behavior. In ASP.NET 4 the trust element has a new legacyCasModel attribute which is set to false by default. Setting it to true tells ASP.NET 4 to revert the application to most, though not quite all, of the previous ASP.NET CAS behavior.

When the legacyCasModel attribute is set to true the following occurs:

  • Partial trust application domain boundaries revert to being full trust again. This means for certain scenarios where full trust code executes with only full trust code on the stack, asserts are not needed to suppress security demands.

  • Enterprise, machine, and user CAS policies once again are intersected with ASP.NET CAS policy. This means any custom permissions created by using caspol.exe or Mscorcfg.msc take effect.

  • It is possible to specify multiple fine-grained permission sets that apply to different loaded assemblies.

The one CAS-related behavior that does not change is that ASP. NET 4 assemblies are still marked as conditional APTCA (conditional APTCA is described later in this topic). Conditional APTCA cannot be reverted because it involves removing the AspNetHostingPermission attribute from most ASP.NET 4 public APIs. There is no efficient way to have that permission appear all over ASP.NET public APIs when running in legacy CAS mode, but then have the permission disappear when running in the new CAS model.

To distinguish between the permission sets that apply in the legacy CAS model from the single permission set that applies in the new CAS model, ASP.NET 4 reads CAS policy from a different set of partial trust configuration files when the <trust> element legacyCasModel attribute is set to true. For every trust policy file that exists for the ASP.NET built-in trust levels, there are actually two versions of the file. One version is read when ASP.NET 4 uses the new CAS model, and the other version is used by ASP.NET 4 for the legacy CAS model. Use the Medium trust level as an example: when ASP.NET 4 runs in legacy mode it reads the policy file named legacy.web_mediumtrust.config. Note 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 only difference between the legacy and non-legacy CAS policy files is that the legacy files include the old <CodeGroup> definition that reference the Microsoft signing key and ECMA signing key.

Because it is possible to revert to the old CAS model, you may be tempted to set the legacyCasModel switch to true and avoid making any changes. However it is very important to understand that the legacy switch exists 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 initial flavor of the .NET Framework to move to the new model, and the goal is to move all desktop and server partial trust scenarios to run on the new CAS model. As a result, it is strongly recommended that developers invest the effort to re-enable their code to work in the CAS model. Similarly administrators who may have previously relied on caspol.exe and Mscorcfg.msc should move to relying on customizations to 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 the named ASP.NET partial trust permission set, developers and administrators still have flexibility to influence the process by which a permission set is associated with an assembly. There are two declarative approaches and one programmatic approach to customizing the process of associating a permission set with a piece of running code:

  • As with previous versions of ASP.NET, the partial trust policy file for a given trust level can be customized.

  • ASP.NET 4 enables static configuration of full trust assemblies.

  • ASP.NET 4 exposes the functionality of the CLR HostSecurityManager class in a constrained manner by using the ASP.NET 4 type HostSecurityPolicyResolver.

The first declarative customization approach of modifying ASP.NET partial trust policy files is the same as in previous versions of ASP.NET. Developers and administrators can modify the set of permissions in the ASP.NET named permission set. Developers and administrators can also add additional <CodeGroup > definitions with custom membership conditions. As noted earlier, remember that for the new CAS model the customizations need to 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.

A caveat for ASP.NET 4 is that all custom CodeGroup definitions must map to one of three possible permission set names: FullTrust, ASP.Net, or Nothing. Because ASP.NET 4 partial trust application domains are homogeneous by default, the result of evaluating any custom policy entries must result in one of a constrained set of possible permission sets. Although it may look like you can define different named permission sets when using 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 runtime SecurityException exception, indicating 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 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 sees that an assembly is associated with an empty permission set, the CLR will throw a SecurityException exception, and will refuse to load assemblies with an empty permission set.

An additional fact about the named ASP.NET permission set is that ASP.NET 4 allows you to change the name of this permission set via the permissionSetName attribute on the trust element. Developers and administrators can set a different name on the permissionSetName attribute, and at runtime 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 developers and administrators would need 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 separate and distinct from the ASP.NET default permission set.

The second declarative policy customization is new in ASP.NET 4 and allows developers and administrators to explicitly establish a list of assembly identities that will always be granted full trust. Within the securityPolicy configuration element there is a new child configuration section fullTrustAssemblies. The FullTrustAssembliesSection section is a standard managed add, remove, and clear type of collection where you can specify one or more assembly identities that will be granted full trust at runtime. An example section definition is shown below:

<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 the assembly name, the assembly version, and a 320 character hexadecimal string that is the hex character representation of the public half of the signing key. Notice that there is no concept of assembly location in the definition. It is up to the individual hosting environment (ASP.NET 4, in this case) to successfully find and load assemblies. If a loaded assembly matches the information contained in one of the add elements within fullTrustAssemblies, then the assembly is granted full trust.

Developers and administrators should use fullTrustAssemblies for assemblies that are not GAC-deployed, but are intended to always run in full trust. Because fullTrustAssemblies can be customized from the root Web.config file all the way down to individual application-level Web.config files, it is an easier and more flexible approach for granting full trust than using a membership condition and code group in a partial trust policy file. The fullTrustAssemblies lists can be easily customized for individual applications by just populating different sets of information for different applications. This can be done either in application-level Web.config files, or up in root Web.config files in location elements.

Note that the set of full trust assemblies is established immediately at the time a partial trust application domain is created. As a result if either of the other two customization approaches (partial trust policy file and custom HostSecurityPolicyResolver objects) include information that results in a different grant set for an assembly listed in fullTrustAssemblies this information is ignored and the assembly is still granted full trust.

The third way to customize permission set to assembly associations is programmatically with a custom implementation of the ASP.NET 4 System.Web.Hosting.HostSecurityPolicyResolver type. At run time, ASP.NET 4 internally has its own implementation of the CLR HostSecurityManager type. A HostSecurityManager object is called by the CLR each time an assembly is loaded, and one of its duties is to return a PermissionSet object that should be associated for a given assembly and set of evidence. ASP.NET 4 allows you to customize this decision by calling out to a custom HostSecurityPolicyResolver object each time the CLR asks ASP.NET 4 for a permission-set decision.

A custom HostSecurityPolicyResolver object is defined in configuration using the new "hostSecurityPolicyResolverType" attribute of the trust element. If ASP.NET 4 sees that a custom HostSecurityPolicyResolver object is configured for an application, it calls the HostSecurityPolicyResolver.ResolvePolicy method on the custom resolver each time the CLR asks for a permission set decision. Unlike what a HostSecurityManager object does, however, a HostSecurityPolicyResolver object can only return a constrained set of possible decisions back to ASP.NET 4. The ResolvePolicy method return value is one of four possible values from the HostSecurityPolicyResult enumeration:

  • DefaultPolicy. Indicates that ASP.NET 4 should use its own logic for determining the appropriate permission set for the assembly. Developers should return DefaultPolicy for assemblies where they do not want 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 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 associated with the application domain. Usually this means the assembly will be granted the permissions 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 empty permission set (in other words, new PermissionSet(PermissionState.None)).

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

The code sample below shows a custom HostSecurityPolicyResolver object that grants full trust to all assemblies loaded from a specific directory. This could be a scenario for organizations that place compiled assemblies in a specific location on disk (not the GAC) and that want all files from that location to automatically run at 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.

C#
public class MyCustomResolver : HostSecurityPolicyResolver
{ 
  public override HostSecurityPolicyResults 
                              ResolvePolicy(Evidence evidence)
  {
    IEnumerator hostEvidence = evidence.GetHostEnumerator();

    while (hostEvidence.MoveNext())
    {
      object hostEvidenceObject = hostEvidence.Current;
                
      if (hostEvidenceObject is System.Security.Policy.Url)
      {
        System.Security.Policy.Url urlEvidence = 
          hostEvidenceObject as System.Security.Policy.Url;

        string fileLocation = urlEvidence.Value;

              if(fileLocation.StartsWith(
                "file:///C:/CustomFullTrustDirectory/"))
              return HostSecurityPolicyResults.FullTrust;
      }
    }  
    
    //tell ASP.NET to perform its own logic by default
    return HostSecurityPolicyResults.DefaultPolicy;
  }
}
Conditional APTCA

In versions of the .NET Framework earlier than version 4, many fully trusted assemblies, including ASP.NET assemblies, were attributed with the System.Security.AllowPartiallTrustedCallersAttribute attribute (APTCA for short) to allow partial trust callers access to public APIs contained in such attributed assemblies. The CLR has introduced a variation of APTCA referred to as "conditional APTCA" that enables APTCA attributed assemblies to retain APTCA characteristics in only certain hosted environments. As a result conditional APTCA makes it much easier for features like 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 that care about APTCA and conditional APTCA can provide a list of assemblies to the CLR that specifies a set of assemblies that should always have their APTCA setting honored. Fully trusted assemblies that want their APTCA characteristics enabled only in certain host environments indicate this with the following variation of the assembly level APTCA attribute:

C#
[assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel=NotVisibleByDefault)]

At run time, when the CLR is asked to load an assembly marked as conditionally APTCA, the CLR will check the list of valid conditional APTCA assemblies supplied by the host environment. If the assembly is on the list, then the CLR will treat all publicly exposed code in the assembly just as if the assembly had been attributed with the pre-.NET Framework 4 version of APTCA.

However if a conditional APTCA assembly is not on the host environment's list of assemblies that should be treated as APTCA, the assembly still will be loaded but without its APTCA characteristics. Things get a little complicated at this point since the actual availability of public APIs in such an assembly to partial trust user code will vary depending on whether or not the assembly is 100% security transparent – i.e. attributed with an assembly level System.Security.SecurityTransparent attribute (security transparency in ASP.NET 4 is described in a later section of this whitepaper). In summary what happens for ASP.NET 4's public APIs falls into two buckets:

  • For most ASP.NET related assemblies all of the public APIs become unavailable to partial trust callers. This effectively prevents the vast majority of ASP.NET 4's public APIs 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 will still be callable from partial trust callers. However once code paths in those assemblies eventually reach back into the rest of the ASP.NET codebase the calls will fail. The end result is basically the same behavior from previous ASP.NET releases, with the minor difference that calls to APIs in these assemblies will progress a little further prior to failing.

    • There are only two functional 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 doesn't really count as a 100% security transparent assembly in ASP.NET 4 because all of the types that used to exist in that assembly were moved into System.Web.dll. Effectively System.Web.Routing.dll is a metadata-only assembly.

The conditional APTCA attribute variation can now be found on all of the following ASP.NET 4 related 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 is a new assembly in ASP.NET 4)

  • System.Web.Abstractions.dll (types in this assembly were moved back into System.Web.dll)

  • System.Web.Routing.dll (types in this assembly were moved back into System.Web.dll)

Conditional APTCA and ASP.NET Hosting Permission Attributes

Conditional APTCA is the technology that allowed ASP.NET 4 to remove the AspNetHostingPermission from 99% of ASP.NET 4's public APIs. There are a few places where AspNethHostingPermission still exist in ASP.NET 4, but those places are where the permission's intent really makes sense.

For the other 99% of the ASP.NET APIs the two following uses of AspNetHostingPermission have disappeared:

C#
[AspNetHostingPermission(SecurityAction.LinkDemand,
                         Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand,
                         Level=AspNetHostingPermissionLevel.Minimal)]

In practice this means more than 1000 classes (and their methods) have had one or both of these attributes removed from their definitions.

These permission definitions were used by earlier versions of ASP.NET to prevent ASP.NET assemblies from being loaded in non-Web partial trust environments — the biggest environment of concern being partially trusted managed controls and managed applications loaded into browsers like Internet Explorer and Firefox. With conditional APTCA, the same effective protections are still 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 the CLR with a list of conditional APTCA assemblies where their APTCA characteristics should be honored. ASP.NET 4 supplies a hard-coded list to the CLR that contains all of the ASP.NET 4 assemblies. If ASP.NET 4 did not do this, Web applications would fail instantly when the first line of ASP.NET internal code attempted to run within a partial trust application domain.

Because conditional APTCA is a new CAS concept that has not yet been implemented in other parts of the .NET Framework, it is likely that future versions of the .NET Framework will expand the list of conditional APTCA assemblies. Additionally, as individual developers become aware of conditional APTCA and use it for their 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 added a new managed configuration section where new conditional APTCA assemblies can be added.

The existing securityPolicy section has a new child configuration section named partialTrustVisibleAssemblies. The partialTrustVisibleAssemblies section is a standard, managed add/remove/clear collection where you can specify one or more assembly identities that should be treated as APTCA if attributed with conditional APTCA. An example section definition is shown below:

<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 and by a 320-character hexadecimal string (for example, 0x03FA4D...) that is the hex character representation of the public half of the signing key used on the conditional APTCA attributed assembly. There is no need to specify a version attribute – only the assembly name and public key token are needed by the CLR.

How Conditional APTCA Affects Non-Web Partial Trust Applications

In earlier versions of ASP.NET there were a few scattered types and namespaces that intentionally were not attributed with AspNetHostingPermission. This allowed certain types to be called from non-ASP.NET partial trust environments like ClickOnce applications. The types and namespaces where this was allowed were:

For the first two cases (types in System.Web.ClientServices), these types are now un-useable in .NET Framework 4 partial trust environments like ClickOnce. Because the containing assembly (System.Web.Extensions.dll) is a ASP.NET 4 conditionally APTCA attributed assemblies, 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. Currently there are no plans to re-enable these types for partial trust ClickOnce usage. There are several reasons.

First, the .NET Framework 4 has been split into a client and an extended SKU and the general assumption is that many ClickOnce applications will target the client SKU. It would be a substantial engineering investment figuring out how to re-factor ASP.NET client services types into the client SKU. In addition, layered on top of this re-factoring, there is additional complexity around the question of how to accomplish the re-factoring while maintaining the necessary conditional APTCA boundaries. As a result, in the .NET Framework 4 the client services types are only available to non-ASP.NET full-trust environments, including ClickOnce applications configured to run at full trust using the extended .NET Framework 4 SKU.

For the HttpUtility type, the effect of conditional APTCA depends on which methods a developer was using:

  • If partial trust code was calling any of the HtmlEncode or HtmlDecode method overloads, you can instead switch to using the new .NET Framework 4 System.Net.WebUtility class. The new WebUtility type contains the ASP.NET HTML encoding/decoding implementations, but refactored and moved into the System.Net namespace and contained in System.dll. Because System.dll is available in all partial trust host environments there is no issue with WebUtility method's accessibility to non-ASP.NET partial trust applications.

  • If partial trust code was calling any of the other methods on HttpUtility, the same issue as described earlier for the client services types applies. That is, HttpUtility is available only to non-ASP.NET full trust callers in the .NET Framework 4.

Security Transparency and how it works in ASP.NET 4

Throughout this document there have been references to "security transparency" and the fact that ASP.NET uses it. An exhaustive discussion of security transparency is beyond the scope of this topic. However to briefly summarize, security transparency is a concept whereby developers can indicate to the CLR whether or not a piece of code will ever carry out a security sensitive operation. Transparent code can never assert permissions – this holds true regardless of whether or not transparent code is fully trusted (e.g. in the GAC) or partial trust.

As a result security transparency is a very powerful feature for .NET Framework teams 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 broad swath of public and internal APIs even though .NET Framework code exists in the fully trusted GAC.

With security transparency either an entire assembly can be transparent, or only a subset of the code within the assembly can be transparent. Although the ideal case is the former, when writing .NET Framework code it is not uncommon to have some code paths that do have a legitimate need to carry out security sensitive tasks. Assemblies that contain 100% transparent code contain an assembly level System.Security.SecurityTransparent attribute.

Assemblies with a mixture of transparent and non-transparent code do not have an assembly level transparency attribute. Instead individual classes within such assemblies will be attributed with either the System.Security.SecuritySafeCritical attribute or the System.Security.SecurityCritical attribute. The behavior of unattributed classes is a bit complicated. However as a simplified explanation: un-attributed types in ASP.NET 4 assemblies that have adopted the new transparency model are implicitly considered to be security transparent.

Security Transparency in Practice and Security RuleSets

Of course when security and backwards compatibility intersect, no security architecture retains its initial purity of concept and this holds true for ASP.NET 4. Since so much of ASP.NET 4's codebase is in System.Web.dll, it was not practical to convert all of ASP.NET 4's codebase over to the new transparency model. Instead, ASP.NET 4's code can be partitioned into three general buckets:

  • 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 (Note that all the types in this assembly were marked as obsolete in ASP.NET 4. Even though the assembly still exists the expectation is that developers stop using the types in this assembly over time.)

  • Code that was transitioned to the new transparency model, which include 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 metadata-only and whose types moved into a different ASP.NET assembly, including the following

    • System.Web.Abstractions.dll (Note that all the types that were in this assembly in earlier versions of ASP.NET were moved back into System.Web.dll. As a result, Sytem.Web.Abstractions.dll is a metadata-only assembly in ASP.NET 4).

    • System.Web.Routing.dll (Note that all the types that were in this assembly in earlier versions of ASP.NET were moved back into System.Web.dll. As a result, System.Web.Routing.dll is a metadata-only assembly in ASP.NET 4).

The easiest case to describe from the list above is the portion of ASP.NET that moved to the new transparency model. For those assemblies, and the public types contained within them, the vast majority of code is considered security transparent. A relatively small of amount of code in these assemblies performs security-sensitive operations, and that code is marked as either safe-critical or critical code.

The more complicated case rests with all of the other functional assemblies (leaving out the obsolete or type-redirected assemblies) that did not move to the new transparency model. The way in which ASP.NET 4 assemblies express their adoption of the new transparency model is with a new .NET Framework 4 concept introduced by the CLR called a "SecurityRuleSet".

There are two levels of "SecurityRuleSet" configurations, level one and level two. The SecurityRuleSet configuration for all types in an assembly is expressed with the System.Security.SecurityRulesAttribute assembly-level attribute. All ASP.NET 4 assemblies that moved to the new transparency model have the following assembly level attribute:

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

All ASP.NET 4 assemblies that remained on the older 2.0-era transparency model (which for ASP.NET effectively means no transparency since ASP.NET prior to ASP.NET 4 never used transparency concepts) have the following assembly level attribute:

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

Functionally this means that older public ASP.NET 4 APIs can be called from partial trust user code and can internally carry out any security sensitive operations. This 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 out-of-band releases like MVC, most new ASP.NET code is security transparent and thus safer by default.

The CLR considers the public surface area of all ASP.NET 4 assemblies with SecurityRulesSet.Level1 as being safe-critical by default (that is, equivalent to being attributed with System.Security.SecuritySafeCritical) as long as the hosting environment honors the APTCA attribute. If APTCA is not honored, the CLR will trigger 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 a level-1-attributed ASP.NET assembly, developers see the same behavior as in previous releases of the .NET Framework when partial trust code attempted to call a non-APTCA attributed full trust assembly.

The CLR considers the public surface area of all ASP.NET 4 assemblies with SecurityRulesSet.Level2 as being security transparent by default (that is, equivalent to being attributed with System.Security.SecurityTransparent) as long as the hosting environment honors the APTCA attribute. If APTCA is not honored, and a level-2-attributed ASP.NET assembly 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 fail with a SecurityException exception.

As mentioned earlier, if APTCA is not honored, and a Level2-attributed ASP.NET assembly is 100% security transparent, then partial trust callers will be able to successfully call any of the public APIs in that assembly. However in practice all this means is that a SecurityException exception occurs a bit later in the call path when the code in the 100% security transparent assembly eventually calls into some other portion of either a level-1-attributed ASP.NET assembly, or a level-2-attributed ASP.NET assembly that is not 100% security transparent.

Transparency and ASP.NET Compilation

Any compiled artifacts created by the ASP.NET compilation system are also affected by ASP.NET 4's adoption of both the new CAS model as well as the new security transparency model. This includes items like page assemblies, pre-compiled assemblies, and the compiled results of the App_Code directory. The compilation system varies it behavior based upon the setting of the legacyCasModel attribute of the trust element.

The table below describes how dynamically compiled artifacts are attributed in both the legacy and the newer CAS model:

legacyCasModel attribute setting

Website trust level

Attributes applied to compiled assemblies

False (i.e. new CAS model)

Full trust

SecurityRules(SecurityRuleSet.Level2)

High trust or lower

SecurityTransparent

SecurityRules(SecurityRuleSet.Level2)

True (i.e. 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 upon <trust legacyCasModel="true|false"/>, there are some restrictions on how developers share compiled artifacts across different partial trust ASP.NET 4 applications. Compiled artifacts created from a partial trust application with the legacyCasModel attribute set to "true" (i.e. the old CAS model) can be used in other partial trust applications using either the new or the old CAS models. However compiled artifacts created from a partial trust application with the legacyCasModel attribute set to false (i.e. the new CAS model) should not be deployed in other partial trust applications using the old CAS model. The older CAS model does not interpret the SecurityRules attribute and thus the CLR will not enforce any of the new security restrictions associated with the SecurityRules attribute.

Page view tracker