Chapter 9 – Using Code Access Security with ASP.NET
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |
Improving Web Application Security: Threats and Countermeasures
J.D. Meier, Alex Mackman, Michael Dunner, Srinath Vasireddy, Ray Escamilla and Anandha Murukan
Microsoft Corporation
Published: June 2003
Last Revised: January 2006
Applies to:
- ASP.NET version 1.1
- .NET Framework version 1.1
See the "patterns & practices Security Guidance for Applications Index" for links to additional security resources.
See the Landing Page Page for the starting point and a complete overview of Improving Web Application Security: Threats and Countermeasures.
Summary: With the .NET Framework version 1.1 you can configure an ASP.NET Web application or Web service to run with partial trust to improve security by constraining its ability to access system level resources and resources owned by other applications. This chapter shows you how ASP.NET code access security policy works, how to configure it and how to develop Web applications for partial trust environments. The chapter explains how to sandbox resource access, for example code that accesses databases, the event log and Web services so that it can successfully run at reduced trust levels.
Contents
In This Chapter
Overview
How to Use This Chapter
Resource Access
Full Trust and Partial Trust
Configuring Code Access Security in ASP.NET
ASP.NET Policy Files
ASP.NET Policy
Developing Partial Trust Web Applications
Trust Levels
Approaches for Partial Trust Web Applications
Customize Policy
Sandbox Privileged Code
Deciding Which Approach to Take
Medium Trust
Medium Trust Restrictions
Summary
Additional Resources
In This Chapter
- Configuring Web application trust levels and ASP.NET code access security policy
- Developing partial-trust Web applications
- Sandboxing privileged code
- Writing to the event log from medium-trust Web applications
- Calling OLE DB data sources from medium-trust Web applications
- Calling Web services from medium-trust Web applications
Overview
Code access security is a resource constraint model that allows administrators to determine if and how particular code is able to access specified resources and perform other privileged operations. For example, an administrator might decide that code downloaded from the Internet should not be given permission to access any resources, while Web application code developed by a particular company should be offered a higher degree of trust and, for example, be allowed to access the file system, the event log, and Microsoft® SQL Server™ databases.
Traditional principal-based security, such as that provided by the operating system, authorizes access to resources based on user identity. For example, programs launched by a local administrator have no limitations on the local machine. Unfortunately, if the administrator's identity is spoofed and a malicious user is able to execute code using the administrator's security context, the malicious user also has no restrictions. This is where code access security is important because it can provide additional restrictions and security based on the code itself, rather than the user running the code.
With Microsoft .NET Framework versions 1.1 and 2.0, administrators can configure policy for ASP.NET Web applications and Web services, which might consist of multiple assemblies. They can also grant code access security permissions to allow the application to access specific resource types and to perform specific privileged operations.
Note Web applications and Web services built using .NET Framework version 1.0 always run with unrestricted code access permissions. This is not configurable.
Using code access security with Web applications helps you provide application isolation in hosted environments where multiple Web applications run on the same Web server. Internet service providers (ISPs) that run multiple applications from different companies can use code access security to:
Isolate applications from each other.
For example, code access security can be used to ensure that one Web application cannot write to another Web application's directories.
Isolate applications from system resources.
For example, code access security can restrict access to the file system, registry, event logs, and network resources, as well as other system resources.
Code access security is one mechanism that can be used to help provide application isolation. Microsoft Windows Server® 2003 and Internet Information Services (IIS) 6.0 also provide process isolation for Web applications. Process isolation combined with code access security provides the recommended model for application isolation. For more information, see Chapter 20, "Hosting Multiple ASP.NET Applications."
How to Use This Chapter
This chapter does not cover the fundamentals of code access security. A certain amount of prerequisite knowledge is assumed, although key concepts are reiterated where appropriate. For more information about how code access security works, see Chapter 8, "Code Access Security in Practice."
The current chapter focuses on ASP.NET code access security policy configuration and shows you how to overcome some of the main hurdles that you might encounter when you develop partial-trust Web applications.
Resource Access
All resource access from ASP.NET applications and managed code in general is subject to the following two security layers:
Code access security. This security layer verifies that all of the code in the current call stack, leading up to and including the resource access code, is authorized to access the resource. An administrator uses code access security policy to grant permissions to assemblies. The permissions determine precisely which resource types the assembly can access. Numerous permission types correspond to the different resource types that can be accessed. These types include the file system, registry, event log, directory services, SQL Server, OLE DB data sources, and network resources.
For a full list of code access permissions, see Chapter 8, "Code Access Security in Practice."
Operating System/Platform Security. This security layer verifies that the security context of the requesting thread can access the resource. If the thread is impersonating, then the thread impersonation token is used. If not, then the process token is used and is compared against the access control list (ACL) that is attached to the resource to determine whether or not the requested operation can be performed and the resource can be accessed.
Both checks must succeed for the resource to be successfully accessed. All of the resource types that are exposed by the .NET Framework classes are protected with code access permissions. Figure 9.1 shows a range of common resource types that are accessed by Web applications, as well as the associated code access permission that is required for the access attempt to succeed.
Figure 9.1
Common resource types accessed from ASP.NET Web applications and associated permission types
Full Trust and Partial Trust
By default, Web applications run with full trust. Full-trust applications are granted unrestricted code access permissions by code access security policy. These permissions include built-in system and custom permissions. This means that code access security will not prevent your application from accessing any of the secured resource types that Figure 9.1 shows. The success or failure of the resource access attempt is determined purely by operating system-level security. Web applications that run with full trust include all ASP.NET applications built using .NET Framework version 1.0. By default, .NET Framework version 1.1 and 2.0 applications run with full trust, but the trust level can be configured using the <trust> element, which is described later in this chapter.
If an application is configured with a trust level other than "Full," it is referred to as a partial-trust application. Partial-trust applications have restricted permissions, which limit their ability to access secured resources.
Important Web applications built on .NET Framework version 1.0 always run with full trust because the types in System.Web demand full-trust callers.
Configuring Code Access Security in ASP.NET
By default, Web applications run with full trust and have unrestricted permissions. To modify code access security trust levels in ASP.NET, you have to set a switch in the Machine.config file or the application-level Web.config file and configure the application as a partial-trust application.
Note In ASP.NET 2.0, the trust level can be set in your application-level Web.config file or in the machine-level Web.config file. For more information, please see, "How To: Use Code Access Security in ASP.NET 2.0."
Configuring Trust Levels
The <trust> element in Machine.config controls whether or not code access security is enabled for a Web application. Open Machine.config or Web.config, search for "<trust>", and you will see the following.
<system.web>
<!-- level="[Full|High|Medium|Low|Minimal]" -->
<trust level="Full" originUrl=""/>
</system.web>
With the trust level set to "Full," code access security is effectively disabled because permission demands do not stand in the way of resource access attempts. This is the only option for ASP.NET Web applications built on .NET Framework version 1.0. As you go through the list from "Full" to "Minimal," each level takes away more permissions, which further restricts your application's ability to access secured resources and perform privileged operations. Each level gives greater degrees of application isolation. Table 9.1 shows the predefined trust levels and indicates the major restrictions in comparison to the previous level.
Table 9.1 Restrictions Imposed by the ASP.NET Trust Levels
ASP.NET Trust Level |
Main Restrictions |
---|---|
Full | Unrestricted permissions. Applications can access any resource that is subject to operating system security. All privileged operations are supported. |
High | Not able to call unmanaged code
Not able to call serviced components Not able to write to the event log Not able to access Microsoft Message Queuing queues Not able to access OLE DB data sources Note In ASP.NET 2.0, you can access OLE DB, Oracle, and ODBC data sources by customizing the policy and granting the appropriate permission; for example, OleDbPermission, OraclePermission, or OdbcPermission. |
Medium | In addition to the above, file access is restricted to the current application directory and registry access is not permitted.
No reflection permissions whatsoever. No sockets permission. Can send e-mail by using SMTP servers. Note In ASP.NET 2.0, you can use the SQL Server data provider from medium-trust applications. You can also access OLE DB, Oracle, and ODBC data sources by customizing the trust-level policy and granting the appropriate permission; for example, OleDbPermission, OraclePermission, or OdbcPermission. |
Low | In addition to the above, the application is not able to connect to SQL Server and code cannot call CodeAccessPermission.Assert (no assertion security permission).
Read-only access for file I/O within the application's virtual directory structure. |
Minimal | Only the execute permission is available. |
Locking the Trust Level
If a Web server administrator wants to use code access security to ensure application isolation and restrict access to system level resources, the administrator must be able to define security policy at the machine level and prevent individual applications from overriding it.
Application service providers or anyone responsible for running multiple Web applications on the same server should lock the trust level for all Web applications. To do this, enclose the <trust> element in the Machine.config file within a <location> tag, and then set the allowOverride attribute to false, as shown in the following example.
<location allowOverride="false">
<system.web>
<!-- level="[Full|High|Medium|Low|Minimal]" -->
<trust level="Medium" originUrl=""/>
</system.web>
</location>
You can also use a path attribute on the <location> element to apply a configuration to a specific site or Web application that cannot be overridden. For more information about the <location> element, see Chapter 19, "Securing Your ASP.NET Application and Web Services."
Note In ASP.NET 2.0, the trust level can be locked in the machine-level Web.config file.
ASP.NET Policy Files
Each trust level is mapped to an individual XML policy file and the policy file lists the set of permissions granted by each trust level. Policy files are located in the following directory:
%windir%\Microsoft.NET\Framework\{version}\CONFIG
Trust levels are mapped to policy files by the <trustLevel> elements in the Machine.config file, which are located just above the <trust> element, as shown in the following example.
<location allowOverride="true">
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
<trustLevel name="High" policyFile="web_hightrust.config"/>
<trustLevel name="Medium" policyFile="web_mediumtrust.config"/>
<trustLevel name="Low" policyFile="web_lowtrust.config"/>
<trustLevel name="Minimal" policyFile="web_minimaltrust.config"/>
</securityPolicy>
<!-- level="[Full|High|Medium|Low|Minimal]" -->
<trust level="Full" originUrl=""/>
</system.web>
</location>
Note No policy file exists for the full-trust level. This is a special case that simply indicates the unrestricted set of all permissions.
ASP.NET policy is fully configurable. In addition to the default policy levels, administrators can create custom permission files and configure them using the <trust> element, which is described later in this chapter. The policy file associated with the custom level must also be defined by a <trustLevel> element in the Machine.config file.
Note In ASP.NET 2.0, the trust level can be configured in the machine-level Web.config file.
ASP.NET Policy
Code access security policy is hierarchical and is administered at multiple levels. Policy can be created for the enterprise, machine, user, and application domain levels. ASP.NET code access security policy is an example of application domain-level policy.
Settings in a separate XML configuration file define the policy for each level. Enterprise, machine, and user policy can be configured using the Microsoft .NET Framework configuration tool, but ASP.NET policy files must be edited manually using an XML or text editor.
The individual ASP.NET trust-level policy files say which permissions might be granted to applications configured at a particular trust level. The actual permissions that are granted to an ASP.NET application are determined by intersecting the permission grants from all policy levels, including enterprise, machine, user, and ASP.NET (application domain) level policy.
Because policy is evaluated from enterprise level down to ASP.NET application level, permissions can only be taken away. You cannot add a permission at the ASP.NET level without a higher level first granting the permission. This approach ensures that the enterprise administrator always has the final say and that malicious code that runs in an application domain cannot request and be granted more permissions than an administrator configures.
For more information about policy evaluation, see Chapter 8, "Code Access Security in Practice."
Inside an ASP.NET Policy File
To see which permissions are defined by a particular trust level, open the relevant policy file in Notepad or (preferably) an XML editor and locate the "ASP.NET" named permission set. This permission set lists the permissions that are configured for the application at the current trust level.
Note You will also see the "FullTrust" and "Nothing" permission sets. These sets contain no permission elements because "FullTrust" implies all permissions and "Nothing" contains no permissions.
The following fragment shows the major elements of an ASP.NET policy file:
<configuration>
<mscorlib>
<security>
<policy>
<PolicyLevel version="1">
<SecurityClasses>
... list of security classes, permission types,
and code group types ...
</SecurityClasses>
<NamedPermissionSets>
<PermissionSet Name="FullTrust" ... />
<PermissionSet Name="Nothing" .../>
<PermissionSet Name="ASP.NET" ...
... This is the interesting part ...
... List of individual permissions...
<IPermission
class="AspNetHostingPermission"
version="1"
Level="High" />
<IPermission
class="DnsPermission"
version="1"
Unrestricted="true" />
...Continued list of permissions...
</PermissionSet>
</PolicyLevel version="1">
</policy>
</security>
</mscorlib>
</configuration>
Notice that each permission is defined by an <IPermission> element, which defines the permission type name, version, and whether or not it is in the unrestricted state.
Permission State and Unrestricted Permissions
Many permissions include state, which is used to fine-tune the access rights specified by the permission. The state determines precisely what the permission allows your application to do. For example, a FileIOPermission might specify a directory and an access type (read, write, and so on). The following permission demand requires that calling code is granted read permission to access the C:\SomeDir directory:
(new FileIOPermission(FileIOPermissionAccess.Read, @"C:\SomeDir")).Demand();
In its unrestricted state, the FileIOPermission allows any type of access to any area on the file system (of course, operating system security still applies). The following permission demand requires that the calling code be granted the unrestricted FileIOPermission:
(new FileIOPermission(PermissionState.Unrestricted)).Demand();
The ASP.NET Named Permission Set
ASP.NET policy files contain an "ASP.NET" named permission set. This defines the set of permissions that is granted by application domain policy to associated applications.
ASP.NET policy also introduces a custom AspNetHostingPermission, which has an associated Level attribute that corresponds to one of the default levels. All public types in the System.Web and System.Web.Mobile are protected with demands for the Minimum level of this permission. This risk mitigation strategy is designed to ensure that Web application code cannot be used in other partial-trust environments without specific policy configuration by an administrator.
Substitution Parameters
If you edit one of the ASP.NET policy files, you will notice that some of the permission elements contain substitution parameters ($AppDirUrl$, $CodeGen$, and $Gac$). These parameters allow you to configure permissions to assemblies that are part of your Web application, but are loaded from different locations. Each substitution parameter is replaced with an actual value at security policy evaluation time, which occurs when your Web application assembly is loaded for the first time. Your Web application might consist of the following three assembly types:
Private assemblies that are compiled at build time and deployed in the application's bin directory
Important This type of assembly cannot be strong named. Strong named assemblies used by ASP.NET Web applications must be installed in the global assembly cache. This restriction is necessary because of the internal workings of the multi-application domain worker process.
Dynamically compiled assemblies that are generated in response to a page request
Shared assemblies that are loaded from the computer's global assembly cache
Each of these assembly types has an associated substitution parameter, which Table 9.2 summarizes.
Table 9.2 ASP.NET Code Access Security Policy Substitution Parameters
Parameter | Represents |
---|---|
$AppDirUrl$ | The application's virtual root directory. This allows permissions to be applied to code that is located in the application's bin directory.
For example, if a virtual directory is mapped to C:\YourWebApp, then $AppDirUrl$ would equate to C:\YourWebApp. |
$CodeGen$ | The directory that contains dynamically generated assemblies (for example, the result of .aspx page compiles). This can be configured on a per application basis and defaults to %windir%\Microsoft.NET\Framework\{version}\Temporary ASP.NET Files.
$CodeGen$ allows permissions to be applied to dynamically generated assemblies. |
$Gac$ | Any assembly that is installed in the computer's global assembly cache (GAC) (%windir%\assembly). This allows permissions to be granted to strong named assemblies loaded from the GAC by the Web application. |
Developing Partial Trust Web Applications
Partial trust Web applications are applications that do not have full trust and have a restricted set of code access permissions determined by code access security policy. As a result, partial-trust applications are limited in their ability to access secured resources and perform other privileged operations. Certain permissions are denied to partial-trust applications, so resources requiring those permissions cannot be directly accessed. Other permissions are granted in a restricted way, so resources that require those permissions might be accessible, but in a limited way. For example, a restricted FileIOPermission might specify that the application can access the file system, but only in directories beneath the application's virtual directory root.
Why Partial Trust?
By configuring a Web application or Web service for partial trust, you can restrict the application's ability to access crucial system resources or resources that belong to other Web applications. By granting only the permissions that the application requires and no more, you can build least privileged Web applications and limit damage potential should the Web application be compromised by a code injection attack.
Problems You Might Encounter
If you take an existing Web application and reconfigure it to run at a partial-trust level, you are likely to run into the following issues, unless the application is extremely limited in the resources it accesses:
Your application is unable to call strong named assemblies that are not annotated with AllowPartiallyTrustedCallersAttribute (APTCA). Without APTCA, strong named assemblies issue a demand for full trust, which will fail when the demand reaches your partial-trust Web application. Many system assemblies only support full-trust callers. The following list shows which .NET Framework assemblies support partial-trust callers and can be called directly by partial-trust Web applications without necessitating sandboxed wrapper assemblies.
Note Sandboxing is discussed in detail later in this chapter.
The following system assemblies have APTCA applied, which means that they can be called by partial-trust Web applications or any partially trusted code:
- System.Windows.Forms.dll
- System.Drawing.dll
- System.dll
- Mscorlib.dll
- IEExecRemote.dll
- Accessibility.dll
- Microsoft.VisualBasic.dll
- System.XML.dll
- System.Web.dll
- System.Web.Services.dll
- System.Data.dll
If your partial-trust application fails because it calls a strong named assembly that is not marked with APTCA, a generic SecurityException is generated. In this circumstance, the exception contains no additional information to indicate that the call failed because of a failed demand for full trust.
Permission demands might start to fail. The configured trust level might not grant the necessary permission for your application to access a specific resource type. The following are some common scenarios where this could prove problematic:
- Your application uses the event log or registry. Partial trust Web applications do not have the necessary permissions to access these system resources. If your code does so, a SecurityException will be generated.
- Your application uses the ADO.NET OLE DB data provider to access a data source. The OLE DB data provider requires full-trust callers.
Note In ASP.NET 2.0, you can use the SQL Server data provider from medium-trust applications. You can also access OLE DB, Oracle, and ODBC data sources by customizing the trust-level policy and granting the appropriate permission; for example, OleDbPermission, OraclePermission, or OdbcPermission. For more information on customizing the trust-level policy file, see "How To: Use Code Access Security in ASP.NET 2.0."
- Your application calls a Web service. Partial-trust Web applications have a restricted WebPermission, which affects the ability of the application to call Web services located on remote sites.
Trust Levels
If you plan to migrate an existing application to a partial-trust level, a good approach is to reduce permissions incrementally so that you can see what parts of your application break. For example, start by setting the trust level attribute to High, then Medium, and so on. Ultimately, the trust level you should target depends on the degree of restriction you want to place on the application. Use the following as guidance:
- Applications configured for high, medium, low, or minimal trust will be unable to call unmanaged code or serviced components, write to the event log, access Message Queuing queues, or access OLE DB data sources.
- Applications configured for high trust have unrestricted access to the file system.
- Applications configured for medium trust have restricted file system access. They can only access files in their own application directory hierarchy.
- Applications configured for low or minimal trust cannot access SQL Server databases.
- Minimal trust applications cannot access any resources.
Table 9.3 identifies the permissions that each ASP.NET trust level grants. The full level is omitted from the table because it grants all of the permissions in their unrestricted state.
Table 9.3 Default ASP.NET Policy Permissions and Trust Levels
Permission and State | High | Medium | Low | Minimal |
---|---|---|---|---|
AspNetHosting Level |
High |
Medium |
Low |
Minimal |
DnsPermission Unrestricted |
v |
v |
||
EnvironmentPermission Unrestricted Read Write |
v |
TEMP; TMP; USERNAME; OS; COMPUTERNAME |
||
EventLogPermission | ||||
FileIOPermission Unrestricted Read Write Append PathDiscovery |
v |
$AppDir$ $AppDir$ $AppDir$ $AppDir$ |
$AppDir$ $AppDir$ |
|
IsolatedStorageFilePermission Unrestricted AssemblyIsolationByUser- Unrestricted UserQuota |
v |
v v |
v 1MB (can vary with site) |
|
OleDbClientPermission Unrestricted |
||||
PrintingPermission Unrestricted DefaultPrinting |
v |
v |
||
ReflectionPermission Unrestricted ReflectionEmit |
v |
|||
RegistryPermission Unrestricted |
v |
|||
SecurityPermission Unrestricted Assertion Execution ControlThread ControlPrinicipal RemotingConfiguration |
v v v v v |
v v v v v |
v |
v |
SocketPermission Unrestricted |
v |
|||
SqlClientPermission Unrestricted |
v |
v |
||
WebPermission Unrestricted |
v |
$OriginHost$ |
Note In ASP.NET 2.0, you can use the SQL Server data provider from medium-trust applications. You can also access OLE DB, Oracle, and ODBC data sources by customizing the trust-level policy and granting the appropriate permission; for example, OleDbPermission, OraclePermission, or OdbcPermission. For more information on customizing the trust-level policy file, see "How To: Use Code Access Security in ASP.NET 2.0."
Approaches for Partial Trust Web Applications
If you develop a partial-trust application or enable an existing application to run at a partial-trust level, and you run into problems because your application is trying to access resources for which the relevant permissions have not been granted, you can use two basic approaches:
Customize policy
Customize policy to grant the required permissions to your application. This might not be possible, for example in hosting environments, where policy restrictions are rigid.
Sandbox privileged code
Place resource access code in a wrapper assembly, grant the wrapper assembly full trust (not the Web application), and sandbox the permission requirements of privileged code.
The right approach depends on what the problem is. If the problem is related to the fact that you are trying to call a system assembly that does not contain AllowPartiallyTrustedCallersAttribute, the problem becomes how to give a piece of code full trust. In this scenario, you should use the sandboxing approach and grant the sandboxed wrapper assembly full trust.
Note Customizing policy is the easier of the two approaches because it does not require any development effort.
Customize Policy
If your Web application contains code that requires more permissions than are granted by a particular ASP.NET trust level, the easiest option is customizing a policy file to grant the additional code access security permission to your Web application. You can either modify an existing policy file and grant additional permissions or create a new one based on an existing policy file.
Note If you modify one of the built-in policy files, for example, the medium-trust Web_mediumtrust.config policy file, this affects all applications that are configured to run with medium trust.
To customize policy for a specific application
Copy one of the existing policy files to create a new policy file. For example, copy the medium trust policy file and create a new policy file such as the following:
%windir%\Microsoft.NET\Framework\{version}\CONFIG\web_yourtrust.config
Add the required permission to the ASP.NET permission set in the policy file or, alternatively, modify an existing permission to grant a less restrictive permission.
Add a new <trustLevel> mapping beneath <securityPolicy> in the Machine.config file for the new trust level file, as follows:
<securityPolicy> <trustLevel name="Custom" policyFile="web_yourtrust.config"/> . . . </securityPolicy>
Configure your application to run with the new trust level by configuring the <trust> element in the application's Web.config file, as follows:
<system.web> <trust level="Custom" originUrl=""/> </system.web>
Sandbox Privileged Code
Another approach that does not require an update to ASP.NET code access security policy is wrapping your resource access code in its own wrapper assembly and configuring machine-level code access security policy to grant the specific assembly the appropriate permission. Then you can sandbox the higher-privileged code using the CodeAccessPermission.Assert method so you do not have to change the overall permission grant of the Web application. The Assert method prevents the security demand issued by the resource access code from propagating back up the call stack beyond the boundaries of the wrapper assembly.
A Sandboxing Pattern
You can apply the following pattern to any privileged code that needs to access a restricted resource or perform another privileged operation for which the parent Web application does not have sufficient permissions:
Encapsulate the resource access code in a wrapper assembly.
Make sure the assembly is strong named so that it can be installed in the GAC.
Assert the relevant permission prior to accessing the resource.
This means that the caller must have the assertion security permission (SecurityPermission with SecurityPermissionFlag.Assertion). Applications configured for Medium or higher trust levels have this permission.
Asserting permissions is a dangerous thing to do because it means that the code that calls your code can access the resource that is encapsulated by your assembly without requiring the relevant resource access permission. The Assert statement says that your code can vouch for the legitimacy of its callers. To do this, your code should demand an alternate permission so that it can authorize the calling code prior to calling Assert. In this way, you only allow code that has been granted the alternate permission to access the resource that your assembly exposes.
The .NET Framework might not provide a suitable permission to demand. In this case, you can create and demand a custom permission. For more information about how to create a custom permission, see "How To: Create a Custom Encryption Permission" in the "How To" section of this guide.
Annotate the wrapper assembly with APTCA.
This allows the partial-trust Web application to call the assembly.
Install the wrapper assembly in the GAC.
This gives full trust to the wrapper, but not the Web application. The ASP.NET policy files contain the following code group, which grants full trust to any assembly located in the GAC:
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust"> <IMembershipCondition class="UrlMembershipCondition" Url="$Gac$/*" version="1" /> </CodeGroup>
Note Default enterprise and local machine policy also grant full trust to any code located in the My Computer zone, which includes code installed in the GAC. This is important because granted permissions are intersected across policy levels.
Configure the Web application trust level (for example, set it to "Medium").
Figure 9.2 shows the sandboxing approach.
Figure 9.2
Sandboxing privileged code in its own assembly, which asserts the relevant permission
It is good practice to use separate assemblies to encapsulate resource access and avoid placing resource access code in .aspx files or code behind files. For example, create a separate data access assembly to encapsulate database access. This makes it easier to migrate applications to partial-trust environments.
Deciding Which Approach to Take
The right approach depends upon the problem you are trying to solve and whether or not you have the option of modifying security policy on the Web server.
Customizing Policy
This approach is the easier of the two and does not require any developer effort. However, you might not be permitted to modify policy on the Web server and, in certain scenarios, your code that calls the .NET Framework class library might require full trust. In these situations, you must use sandboxing. For example, the following resources demand full trust, and you must sandbox your resource access code when it accesses them:
- Event log (through the EventLog class)
- OLE DB data sources (through the ADO.NET OLE DB data provider)
- ODBC data sources (through the ADO.NET ODBC .NET data provider)
- Oracle databases (through the ADO.NET Oracle .NET data provider)
Note This list is not exhaustive but it includes commonly used resource types that require full trust in ASP.NET 1.1.
Note In ASP.NET 2.0, you can access OLE DB, Oracle, and ODBC data sources by customizing the trust-level policy and granting the appropriate permission; for example, OleDbPermission, OraclePermission, or OdbcPermission. For more information on customizing the trust-level policy file, see "How To: Use Code Access Security in ASP.NET 2.0."
Sandboxing
If you sandbox your privileged application code in a separate assembly, you can grant additional permissions to the assembly. Alternatively, you can grant it full trust without requiring your entire application to run with extended permissions.
For example, consider code that uses the ADO.NET 1.1 OLE DB data provider and interacts with the System.Data.OleDb.OleDbCommand class. This code requires full trust. Although the System.Data.dll assembly is marked with AllowPartiallyTrustedCallersAttribute, the System.Data.OleDb.OleDbCommand class, among others, cannot be called by partial-trust callers because it is protected with a link demand for full trust.
To see this, run the following command using the permview utility from the %windir%\Microsoft.NET\Framework\{version} directory:
permview /DECL /OUTPUT System.Data.Perms.txt System.Data.dll
The output in System.Data.Perms.txt includes the following output:
class System.Data.OleDb.OleDbCommand LinktimeDemand permission set:
<PermissionSet class="System.Security.PermissionSet"
version="1" Unrestricted="true"/>
This illustrates that an unrestricted permission set (full trust) is used in a link demand that protects the System.Data.OleDb.OleDbCommand class. In scenarios such as this, it is not sufficient to configure policy to grant specific unrestricted permissions, such as OleDbPermission, to your partial-trust code. Instead, you must sandbox your resource access code and grant it full trust, and the easiest way to do this is to install it in the GAC. Use Permview.exe to find out about the permission requirements of other classes, although this only shows declarative security attributes. If a class imperatively demands full trust, you cannot see this through Permview.exe. In this event, test the security requirements of the class by calling it from partial-trust code and diagnosing any security exceptions.
Note Just because an assembly is marked with APTCA, it does not mean all of the contained classes support partial-trust callers. Some classes may include explicit demands for full trust.
Note This example is applicable to ASP.NET 1.1 only because the OLE DB data provider does not require full trust in ASP.NET 2.0. In ASP.NET 2.0, it is sufficient to configure the policy to grant specific unrestricted permissions, such as OleDbPermission, to your partial-trust code.
Medium Trust
If you host Web applications, you may choose to implement a medium trust security policy to restrict privileged operations. This section focuses on running medium trust applications, and shows you how to overcome the problems you are likely to encounter.
Running at medium trust has the following two main benefits:
- Reduced attack surface
- Application isolation
Reduced Attack Surface
Since medium trust does not grant the application unrestricted access to all permissions, your attack surface is reduced by granting the application a subset of the full permission set. Many of the permissions granted by medium trust policy are also in a restricted state. If an attacker is somehow able to take control of your application, the attacker is limited in what he or she can do.
Application Isolation
Application isolation with code access security restricts access to system resources and resources owned by other applications. For example, even though the process identity might be allowed to read and write files outside of the Web application directory, the FileIOPermission in medium trust applications is restricted. It only permits the application to read or write to its own application directory hierarchy.
Medium Trust Restrictions
If your application runs at medium trust, it faces a number of restrictions, the most significant of which are:
- It cannot directly access the event log.
- It has restricted file system access and can only access files in the application's virtual directory hierarchy.
- It cannot directly access OLE DB data sources (although medium trust applications are granted the SqlClientPermission, which allows them to access SQL Server).
- It has limited access to Web services.
- It cannot directly access the Windows registry.
This section shows you how to access the following resource types from a medium-trust Web application or Web service:
- OLE DB
- Event log
- Web services
- Registry
OLE DB
Medium-trust Web applications are not granted the OleDbPermission. Furthermore, the OLE DB .NET data provider in ASP.NET 1.1 demands full-trust callers. If you are running ASP.NET 1.1, use the sandboxing approach. Place your data access code in a separate assembly, strong name it, and install it in the GAC, which gives it full trust.
Note Modifying policy does not work unless you set the trust level to "Full" because the OLE DB managed provider demands full trust. If you are running ASP.NET 2.0 and have an application that needs to access OLE DB data sources while running at medium trust, you do not need full trust but only need to customize the policy and grant the OleDbPermission.
Figure 9.3 shows the arrangement.
Figure 9.3
Sandboxing OLE DB resource access
Sandboxing
In this approach, you create a wrapper assembly to encapsulate OLE DB data source access. This assembly is granted full-trust permissions, which are required to use the ADO.NET OLE DB managed provider in ASP.NET 1.1.
To build a sandboxed wrapper assembly to call OLE DB data sources
Create an assembly for your data access code. Configure the assembly version, strong name the assembly, and mark it with the AllowPartiallyTrustedCallersAttribute, as follows:
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyKeyFile(@"..\..\oledbwrapper.snk")] [assembly:AllowPartiallyTrustedCallersAttribute()]
You must annotate any strong named assembly with AllowPartiallyTrustedCallersAttribute if you want to support partial-trust callers. This suppresses an implicit link demand for full trust made by the .NET Framework whenever code from a strong named assembly is loaded and JIT-compiled.
Request full trust. Although not strictly necessary, requesting full trust is a good practice because it allows an administrator to view the assembly's permission requirements by using tools like Permview.exe. To request full trust, request the unrestricted permission set as follows:
[assembly: PermissionSet(SecurityAction.RequestMinimum, Unrestricted=true)]
Wrap database calls with an Assert statement to assert full trust. Wrap a matching RevertAssert call to reverse the effect of the assert. Although not strictly necessary, it is a good practice to place the call to RevertAssert in a finally block.
Because the OLE DB provider demands full trust, the wrapper must assert full-trust. Asserting an OleDbPermission is not sufficient. Step 7 explains how to improve the security of using CodeAccessPermission.Assert.
public OleDbDataReader GetProductList() { try { // Assert full trust (the unrestricted permission set) new PermissionSet(PermissionState.Unrestricted).Assert(); OleDbConnection conn = new OleDbConnection( "Provider=SQLOLEDB; Data Source=(local);" + "Integrated Security=SSPI; Initial Catalog=Northwind"); OleDbCommand cmd = new OleDbCommand("spRetrieveProducts", conn); cmd.CommandType = CommandType.StoredProcedure; conn.Open(); OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); return reader; } catch(OleDbException dbex) { // Log and handle exception } catch(Exception ex) { // Log and handle exception } finally { CodeAccessPermission.RevertAssert(); } return null; }
Build the assembly and install it in the GAC with the following command:
gacutil -i oledbwrapper.dll
To ensure that the assembly is added to the GAC after each subsequent rebuild, add the following post build event command line (available from the project's properties in Visual Studio.NET) to your wrapper assembly project:
"C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\gacutil.exe" /i $(TargetPath)
Note Any strong named assembly that is called by an ASP.NET Web application or Web service must be installed in the GAC. In this instance, you should install the assembly in the GAC to ensure that it is granted full trust.
Configure your Web application for medium trust. Add the following code to Web.config or place it in Machine.config inside a <location> element that points to your application:
<trust level="Medium" originUrl=""/>
Reference the data access assembly from your ASP.NET Web application.
Since a strong named assembly must be in the GAC and not the \bin directory of a Web application, you must add the assembly to the list of assemblies used in the application if you are not using code behind files. You can obtain the PublicKeyToken of your assembly by using the following command:
sn -Tp oledbwrapper.dll
Note Use a capital –T switch.
Then add the following to Machine.config or Web.config:
<compilation debug="false" > <assemblies> <add assembly="oledbwrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4b...06" /> </assemblies> </compilation>
Note In between successive rebuilds of your wrapper assembly, you might need to recycle the ASP.NET worker process because your wrapper assembly, which is installed in the GAC is cached by the ASP.NET process. To recycle the ASP.NET worker process, you can run the IISreset.exe utility.
Protect the code that calls Assert.
The Assert call means that any code that calls the data access wrapper can interact with the OLE DB data source. To prevent malicious code from calling the data access component and potentially using it to attack the database, you can issue a full demand for a custom permission prior to calling Assert and update the medium-trust policy file to grant your Web application the custom permission. This solution entails a reasonable amount of developer effort.
For more information about developing a custom permission, see "How To: Create a Custom Encryption Permission" in the "How To" section of this guide.
Event Log
The EventLogPermission class is designed to encapsulate the rights of code to access the event log. Currently, however, code must be granted full trust to be able to access the event log. This means that a medium trust Web application cannot directly access the event log. To do so, you must sandbox your event logging code.
Accessing the Event Log
Create the event sources used by your application at installation time, when administrator privileges are available. A good approach is to use a .NET installer class, which can be instantiated by the Windows Installer (if you are using .msi deployment) or by the InstallUtil.exe system utility.
If you are unable to create event sources at installation time, and you are in deployment, the administrator should manually create new event source entry beneath the following registry key
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\<LogName>
Note You should not grant write permission to the ASP.NET process account (or any impersonated account if your application uses impersonation) on the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\ registry key. If you allow write access to this key and the account is compromised, the attacker can modify any log-related setting, including access control to the log, for any log on the system.
For more information about this approach, see "Auditing and Logging" in Chapter 10, "Building Secure ASP.NET Pages and Controls."
Sandboxing
To sandbox your event logging code, you create a wrapper assembly to encapsulate event log access. You then install the wrapper assembly in the global assembly cache so that is granted full trust by code access security policy.
To build a sandboxed wrapper assembly to write to the event log
Create an assembly for your event log code. Configure the assembly version, strong name the assembly, and mark it with the AllowPartiallyTrustedCallersAttribute, as shown in the following example.
[[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyKeyFile(@"..\..\eventlogwrapper.snk")] [assembly:AllowPartiallyTrustedCallersAttribute()]
You must annotate any strong named assembly with AllowPartiallyTrustedCallersAttribute if you want to support partial-trust callers. This suppresses an implicit link demand for full trust made by the .NET Framework whenever code from a strong named assembly is loaded and JIT-compiled.
Note AllowPartiallyTrustedCallersAttribute is defined in the System.Security namespace, so you must reference this namespace with a using statement.
Request appropriate permissions.
Although not strictly necessary, requesting appropriate permissions is a good practice because it allows an administrator to view the assembly's permission requirements by using tools like Permview.exe. Since the event log assembly can be accessed from partial-trust callers, this assembly does not need to request a full trust permission set. The assembly in this example only writes to the event log on a specific machine and, therefore, only needs the following permission request:
[assembly:EventLogPermissionAttribute(SecurityAction.RequestMinimum, MachineName="<machine name>", PermissionAccess=EventLogPermissionAccess.Instrument)]
However, if your assembly needs to request full trust, request the unrestricted permission set as follows:
[assembly: PermissionSet(SecurityAction.RequestMinimum, Unrestricted=true)]
Wrap event log calls with an Assert statement that asserts full trust and a matching RevertAssert that reverses the effect of the assert. Although not strictly necessary, it is a good practice to place the call to RevertAssert in a finally block. The following code writes an Information entry to the Application log with the text "Writing to the event log":
try { string source = "Event Source"; string log = "Application"; string eventText = "Writing to the event log"; EventLogEntryType eventType = EventLogEntryType.Information; //Assert permission EventLogPermission eventPerm; eventPerm = new EventLogPermission(EventLogPermissionAccess.Instrument, "<machinename>"); eventPerm.Assert(); //Check to see if the source exists. if(!EventLog.SourceExists(source)) {//The keys do not exist, so register your application as a source EventLog.CreateEventSource(source, log); } //Write to the log. EventLog.WriteEntry(source, eventText, eventType); } catch(Exception ex) {/*Handle exception*/} finally { CodeAccessPermission.RevertAssert(); }
Build the assembly and install it in the GAC with the following command:
gacutil -i eventlogwrapper.dll
To ensure that the assembly is added to the GAC after each subsequent rebuild, add the following post build event command line (available from the project's properties in Visual Studio.NET) to your wrapper assembly project:
"C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\gacutil.exe" /i $(TargetPath)
Note Any strong named assembly called by an ASP.NET Web application or Web service must be installed in the GAC. Assemblies that are installed in the GAC are granted full trust by default code access security policy.
Configure your Web application for medium trust. Add the following to Web.config or place it in Machine.config inside a <location> element that points to your application:
<trust level="Medium" originUrl=""/>
Reference the event log assembly from your ASP.NET Web application.
Since a strong named assembly must be in the GAC and not the \bin directory of a Web application, then you must add the assembly to the list of assemblies used in the application if you are not using code behind files. You can obtain the PublicKeyToken of your assembly by using the following command:
sn -Tp eventlogwapper.dll
Note Use a capital –T switch.
Then add the following code to Machine.config or Web.config:
<compilation debug="false" > <assemblies> <add assembly="eventlogwrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4b...06"/> </assemblies> </compilation>
Note In between successive rebuilds of your wrapper assembly, you might need to recycle the ASP.NET worker process because your wrapper assembly, which is installed in the GAC is cached by the ASP.NET process. To recycle the ASP.NET worker process (Aspnet_wp.exe) you can run the iisreset.exe utility.
Protect the code that calls the Assert method. The Assert call means that any code that calls the event log wrapper is able to interact with the event log. To prevent malicious code from calling the event log wrapper and potentially using it to fill the event log, you can issue a full demand for a custom permission prior to calling Assert and update the medium trust policy file to grant your Web application the custom permission. This solution entails a reasonable amount of developer effort.
For more information about how to develop a custom permission, see "How To: Create a Custom Encryption Permission" in the "How To" section of this guide.
Web Services
By default, medium-trust policy grants ASP.NET Web applications a restricted WebPermission. To be able to call Web services from your Web application, you must configure the originUrl attribute on your application's <trust> element.
To call a single Web service from a medium trust Web application
Configure the application to run at medium trust.
Set the originUrl to point to the Web service you want to be able to call, as follows:
<trust level="Medium" originUrl="https://servername/.*"/>
The originUrl value is used in the constructor for a System.Text.RegEx regular expression class so that in can perform a match on the URLs that are accessible by the Web service. This RegEx class is used in conjunction with a WebPermission class. The ".*" matches any URL beginning with "http://servername/".
The originUrl attribute is used when ASP.NET policy is evaluated. It gives a value for the $OriginHost$ substitution parameter. Here is the WebPermission definition from Web_mediumtrust.config:
<IPermission
class="WebPermission"
version="1">
<ConnectAccess>
<URI uri="$OriginHost$"/>
</ConnectAccess>
</IPermission>
If you do not specify the Web servers accessed by your application, any Web service request will fail with a SecurityException. To call a Web service on the local Web server, use the following configuration:
<trust level="Medium" originUrl="https://localhost/.*" />
If your application needs to access multiple Web services on different servers, you need to customize ASP.NET policy because you can only specify one originUrl on the <trust> element in Web.config or Machine.config.
To call multiple Web services from a medium-trust application
Copy the Web_mediumtrust.config file, which is in the following directory, to a file called Web_mediumtrust_WebService.config, which is located in the same directory.
%windir%\Microsoft.NET\Framework\{version}\CONFIG
Locate WebPermission and add a <URI> element for each server you will be accessing, as follows:
<IPermission class="WebPermission" version="1"> <ConnectAccess> <URI uri="$OriginHost$"/> <URI uri="http://server1/.*"/> <URI uri="http://server2/.*"/> <URI uri="http://server3/.*"/> </ConnectAccess> </IPermission>
If you call the Web service using its NetBIOS) name, DNS name, and/or IP address, you must have a separate <URI> element for each URI as shown in the following example.
<IPermission class="WebPermission" version="1"> <ConnectAccess> <URI uri="$OriginHost$"/> <URI uri="http://servername.yourDomain.com/.*"/> <URI uri="http:// servername/.*"/> <URI uri="http://127.0.0.1/.*"/> </ConnectAccess> </IPermission>
Save the file.
Update your application's Web.config file to point to the newly created policy file. This requires that you create a new trust level and map it to the new policy file. Next, configure the <trust> element of your application to use the new level.
The following fragment shows the necessary additions to Web.config:
<system.web> <securityPolicy> <trustLevel name="MediumPlusWebPermission" policyFile="web_mediumtrust_WebService.config"/> </securityPolicy> <trust level=" MediumPlusWebPermission" originUrl=""/> </system.web>
Using Default Credentials
You might need to call a Web service that uses Windows authentication and specify authentication credentials through the proxy credential cache. For example:
proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
In this case, the ASP.NET application requires the EnvironmentPermission with read access to the USERNAME environment variable. Default medium-trust policy grants this permission to Web applications.
In an ASP.NET server-side scenario, the credentials are obtained from the ASP.NET application's thread or process-level token. If DefaultCredentials are used from a desktop application, the current interactive user's token is used. The demand for EnvironmentPermission is a risk mitigation strategy designed to ensure that code cannot use the local user's credentials at will and expose them to the network.
Registry
By default, medium-trust Web applications are not granted the RegistryPermission. To configure your application to access the registry, you must either modify ASP.NET policy to grant this permission to your application or develop a sandboxed wrapper assembly that has the necessary permission.
The sandboxing approach is the same as described earlier for OLE DB data sources and the event log.
Customizing Policy
The easiest way to customize policy is to create a custom policy file based on the medium-trust policy file and configure your application to use the custom policy. The custom policy grants RegistryPermission to the application.
To create a custom policy to allow registry access
Copy the Web_mediumtrust.config file, which is in the following directory, to a file called Web_mediumtrust_Registry.config, which is located in the same directory.
%windir%\Microsoft.NET\Framework\{version}\CONFIG
By making a copy and creating a custom policy file, you avoid making changes directly to the Web_mediumtrust.config file. Making changes directly to the default medium trust file affects every application on the machine that is configured for medium trust.
Locate the <SecurityClasses> element and add the following to register the RegistryPermission class:
<SecurityClass Name="RegistryPermission" Description="System.Security.Permissions.RegistryPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
Locate the ASP.NET permission set and add the unrestricted RegistryPermission to the permission set as follows:
<IPermission class="RegistryPermission" version="1" Unrestricted="true" />
Save the file.
Update the Machine.config file to create a new trust level that is mapped to the new policy file.
<system.web> <securityPolicy> <trustLevel name="MediumPlusRegistry" policyFile="web_mediumtrust_Registry.config "/> </securityPolicy>
Update your application's Web.config to configure the application's <trust> level.
<system.web> <trust level="MediumPlusRegistry" originUrl=""/> </system.web>
Summary
Code access security is a resource constraint security model that can be used to help provide application isolation. Applications can be configured to run at various partial-trust levels. The trust level determines the permissions that are granted to the ASP.NET Web application or Web service. This determines the resource types that can be accessed, and the other types of privileged operation that can be performed. Note that all resource access is ultimately subject to operating system security.
The recommended isolation model uses IIS 6.0 application pools on Windows Server 2003 and provides process level isolation in addition to code access security. On Windows 2000, isolation can only be achieved using code access security and separate thread identities.
Migrating an application to run with partial trust usually requires a certain amount of reengineering. You might need to reengineer if the application accesses resources that are not permitted by the partial trust level or if it calls strong named assemblies that do not contain APTCA. In these cases, you can sandbox privileged resource access in separate wrapper assemblies. In some scenarios, you might be able to create and use custom policy files, although this depends on your Web server's security policy.
It is a good design practice to place resource access code in separate assemblies and avoid placing this code in .aspx files and code behind files. The use of separate assemblies allows code access security policy to be applied to the assembly independently from the Web application and it allows you to develop sandboxed trusted code to perform resource access.
Additional Resources
For more information, see the following resources:
- "Security in .NET: The Security Infrastructure of the CLR Provides Evidence, Policy, Permissions, and Enforcement Services" in MSDN Magazine at https://msdn.microsoft.com/en-us/magazine/cc188939.aspx.
- "Security in .NET: Enforce Code Access Rights with the Common Language Runtime" in MSDN Magazine at https://msdn.microsoft.com/en-us/magazine/cc301467.aspx.
- LaMacchia, Lange, Lyons, Martin, and Price. .NET Framework Security. Addison Wesley Professional, 2002.
- How To: Create a Custom Encryption Permission in the How To section of this guide.
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |