다음을 통해 공유


Chapter 8 – Code Access Security in Practice

 

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.

patterns & practices Developer Center

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:

  • .NET Framework version 1.1

See the "patterns & practices Security Guidance for Applications Index" for links to additional security resources.

See the Landing Page for the starting point and a complete overview of Improving Web Application Security: Threats and Countermeasures.

Summary: This chapter shows you with practical examples, how to use .NET Framework code access security to improve the security of your assemblies. It presents an overview of the code access security programming model and then shows you how to use code access security to improve the security of your managed code.

Contents

In This Chapter
Overview
How to Use This Chapter
Code Access Security Explained
APTCA
Privileged Code
Requesting Permissions
Authorizing Code
Link Demands
Assert and RevertAssert
Constraining Code
File I/O
Event Log
Registry
Data Access
Directory Services
Environment Variables
Web Services
Sockets and DNS
Unmanaged Code
Delegates
Serialization
Summary
Additional Resources

In This Chapter

  • Code access security explained
  • Using APTCA
  • Requesting permissions
  • Sandboxing privileged code
  • Authorizing code with identity demands
  • Serialization, delegates, and threading
  • Calling unmanaged code

Overview

Code access security is a resource constraint model designed to restrict the types of system resource that code can access and the types of privileged operation that the code can perform. These restrictions are independent of the user who calls the code or the user account under which the code runs.

Code access security delivers three main benefits. By using code access security, you can:

  • Restrict what your code can do

    For example, if you develop an assembly that performs file I/O you can use code access security to restrict your code's access to specific files or directories. This reduces the opportunities for an attacker to coerce your code to access arbitrary files.

  • Restrict which code can call your code

    For example, you may only want your assembly to be called by other code developed by your organization. One way to do this is to use the public key component of an assembly's strong name to apply this kind of restriction. This helps prevent malicious code from calling your code.

  • Identify code

    To successfully administer code access security policy and restrict what code can do, the code must be identifiable. Code access security uses evidence such as an assembly's strong name or its URL, or its computed hash to identify code (assemblies.)

How to Use This Chapter

This chapter takes up where Chapter 7, "Building Secure Assemblies," left off. It shows how you can use code access security to further improve the security of your managed code. To get the most out of this chapter:

  • Read Chapter 6, ".NET Security Fundamentals" for an overview and comparison of user (role)-based security versus code access security. Chapter 6 helps set the scene for the current chapter.
  • Read Chapter 7, "Building Secure Assemblies." Read Chapter 7 before this chapter if you have not already done so.
  • Read Chapter 9, "Using Code Access Security with ASP.NET." After you read this chapter, read Chapter 9 if you are interested specifically in ASP.NET code access security policy and ASP.NET trust levels.

Code Access Security Explained

To use code access security effectively, you need to know the basics such as the terminology and how policy is evaluated. For further background information about code access security, see the "Additional Resources" section at the end of this chapter. If you are already familiar with code access security, you may want to skip this section and go to the "APTCA" (AllowPartiallyTrustedCallersAttribute) section later in this chapter.

Code access security consists of the following elements:

  • Code
  • Evidence
  • Permissions
  • Policy
  • Codegroups

Code

All managed code is subject to code access security. When an assembly is loaded, it is granted a set of code access permissions that determines what resource types it can access and what types of privileged operations it can perform. The Microsoft .NET Framework security system uses evidence to authenticate (identify) code to grant permissions.

Note   An assembly is the unit of configuration and trust for code access security. All code in the same assembly receives the same permission grant and is therefore equally trusted.

Evidence

Evidence is used by the .NET Framework security system to identify assemblies. Code access security policy uses evidence to help grant the right permissions to the right assembly. Location-related evidence includes:

  • URL. The URL that the assembly was obtained from. This is the codebase URL in its raw form, for example, http://webserver/vdir/bin/assembly.dll or file://C:/directory1/directory2/assembly.dll.
  • Site. The site the assembly was obtained from, for example, http://webserver. The site is derived from the codebase URL.
  • Applicationdirectory. The base directory for the running application.
  • Zone. The zone the assembly was obtained from, for example, LocalIntranet or Internet. The zone is also derived from the codebase URL.

Author-related evidence includes:

  • Strong name. This applies to assemblies with a strong name. Strong names are one way to digitally sign an assembly using a private key.

  • Publisher. The Authenticode signature; based on the X.509 certificate used to sign code, representing the development organization.

    Important   Publisher evidence (the Authenticode signature) is ignored by the ASP.NET host and therefore cannot be used to configure code access security policy for server-side Web applications. This evidence is primarily used by the Internet Explorer host.

  • Hash. The assembly hash is based on the overall content of the assembly and allows you to detect a particular compilation of a piece of code, independent of version number. This is useful for detecting when third party assemblies change (without an updated version number) and you have not tested and authorized their use for your build.

Permissions

Permissions represent the rights for code to access a secured resource or perform a privileged operation. The .NET Framework provides codeaccesspermissions and codeidentitypermissions. Code access permissions encapsulate the ability to access a particular resource or perform a privileged operation. Code identity permissions are used to restrict access to code, based on an aspect of the calling code's identity such as its strong name.

Your code is granted permissions by code access security policy that is configured by the administrator. An assembly can also affect the set of permissions that it is ultimately granted by using permission requests. Together, code access security policy and permission requests determine what your code can do. For example, code must be granted the FileIOPermission to access the file system, and code must be granted the RegistryPermission to access the registry. For more information about permission requests, see the "Requesting Permissions" section later in this chapter.

Note   Permission sets are used to group permissions together to ease administration.

Restricted and Unrestricted Permissions

Permissions can be restricted or unrestricted. For example, in its unrestricted state, the FileIOPermission allows code to read or write to any part of the file system. In a restricted state, it might allow code to read files only from a specific directory.

Demands

If you use a class from the .NET Framework class library to access a resource or perform another privileged operation, the class issues a permission demand to ensure that your code, and any code that calls your code, is authorized to access the resource. A permission demand causes the runtime to walk back up through the call stack (stack frame by stack frame), examining the permissions of each caller in the stack. If any caller is found not to have the required permission, a SecurityException is thrown.

A link demand does not perform a full stack walk and only checks the immediate caller, one stack frame further back in the call stack. As a result, there are additional security risks associated with using link demands. You need to be particularly sensitive to luring attacks.

Note   With a luring attack, malicious code accesses the resources and operations that are exposed by your assembly, by calling your code through a trusted intermediary assembly.

For more information about how to use link demands correctly, see the "Link Demands" section later in this chapter.

Assert, Deny, and PermitOnly Methods

Code access permission classes support the Assert, Deny, and PermitOnly methods. You can use these methods to alter the behavior of a permission demand stack walk. They are referred to as stack walk modifiers.

A call to the Assert method causes the stack walk for a matching permission to stop at the site of the Assert call. This is most often used to sandbox privileged code. For more information, see the "Assert and RevertAssert" section later in this chapter.

A call to the Deny method fails any stack walk that reaches it with a matching permission. If you call some non-trusted code, you can use the Deny method to constrain the capabilities of the code that you call.

A call to the PermitOnly method fails any unmatching stack walk. Like the Deny method, it tends to be used infrequently but it can be used to constrain the actions of some non-trusted code that you may call.

Policy

Code access security policy is configured by administrators and it determines the permissions granted to assemblies. Policy can be established at four levels:

  • Enterprise. Used to apply Enterprise-wide policy.

  • Machine. Used to apply machine-level policy.

  • User. Used to apply per user policy.

  • ApplicationDomain. Used to configure the application domain into which an assembly is loaded.

    ASP.NET implements application domain policy to allow you to configure code access security policy for Web applications and Web services. For more information about ASP.NET application domain policy, see Chapter 9, "Using Code Access Security with ASP.NET."

Policy settings are maintained in XML configuration files. The first three levels of policy (Enterprise, Machine, and User) can be configured by using the .NET Framework Configuration tool, which is located in the Administrative Tools program group or the Caspol.exe command line utility. ASP.NET application domain level policy must currently be edited with a text or XML-based editor.

For more information about policy files and locations, see Chapter 19, "Securing Your ASP.NET Application and Web Services."

Code Groups

Each policy file contains a hierarchical collection of code groups. Code groups are used to assign permissions to assemblies. A code group consists of two elements:

  • A membership condition. This is based on evidence, for example, an assembly's zone or its strong name.
  • A permission set. The permissions contained in the permission set are granted to assemblies whose evidence matches the membership condition.

How Does It Work?

Figure 8.1 shows a simplified overview of code access security.

Ff648663.f08thcm01(en-us,PandP.10).gif

Figure 8.1

Code access security*—*a simplified view

The steps shown in Figure 8.1 are summarized below.

  1. An assembly is loaded.

    This operation is performed by an application domain host. On a Web server loading a Web application assembly, this is the ASP.NET host.

  2. Evidence is gathered from the assembly and presented by the host.

  3. Evidence is evaluated against the defined security policy.

  4. The output from security policy evaluation is one or more named permission sets that define the permission grant for the assembly.

    Note   An assembly can include permission requests, which can further reduce the permission grant.

  5. Code within the assembly demands an appropriate permission prior to accessing a restricted resource or performing a privileged operation.

    All of the .NET Framework base classes that access resources or perform privileged operations contain the appropriate permission demands. For example, the FileStream class demands the FileIOPermission, the Registry class demands the RegistryPermission, and so on.

  6. If the assembly (and its callers) have been granted the demanded permission, the operation is allowed to proceed. Otherwise, a security exception is generated.

How Is Policy Evaluated?

When evidence is run through the policy engine, the output is a permission set that defines the set of permissions granted to an assembly. The policy grant is calculated at each level in the policy hierarchy: Enterprise, Machine, User, and Application Domain. The policy grant resulting from each level is then combined using an intersection operation to yield the final policy grant. An intersection is used to ensure that policy lower down in the hierarchy cannot add permissions that were not granted by a higher level. This prevents an individual user or application domain from granting additional permissions that are not granted by the Enterprise administrator.

Figure 8.2 shows how the intersection operation means that the resulting permission grant is determined by all levels of policy in the policy hierarchy.

Ff648663.f08thcm02(en-us,PandP.10).gif

Figure 8.2

Policy intersection across policy levels

In Figure 8.2, you can see that the intersection operation ensures that only those permissions granted by each level form part of the final permission grant.

How Do Permission Requests Affect the Policy Grant?

You can add security attributes to your assembly to specify its permission requirements. You can specify the minimal set of permissions that your assembly must be granted in order to run. These do not affect the permission grant. You can also specify the optional permissions your assembly could make use of but does not absolutely require, and what permissions you want to refuse. Refused permissions are those permissions you want to ensure your assembly never has, even if they are granted by security policy.

If you request optional permissions, the combined optional and minimal permissions are intersected with the policy grant, to further reduce it. Then, any specifically refused permissions are taken away from the policy grant. This is summarized by the following formula where PG is the policy grant from administrator defined security policy and Pmin , Popt , and Prefused are permission requests added to the assembly by the developer.

Resulting Permission Grant = (PG(PminPopt))Prefused

For more information about how to use permission requests, their implications, and when to use them, see the "Requesting Permissions" section later in this chapter.

Policy Evaluation at a Policy Level

An individual policy file at each specific level consists of a hierarchical arrangement of code groups. These code groups include membership conditions that are used to determine which assemblies they apply to, and permission sets that are used to determine the permissions that should be granted to matching assemblies. A hierarchical structure enables multiple permission sets to be assigned to an assembly, and it allows security policy to support simple AND and OR logic. For example, consider the sample security policy shown in Figure 8.3.

Ff648663.f08thcm03(en-us,PandP.10).gif

Figure 8.3

Hierarchical code groups at a single policy level

Note   The AllCode code group is a special code group that matches all assemblies. It forms the root of security policy and in itself grants no permissions, because it is associated with the permission set named Nothing.

Consider the granted permissions based on the security policy shown in Figure 8.3.

  • Any assembly originating from the My_Computer_Zone (any locally installed assembly), is granted the permissions defined by the FullTrust permission set. This is a built-in permission set defined when the .NET Framework is installed and represents the unrestricted set of all permissions.
  • Assemblies authored by Company1 and originating from the intranet zone are granted the permissions defined by the built-in LocalIntranet_Zone permission set and the custom Comp1PSet permission set.
  • Assemblies authored by Company2 are granted permissions defined by the custom Comp2PSet permission set.
  • Any assembly downloaded from a.b.c.com is granted permissions defined by the custom ABCPSet permission set.

Note   If the membership condition for a particular code group is not satisfied, none of its children are evaluated.

Exclusive and Level Final Code Groups

Policy hierarchy processing and traversal can be fine-tuned using a couple of attributes specified at the code group level, both of which can be set through the .NET Framework Configuration Tool. These are:

  • Exclusive

    This indicates that no other sibling code groups should be combined with this code group. You mark a code group as exclusive by selecting This policy level will only have the permissions from the permission set associated with this code group in the .NET Framework Configuration Tool.

  • LevelFinal

    This indicates that any lower level policies should be ignored. You mark a code group as Level Final by selecting Policy levels below this level will not be evaluated in the .NET Framework Configuration Tool. For example, if a matching code group in the machine policy is marked LevelFinal, policy settings from the user policy file is ignored.

    Note   The application domain level policy, for example, ASP.NET policy for server-side Web applications, is always evaluated regardless of the level final setting.

APTCA

An assembly that has a strong name cannot be called by a partial trust assembly (an assembly that is not granted full trust), unless the strong named assembly contains AllowPartiallyTrustedCallersAttribute (APTCA) as follows:

[assembly: AllowPartiallyTrustedCallersAttribute()]

This is a risk mitigation strategy designed to ensure your code cannot inadvertently be exposed to partial trust (potentially malicious) code. The common language runtime silently adds a link demand for the FullTrust permission set to all publicly accessible members on types in a strong named assembly. If you include APTCA, you suppress this link demand.

Avoid Using APTCA

If you use APTCA, your code is immediately more vulnerable to attack and, as a result, it is particularly important to review your code for security vulnerabilities. Use APTCA only where it is strictly necessary.

In the context of server-side Web applications, use APTCA whenever your assembly needs to support partial trust callers. This situation can occur in the following circumstances:

  • Your assembly is to be called by a partial trust Web application. These are applications for which the <trust> level is set to something other than Full. For more information about partial trust Web applications and using APTCA in this situation, see Chapter 9, "Using Code Access Security with ASP.NET."
  • Your assembly is to be called by another assembly that has been granted limited permissions by the code access security administrator.
  • Your assembly is to be called by another assembly that refuses specific permissions by using SecurityAction.RequestRefuse or SecurityAction.RequestOptional. These make the calling assembly a partial trust assembly.
  • Your assembly is to be called by another assembly that uses a stack walk modifier (such as Deny or PermitOnly) to constrain downstream code.

Diagnosing APTCA Issues

If you attempt to call a strong named assembly that is not marked with APTCA from partial trust code such as a partial trust Web application, you see an exception similar to the one shown in Figure 8.4. Notice that the exception details provide no permission details and simply indicate that the required permissions (in this case, FullTrust) cannot be acquired from the calling assembly. In this case, the somewhat confusing description text means that the error occurred because the application's <trust> level was set to something other than Full.

Ff648663.f08thcm04(en-us,PandP.10).gif

Figure 8.4

The result of partial trust code calling a strong named assembly

To overcome this exception, either the calling code must be granted FullTrust or the assembly being called must be annotated with APTCA. Note that individual types within an assembly marked with APTCA might still require full trust callers, because they include an explicit link demand or regular demand for full trust, as shown in the following examples.

[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
[PermissionSet(SecurityAction.Demand, Unrestricted=true)]

Privileged Code

When you design and build secure assemblies, you must be able to identify privileged code. This has important implications for code access security. Privileged code is managed code that accesses secured resources or performs other security-sensitive operations, such as calling unmanaged code, using serialization, or using reflection. Privileged code is privileged because code access security must grant it specific permissions before it can function.

Privileged Resources

Privileged resources for which your code requires specific code access security permissions are shown in the Table 8.1.

Table 8.1   Secure Resources and Associated Permissions

Secure Resource Requires Permission
Data access SqlClientPermission

OleDbPermission

OraclePermission

Note   The ADO.NET OLE DB and Oracle-managed providers currently require full trust.

Directory services DirectoryServicesPermission
DNS databases DnsPermission
Event log EventLogPermission
Environment variables EnvironmentPermission
File system FileIOPermission
Isolated storage IsolatedStoragePermission
Message queues MessageQueuePermission
Performance counters PerformanceCounterPermission
Printers PrinterPermission
Registry RegistryPermission
Sockets SocketPermission
Web services (and other HTTP Internet resources) WebPermission

Privileged Operations

Privileged operations are shown in Table 8.2, together with the associated permissions that calling code requires.

Table 8.2   Privileged Operations and Associated Permissions

Operation Requires Permission
Creating and controlling application domains SecurityPermission with SecurityPermissionFlag.ControlAppDomain
Specifying policy application domains SecurityPermission with SecurityPermissionFlag.ControlDomainPolicy
Asserting security permissions SecurityPermission with SecurityPermissionFlag.Assertion
Creating and manipulating evidence SecurityPermission with SecurityPermissionFlag.ControlEvidence
Creating and manipulating principal objects SecurityPermission with SecurityPermissionFlag.ControlPrincipal
Configuring types and channels remoting SecurityPermission with SecurityPermissionFlag.RemotingConfiguration
Manipulating security policy SecurityPermission with SecurityPermissionFlag.ControlPolicy
Serialization SecurityPermission with SecurityPermissionFlag.SerializationFormatter
Threading operations SecurityPermission with SecurityPermissionFlag.ControlThread
Reflection ReflectionPermission
Calling unmanaged code SecurityPermission with SecurityPermissionFlag.UnmanagedCode

Requesting Permissions

When you design and develop your assemblies, create a list of all the resources that your code accesses, and all the privileged operations that your code performs. At deployment time, the administrator may need this information to appropriately configure code access security policy and to diagnose security related problems.

The best way to communicate the permission requirements of your code is to use assembly level declarative security attributes to specify minimum permission requirements. These are normally placed in Assemblyinfo.cs or Assemblyinfo.vb. This allows the administrator or the consumer of your assembly to check which permissions it requires by using the Permview.exe tool.

RequestMinimum

You can use SecurityAction.RequestMinimum method along with declarative permission attributes to specify the minimum set of permissions your assembly needs to run. For example, if your assembly needs to access the registry, but only needs to retrieve configuration data from a specific key, use an attribute similar to the following:

[assembly: RegistryPermissionAttribute(
                     SecurityAction.RequestMinimum, 
                     Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]

If you know up front that your code will run in a full trust environment and will be granted the full set of unrestricted permissions, using RequestMinimum is less important. However, it is good practice to specify your assembly's permission requirements.

Note   Permission attributes accept a comma-delimited list of properties and property values after the mandatory constructor arguments. These are used to initialize the underlying permission object. A quick way to find out what property names are supported is to use Ildasm.exe on the assembly that contains the permission attribute type.

RequestOptional

If you use SecurityAction.RequestOptional method, no other permissions except those specified with SecurityAction.RequestMinimum and SecurityAction.RequestOptional will be granted to your assembly, even if your assembly would otherwise have been granted additional permissions by code access security policy.

RequestRefused

SecurityAction.RequestRefuse allows you to make sure that your assembly cannot be granted permissions by code access security policy that it does not require. For example, if your assembly does not call unmanaged code, you could use the following attribute to ensure code access security policy does not grant your assembly the unmanaged code permission.

[assembly: SecurityPermissionAttribute(SecurityAction.RequestRefuse, 
                                       UnmanagedCode=true)]

Implications of Using RequestOptional or RequestRefuse

If you use RequestOptional, the set of permissions that are specified with RequestOptional and RequestMinimum are intersected with the permission grant given to your assembly by policy. This means that all other permissions outside of the RequestOptional and RequestMinimum sets are removed from your assembly's permission grant. Additionally, if you use RequestRefuse, the refused permissions are also removed from your assembly's permission grant.

So if you use RequestOptional or RequestRefuse, your assembly becomes a partial trust assembly, which has implications when you call other assemblies. Use the following considerations to help you decide whether you should use SecurityAction.RequestOptional or SecurityAction.RequestRefuse:

  • Do not use them if you need to directly call a strong named assembly without AllowPartiallyTrustedCallersAttribute (APTCA) because this prevents you from being able to call it.

    Many strong named .NET Framework assemblies contain types that do not support partial trust callers and do not include APTCA. For more information, and a list of assemblies that support partial trust callers, see "Developing Partial Trust Web Applications," in Chapter 9, "Using Code Access Security with ASP.NET."

    If you must call strong named assemblies without APTCA, let the administrators who install your code know that your code must be granted full trust by code access security policy to work properly.

  • If you do not need to access any APTCA assemblies, then add permission requests to refuse those permissions that you know your assembly does not need. Test your code early to make sure you really do not require those permissions.

  • If downstream code needs the permission you have refused, a method between you and the downstream code needs to assert the permission. Otherwise, a SecurityException will be generated when the stack walk reaches your code.

Authorizing Code

Code access security allows you to authorize the code that calls your assembly. This reduces the risk of malicious code successfully calling your code. For example, you can use identity permissions to restrict calling code based on identity evidence, such as the public key component of its strong name. You can also use explicit code access permission demands to ensure that the code that calls your assembly has the necessary permissions to access the resource or perform the privileged operation that your assembly exposes.

Usually, you do not explicitly demand code access permissions. The .NET Framework classes do this for you, and a duplicate demand is unnecessary. However, there are occasions when you need to issue explicit demands, for example, if your code exposes a custom resource by using unmanaged code or if your code accesses cached data. You can authorize code in the following ways:

  • Restrict which code can call your code.
  • Restrict inheritance.
  • Consider protecting cached data.
  • Protect custom resources with custom permissions.

Restrict Which Code Can Call Your Code

A method marked as public can be called by any code outside of the current assembly. To further restrict which other code can call your methods, you can use a code access security identity permission demand as shown in the following example.

public sealed class Utility
{
  // Although SomeOperation() is a public method, the following 
  // permission demand means that it can only be called by assemblies 
  // with the specified public key. 
  [StrongNameIdentityPermission(SecurityAction.LinkDemand, 
                                PublicKey="00240000048...97e85d098615")]
  public static void SomeOperation() {}
}

The above code shows a link demand. This results in the authorization of the immediate caller. Therefore, your code is potentially open to luring attacks, where a malicious assembly could potentially access the protected resources or operations provided by your assembly through a trusted intermediary assembly with the specified strong name.

Depending on the nature of the functionality provided by your class, you may need to demand another permission to authorize the calling code in addition to using the identity-based link demand. Alternatively, you can consider using a full demand in conjunction with the StrongNameIdentityPermission, although this assumes that all code in the call stack is strong name signed using the same private key.

Note   Issuing a full stack walk demand for the StrongNameIdentityPermission does not work if your assembly is called by a Web application or Web service. This is because it is not possible to strong name the dynamically compiled classes associated with ASP.NET Web applications or Web services.

Note   In .NET 2.0, the ASP.NET Web applications can be precompiled by using the Visual Studio 2005 or aspnet_compiler.exe command line utility. Therefore, it is now possible to strong name the ASP.NET applications. For more information, see "How to: Sign Assemblies for Precompiled Web Sites."

In .NET 2.0, StrongNameIdentityPermission only works for partial trust callers. Any demand, including a link demand, will always succeed for full trust callers regardless of the strong name of the calling code.

To extract a public key from an assembly

  • Run the following command to obtain a hex representation of a public key from an assembly:

    secutil -hex -strongname yourassembly.dll
    

To extract the public key from a key pair file

  1. Generate the key pair file with the following command:

    sn -k keypairfile
    
  2. Extract the public key from the key pair file:

    sn -p keypairfile publickeyfile
    
  3. Obtain a hex representation of the public key:

    sn -tp publickeyfile > publickeyhex.dat
    

Restrict Inheritance

If your class is designed as base class, you can restrict which other code is allowed to derive from your class by using an inheritance demand coupled with a StrongNameIdentityPermission as shown in the following example. This prevents inheritance of your class from any assembly that is not signed with the private key corresponding to the public key in the demand.

// The following inheritance demand ensures that only code within the 
// assembly with the specified public key (part of the assembly's strong
// name can sub class SomeRestrictedClass
[StrongNameIdentityPermission(SecurityAction.InheritanceDemand,
                              PublicKey="00240000048...97e85d098615")]
public class SomeRestrictedClass
{
}

Note   In .NET 2.0, StrongNameIdentityPermission only works for partial trust callers. Any demand, including a link demand, will always succeed for full trust callers regardless of the strong name of the calling code.

Consider Protecting Cached Data

If you access a resource by using one of the .NET Framework classes, a permission demand appropriate for the resource type in question is issued by the class. If you subsequently cache data for performance reasons, you should consider issuing an explicit code access permission demand prior to accessing the cached data. This ensures the calling code is authorized to access the specific type of resource. For example, if you read data from a file and then cache it, and you want to ensure that calling code is authorized, issue a FileIOPermission demand as shown in the following example.

// The following demand assumes the cached data was originally retrieved from
// C:\SomeDir\SomeFile.dat
new FileIOPermission(FileIOPermissionAccess.Read, 
                     @"C:\SomeDir\SomeFile.dat").Demand();
// Now access the cache and return the data to the caller

Protect Custom Resources with Custom Permissions

If you expose a resource or operation by using unmanaged code, you should sandbox your wrapper code and consider demanding a custom permission to authorize the calling code.

Full trust callers are granted the custom permission automatically as long as the permission type implements the IUnrestrictedPermission interface. Partial trust callers will not have the permission unless it has been specifically granted by code access security policy. This ensures that non-trusted code cannot call your assembly to access the custom resources that it exposes. Sandboxing also means that you are not forced to grant the powerful UnmanagedCodePermission to any code that needs to call your code.

For more information about calling unmanaged code, see the "Unmanaged Code" section later in this chapter. For an example implementation of a custom permission, see "How To: Create a Custom Encryption Permission" in the "How To" section of this guide.

A link demand differs from a regular permission demand in that the run-time demands permissions only from the immediate caller and does not perform a full stack walk. Link demands are performed at JIT compilation time and can only be specified declaratively.

Carefully consider before using a link demand because it is easy to introduce security vulnerabilities if you use them. If you do use link demands, consider the following issues:

  • Luring attacks
  • Performance and link demands
  • Calling methods with link demands
  • Mixing class and method level link demands
  • Interfaces and link demands
  • Structures and link demands
  • Virtual methods and link demands

Luring Attacks

If you protect code with a link demand, it is vulnerable to luring attacks, where malicious code gains access to the resource or operation exposed by your code through a trusted intermediary as shown in Figure 8.5.

Ff648663.f08thcm05(en-us,PandP.10).gif

Figure 8.5

An example of a luring attack with link demands

In figure 8.5, methods in assembly X, which access a secure resource, are protected with a link demand for a specific public key (using a StrongNameIdentityPermission). Assemblies A, B, and C are signed with the private key that corresponds to the public key that assembly X trusts, and so these assemblies can call assembly X. Assemblies A, B, and C are subject to a luring attack if they do not check their callers for specific evidence before making calls to assembly X. For example, assembly D that is not signed with the same private key cannot call assembly X directly. It could, however, access assembly X through the trusted assembly A, if A does not check its callers, either with another link demand or through a full demand.

Only use link demands in an assembly when you trust the assembly's callers not to expose its functionality further (for example, when the caller is an application, not a library) or when you know it is safe just to verify the immediate caller's identity with an identity permission demand.

Note   In .NET 2.0, StrongNameIdentityPermission only works for partial trust callers. Any demand, including a link demand, will always succeed for full trust callers regardless of the strong name of the calling code.

Compared to other Web application performance issues such as network latency and database access, the cost of a stack walk is small. Do not use link demands purely for performance reasons. Full demands provide a much greater degree of security.

If you call a link demand protected method, only your code will be checked by the link demand. In this situation, you should make sure your code takes adequate measures to authorize its callers, for example, by demanding a permission.

Method level link demands override class level link demands. For example, in the following code fragment, the link demand for FileIOPermission must be repeated on the method declaration or the EnvironmentPermission link demand replaces the class level FileIOPermission demand.

[FileIOPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public sealed class SomeClass
{
  // The unrestricted FileIOPermission link demand must be restated at the
  // method level, if the method is decorated with another link demand.
  // Failure to do so means that (in this example) that the 
  // EnvironmentPermission link demand would override the class level
  // FileIOPermission link demand
  [FileIOPermission(SecurityAction.LinkDemand, Unrestricted=true)]
  [EnvironmentPermission(SecurityAction.LinkDemand, Read="PATH")]
  public void SomeMethod()
  {
  }
}

If your class implements an interface and one of the method implementations has a link demand, make sure that the method declaration on the interface definition has the same link demand. Otherwise, the caller simply has to call your method through the interface to bypass the link demand. An example is shown below.

public interface IMyInterface
{
  // The link demand shown on the method implementation below 
  // should be repeated here
  void Method1();
}

public class MyImplementation : IMyInterface
{
  // The method implementation has a link demand but the interface does not
  [SecurityPermission(SecurityAction.LinkDemand, 
            Flags=SecurityPermissionFlag.ControlPrincipal)]
  public void Method1()
  {
  }
}

With the following code, the caller is subject to the link demand:

MyImplementation t = new MyImplementation();
t.Method1();

With the following code, the caller is not subject to the link demand:

IMyInterface i = new MyImplementation();
i.Method1();

Link demands do not prevent the construction of structures by untrusted callers. This is because default constructors are not automatically generated for structures. Therefore, the structure level link demand only applies if you use an explicit constructor.

For example:

[SecurityPermission(SecurityAction.LinkDemand, 
           Flags=SecurityPermissionFlag.ControlPrincipal)]
public struct SomeStruct
{
  // This explicit constructor is protected by the link demand
  public SomeStruct(int i)
  {
    field = i;
  }
  private int field;
}

The following two lines of code both result in a new structure with the field initialized to zero. However, only the first line that uses the explicit constructor is subject to a link demand.

SomeStruct s = new SomeStruct(0);
SomeStruct s = new SomeStruct();

The second line is not subject to a link demand because a default constructor is not generated. If this were a class instead of a structure, the compiler would generate a default constructor annotated with the specified link demand.

If you use link demand to protect a method override in a derived class, make sure you also put it on the corresponding virtual base class method. Otherwise, if the JIT compiler sees a reference to the base class type where no link demand is present, no link demand is performed.

Assert and RevertAssert

You can call the CodeAccessPermission.Assert method to prevent a demand propagating beyond the current stack frame. By using Assert, you vouch for the trustworthiness of your code's callers. Because of the potential for luring attacks, Assert needs to be used with caution.

Asserts are most often used to sandbox privileged code. If you develop code that calls Assert, you need to ensure that there are alternate security measures in place to authorize the calling code. The following recommendations help you to minimize the risks.

  • Use the demand / assert pattern
  • Reduce the Assert duration

Use the Demand / Assert Pattern

Demanding a specific permission before calling Assert is an effective way to authorize upstream code. Sometimes you might be able to demand a built-in permission type to authorize calling code.

Often, if your assembly is exposing functionality that is not provided by the .NET Framework class library, such as calling the Data Protection API (DPAPI), you need to develop a custom permission and demand the custom permission to authorize callers. For example, you might develop a custom Encryption permission to authorize callers to a managed DPAPI wrapper assembly. Demanding this permission and then asserting the unmanaged code permission is an effective way to authorize calling code.

For more information about this approach and about developing custom permissions, see "How To: Create a Custom Encryption Permission" in the "How To" section of this guide.

Reduce the Assert Duration

If you only need to call Assert to satisfy the demands of a single downstream method that your code calls, then place the Assert immediately prior to the downstream method call. Then immediately call RevertAssert to keep the assertion window as small as possible, and to ensure that any subsequent code your method calls does not inadvertently succeed because the Assert is still in effect.

A common practice is to place the call to RevertAssert in a finally block to ensure that it always gets called even in the event of an exception.

Constraining Code

Constraining code and building least privileged code is analogous to using the principle of least privilege when you configure user or service accounts. By restricting the code access security permissions available to your code, you minimize scope for the malicious use of your code.

There are two ways to constrain code to restrict which resources it can access and restrict which other privileged operations it can perform:

  • Using policy permission grants
  • Using stack walk modifiers

Using Policy Permission Grants

You can configure code access security policy to grant a restricted permission set to a specific assembly. This constrains its ability to access resources or perform other privileged operations. For more information, see "How To: Configure Code Access Security Policy to Constrain an Assembly" in the "How To" section of this guide.

Using Stack Walk Modifiers

You can use stack walk modifiers to ensure that only specific permissions are available to the code that you call. For example, you can use SecurityAction.PermitOnly to ensure that your method and any methods that are called only have a restricted permission set. The following example applies a very restrictive permission set. The code only has the permission to execute. It cannot access resources or perform other privileged operations.

[SecurityPermissionAttribute(SecurityAction.PermitOnly, 
                             Flags=SecurityPermissionFlag.Execution)]
public void SomeMethod()
{
  // The current method and downstream can only execute. They cannot access 
  // resources or perform other privileged operations.
  SomeOtherMethod();
}

The following sections show you how to use code access security to constrain various types of resource access including file I/O, event log, registry, data access, directory services, environment variables, Web services, and sockets.

File I/O

To be able to perform file I/O, your assembly must be granted the FileIOPermission by code access security policy. If your code is granted the unrestricted FileIOPermission, it can access files anywhere on the file system, subject to Windows security. A restricted FileIOPermission can be used to constrain an assembly's ability to perform file I/O, for example, by specifying allowed access rights (read, read/write, and so on.)

Constraining File I/O within your Application's Context

A common requirement is to be able to restrict file I/O to specific directory locations such as your application's directory hierarchy.

Note   If your Web application is configured for Medium trust, file access is automatically restricted to the application's virtual directory hierarchy. For more information, see Chapter 9, "Using Code Access Security with ASP.NET."

Configuring your application for Medium trust is one way to constrain file I/O, although this also constrains your application's ability to access other resource types. There are two other ways you can restrict your code's file I/O capabilities:

  • Using PermitOnly to restrict File I/O
  • Configuring code access security policy to restrict File I/O

Using PermitOnly to Restrict File I/O

You can use declarative attributes together with SecurityAction.PermitOnly as shown in the following example to constrain file I/O.

// Allow the code only to read files from c:\YourAppDir
[FileIOPermission(SecurityAction.PermitOnly, Read=@"c:\YourAppDir\")]
[FileIOPermission(SecurityAction.PermitOnly, PathDiscovery=@"c:\YourAppDir\")]
public static string ReadFile(string filename)
{
  // Use Path.GetFilePath() to canonicalize the file name
  // Use FileStream.OpenRead to open the file
  // Use FileStream.Read to access and return the data
}

Note   The second attribute that specifies PathDicovery access is required by the Path.GetFilePath function that is used to canonicalize the input file name.

To avoid hard coding your application's directory hierarchy, you can use imperative security syntax, and use the HttpContext.Current.Request.MapPath(".") to retrieve your Web application's directory at runtime. You must reference the System.Web assembly and add the corresponding using statement as shown in the following example.

using System.Web;

public static string ReadFile(string filename)
{
  string appDir = HttpContext.Current.Request.MapPath(".");
  FileIOPermission f = new FileIOPermission(PermissionState.None);
  f.SetPathList(FileIOPermissionAccess.Read, appDir);
  f.SetPathList(FileIOPermissionAccess.PathDiscovery, appDir);
  f.PermitOnly();

  // Use Path.GetFilePath() to canonicalize the file name
  // Use FileStream.OpenRead to open the file
  // Use FileStream.Read to access and return the data
}

Note   For a Windows application you can replace the call to MapPath with a call to Directory.GetCurrentDirectory to obtain the application's current directory.

Configuring Code Access Security Policy to Restrict File I/O

An administrator can also configure code access security policy to restrict your code's ability to perform file I/O beyond your application's virtual directory hierarchy.

For example, the administrator can configure Enterprise or Machine level code access security policy to grant a restricted FileIOPermission to your assembly. This is most easily done if your assembly contains a strong name, because the administrator can use this cryptographically strong evidence when configuring policy. For assemblies that are not strong named, an alternative form of evidence needs to be used. For more information about how to configure code access security to restrict the file I/O capability of an assembly, see "How To: Configure Code Access Security Policy to Constrain an Assembly, " in the "How To" section of this guide.

If your assembly is called by a Web application, a better approach is to configure ASP.NET (application domain-level) code access security policy because you can use $AppDirUrl$ which represents the application's virtual directory root. For more information about restricting File I/O using ASP.NET code access security policy, see Chapter 9, "Using Code Access Security with ASP.NET."

Requesting FileIOPermission

To help the administrator, if you know your assembly's precise file I/O requirements at build time (for example, you know directory names), declare your assembly's FileIOPermission requirements by using a declarative permission request as shown in the following example.

[assembly: FileIOPermission(SecurityAction.RequestMinimum, Read=@"C:\YourAppDir")]

The administration can see this attribute by using permview.exe. The additional advantage of using SecurityAction.RequestMinimum is that the assembly fails to load if it is not granted sufficient permissions. This is preferable to a runtime security exception.

Event Log

To be able to access the event log, your assembly must be granted the EventLogPermission by code access security policy. If it is not, for example, because it is running within the context of a medium trust Web application, you need to sandbox your event logging code. For more information about sandboxing access to the event log, see Chapter 9, "Using Code Access Security with ASP.NET."

Constraining Event Logging Code

If you want to constrain the actions of event log wrapper code — perhaps code written by another developer or development organization — you can use declarative attributes together with SecurityAction.PermitOnly as shown in the following example.

The following attribute ensures that the WriteToLog method and any methods it calls can only access the local computer's event log and cannot delete event logs or event sources. These operations are not permitted by EventLogPermissionAccess.Instrument.

[EventLogPermission(SecurityAction.PermitOnly, 
                    MachineName=".",
                    PermissionAccess=EventLogPermissionAccess.Instrument)]
public static void WriteToLog( string message )

To enforce read-only access to existing logs, use EventLogPermissionAccess.Browse.

Requesting EventLogPermission

To document the permission requirements of your code, and to ensure that your assembly cannot load if it is granted insufficient event log access by code access security policy, add an assembly level EventLogPermissionAttribute with SecurityAction.RequestMinimum as shown in the following example.

// This attribute indicates that your code requires the ability to access the
// event logs on the local machine only (".") and needs instrumentation access
// which means it can read or write to existing logs and create new event sources 
// and event logs
[assembly: EventLogPermissionAttribute(SecurityAction.RequestMinimum,
                                       MachineName=".",
                                       PermissionAccess=
                                       EventLogPermissionAccess.Instrument)]

Registry

Code that accesses the registry by using the Microsoft.Win32.Registry class must be granted the RegistryPermission by code access security policy. This permission type can be used to constrain registry access to specific keys and sub keys, and can also control code's ability to read, write, or create registry keys and named values.

Constraining Registry Access

To constrain code to reading data from specific registry keys, you can use the RegistryPermissionAttribute together with SecurityAction.PermitOnly. The following attribute ensures that the code can only read from the YourApp key (and subkeys) beneath HKEY_LOCAL_MACHINE\SOFTWARE.

[RegistryPermissionAttribute(SecurityAction.PermitOnly,
                             Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]
public static string GetConfigurationData( string key, string namedValue )
{
  return (string)Registry.
                 LocalMachine.
                 OpenSubKey(key).
                 GetValue(namedValue);
}

Requesting RegistryPermission

To document the permission requirements of your code, and to ensure your assembly cannot load if it is granted insufficient registry access from code access security policy, add an assembly level RegistryPermissionAttribute with SecurityAction.RequestMinimum as shown in the following example.

[assembly: RegistryPermissionAttribute(SecurityAction.RequestMinimum,
                                   Read=@"HKEY_LOCAL_MACHINE\SOFTWARE\YourApp")]

Data Access

The ADO.NET SQL Server data provider supports partial trust callers. The other data providers including the OLE DB, Oracle, and ODBC providers currently require full trust callers.

Note   In ADO.NET 2.0, the OLE DB, Oracle and ODBC .NET data providers no longer require full trust. To use these providers from a partial trust Web application, see How To: Use Medium Trust in ASP.NET 2.0.

If you connect to SQL Server using the SQL Server data provider, your data access code requires the SqlClientPermission. You can use SqlClientPermission to restrict the allowable range of name/value pairs that can be used on a connection string passed to the SqlConnection object. In the following code, the CheckProductStockLevel method has been enhanced with an additional security check to ensure that blank passwords cannot be used in the connection string. If the code retrieves a connection string with a blank password, a SecurityException is thrown.

[SqlClientPermissionAttribute(SecurityAction.PermitOnly, 
                              AllowBlankPassword=false)]
public static int CheckProductStockLevel(string productCode)
{
  // Retrieve the connection string from the registry
  string connectionString = GetConnectionString();
  . . .
}

For more information about how to sandbox data access code to allow the OLE DB and other data providers to be used from partial trust Web applications, see Chapter 9, "Using Code Access Security with ASP.NET."

Directory Services

Currently, code that uses classes from the System.DirectoryServices namespace to access directory services such as Active Directory must be granted full trust. However, you can use the DirectoryServicesPermission to constrain the type of access and the particular directory services to which code can connect.

Constraining Directory Service Access

To constrain code, you can use the DirectoryServicesPermissionAttribute together with SecurityAction.PermitOnly. The following attribute ensures that the code can only connect to a specific LDAP path and can only browse the directory.

[DirectoryServicesPermissionAttribute(SecurityAction.PermitOnly, 
                       Path="LDAP://rootDSE",
                       PermissionAccess=DirectoryServicesPermissionAccess.Browse)]
public static string GetNamingContext(string ldapPath)
{
  DirectorySearcher dsSearcher = new DirectorySearcher(ldapPath);
  dsSearcher.PropertiesToLoad.Add("defaultNamingContext");
  dsSearcher.Filter = "";
  SearchResult result = dsSearcher.FindOne();
  return (string)result.Properties["adsPath"][0];
}

Requesting DirectoryServicesPermission

To document the permission requirements of your code, and to ensure your assembly cannot load if it is granted insufficient directory services access from code access security policy, add an assembly level DirectoryServicesPermissionAttribute with SecurityAction.RequestMinimum as shown in the following example.

[assembly: DirectoryServicesPermissionAttribute(SecurityAction.RequestMinimum, 
                       Path="LDAP://rootDSE",
                       PermissionAccess=DirectoryServicesPermissionAccess.Browse)]

Environment Variables

Code that needs to read or write environment variables using the System.Environment class must be granted the EnvironmentPermission by code access security policy. This permission type can be used to constrain access to specific named environment variables.

Constraining Environment Variable Access

To constrain code so that it can only read specific environment variables, you can use the EnvironmentPermissionAttribute together with SecurityAction.PermitOnly. The following attributes ensure that the code can only read from the username, userdomain, and temp variables.

[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="username")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="userdomain")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="temp")]
public static string GetVariable(string name)
{
  return Environment.GetEnvironmentVariable(name);
}

Requesting EnvironmentPermission

To document the permission requirements of your code, and to ensure your assembly cannot load if it is granted insufficient environment variable access from code access security policy, add an assembly level EnvironmentPermissionAttribute with SecurityAction.RequestMinimum as shown in the following code.

[assembly: EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
                                          Read="username"),
           EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
                                          Read="userdomain"),
           EnvironmentPermissionAttribute(SecurityAction.RequestMinimum,
                                          Read="temp")]

Web Services

Code that calls Web services must be granted the WebPermission by code access security policy. The WebPermission actually constrains access to any HTTP Internet-based resources.

Constraining Web Service Connections

To restrict the Web services to which your code can access, use the WebPermissionAttribute together with SecurityAction.PermitOnly. For example, the following code ensures that the PlaceOrder method and any methods it calls can only invoke Web services on the http://somehost site.

[WebPermissionAttribute(SecurityAction.PermitOnly,
                        ConnectPattern=@"http://somehost/.*")]
[EnvironmentPermissionAttribute(SecurityAction.PermitOnly, Read="USERNAME")]
public static void PlaceOrder(XmlDocument order)
{
  PurchaseService.Order svc = new PurchaseService.Order();
  // Web service uses Windows authentication
  svc.Credentials = System.Net.CredentialCache.DefaultCredentials;
  svc.PlaceOrder(order);
}

In the prior example, the ConnectPattern property of the WebPermissionAttribute class is used. This allows you to supply a regular expression that matches the range of addresses to which a connection can be established. The EnvironmentPermissionAttribute shown previously is required because the code uses Windows authentication and default credentials.

The following example shows how to use the Connect attribute to restrict connections to a specific Web service.

[WebPermissionAttribute(SecurityAction.PermitOnly,
                        Connect=@"http://somehost/order.asmx")]

Sockets and DNS

Code that uses sockets directly by using the System.Net.Sockets.Socket class must be granted the SocketPermission by code access security policy. In addition, if your code uses DNS to map host names to IP addresses, it requires the DnsPermission.

You can use SocketPermission to constrain access to specific ports on specific hosts. You can also restrict whether the socket can be used to accept connections or initiate outbound connections, and you can restrict the transport protocol, for example, Transmission Control Protocol (TCP) or User Datagram Protocol (UDP).

Constraining Socket Access

To constrain code so that it can only use sockets in a restricted way, you can use the SocketPermissionAttribute together with SecurityAction.PermitOnly. The following attributes ensure that the code can connect only to a specific port on a specific host using the TCP protocol. Because the code also calls Dns.Resolve to resolve a host name, the code also requires the DnsPermission.

[SocketPermissionAttribute(SecurityAction.PermitOnly, 
                           Access="Connect", 
                           Host="hostname", 
                           Port="80", 
                           Transport="Tcp")]
[DnsPermissionAttribute(SecurityAction.PermitOnly, Unrestricted=true)]
public string MakeRequest(string hostname, string message)
{
  Socket socket = null;
  IPAddress serverAddress = null;
  IPEndPoint serverEndPoint = null;
  byte[] sendBytes = null, bytesReceived = null;
  int bytesReceivedSize = -1, readSize = 4096;

  serverAddress = Dns.Resolve(hostname).AddressList[0];
  serverEndPoint = new IPEndPoint(serverAddress, 80);
  socket = new Socket(AddressFamily.InterNetwork, 
                      SocketType.Stream, ProtocolType.Tcp);
  bytesReceived = new byte[readSize];
  sendBytes = Encoding.ASCII.GetBytes(message);
  socket.Connect(serverEndPoint);
  socket.Send(sendBytes);
  bytesReceivedSize = socket.Receive(bytesReceived, readSize, 0);
  socket.Close();
  if(-1 != bytesReceivedSize)
  {
    return Encoding.ASCII.GetString(bytesReceived, 0, bytesReceivedSize);
  }
  return "";
}

Requesting SocketPermission and DnsPermission

To document the permission requirements of your code, and to ensure your assembly cannot load if it is granted insufficient socket or DNS access from code access security policy, add an assembly level SocketPermissionAttribute and a DnsPermissionAttribute with SecurityAction.RequestMinimum as shown in the following example.

[assembly: SocketPermissionAttribute(SecurityAction.RequestMinimum, 
                                     Access="Connect", 
                                     Host="hostname", 
                                     Port="80", 
                                     Transport="Tcp")
           DnsPermissionAttribute(SecurityAction.PermitOnly, Unrestricted=true)]

Unmanaged Code

Code that calls unmanaged Win32 APIs or COM components requires the unmanaged code permission. This should only be granted to highly trusted code. It is defined by the SecurityPermission type with its Flags property set to SecurityPermissionFlag.UnmanagedCode.

The following guidelines for calling unmanaged code build upon those introduced in Chapter 7, "Building Secure Assemblies."

  • Use naming conventions to indicate risk.
  • Request the unmanaged code permission.
  • Sandbox unmanaged API calls.
  • Use SupressUnmanagedCodeSecurityAttribute with caution.

Use Naming Conventions to Indicate Risk

Categorize your unmanaged code and prefix the types used to encapsulate the unmanaged APIs by using the following naming convention.

  • Safe. This identifies code that poses no possible security threat. It is harmless for any code, malicious or otherwise, to call. An example is code that returns the current processor tick count. Safe classes can be annotated with the SuppressUnmanagedCode attribute which turns off the code access security permission demand for full trust.

    [SuppressUnmanagedCode]
    class SafeNativeMethods {
           [DllImport("user32")]
           internal static extern void MessageBox(string text);
    }
    
  • Native. This is potentially dangerous unmanaged code, but code that is protected with a full stack walking demand for the unmanaged code permission. These are implicitly made by the interop layer unless they have been suppressed with the SupressUnmanagedCode attribute.

    class NativeMethods {
           [DllImport("user32")]
           internal static extern void FormatDrive(string driveLetter);
    }
    
  • Unsafe. This is potentially dangerous unmanaged code that has the security demand for the unmanaged code permission declaratively suppressed. These methods are potentially dangerous. Any caller of these methods must do a full security review to ensure that the usage is safe and protected because no stack walk is performed.

    [SuppressUnmanagedCodeSecurity]
    class UnsafeNativeMethods {
           [DllImport("user32")]
           internal static extern void CreateFile(string fileName);
    }
    

Request the Unmanaged Code Permission

Strong-named

[assembly: SecurityPermission(SecurityAction.RequestMinimum, 
                              UnmanagedCode=true)]

Sandbox Unmanaged API Calls

Isolate calls to unmanaged code in specific assemblies and keep the number of assemblies that call unmanaged code to a minimum. Then, use the sandboxing pattern to ensure that the unmanaged code permission is only granted to selected assemblies.

To sandbox your managed code that calls unmanaged code

  1. Place your code that calls unmanaged code in a separate (wrapper) assembly.

  2. Add a strong name to the assembly.

    This allows custom code access security policy to be easily applied to the assembly. For more information, see the "Strong Names" section in Chapter 7, "Building Secure Assemblies."

  3. Request the unmanaged code permission (as described in the preceding section.)

  4. Authorize calling code with a full permission demand.

    You typically need to use a custom permission that represents the unmanaged resource being exposed by your assembly. For example:

    (new EncryptionPermission(EncryptionPermissionFlag.Encrypt, 
                              storePermissionFlag.Machine)).Demand();
    
  5. Assert the unmanaged code permission in your wrapper class:

    (new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();
    

For a full example implementation that shows you how to call the unmanaged Win32 DPAPI functionality, see "How To: Create a Custom Encryption Permission," in the "How To" section of this guide.

Use SuppressUnmanagedCodeSecurity with Caution

If your assembly makes many calls to unmanaged code, the performance overhead associated with multiple unmanaged code permission demands can become an issue.

In this case, you can use the SupressUnmanagedCodeSecurity attribute on the P/Invoke method declaration. This causes the full demand for the unmanaged permission to be replaced with a link demand which only occurs once at JIT compilation time.

In common with the use of link demands, your code is now vulnerable to luring attacks. To mitigate the risk, you should only suppress the unmanaged code permission demand if your assembly takes adequate precautions to ensure it cannot be coerced by malicious code to perform unwanted operations. An example of a suitable countermeasure is if your assembly demands a custom permission that more closely reflects the operation being performed by the unmanaged code

Using SuppressUnmanagedCodeSecurity with P/Invoke

The following code shows how to apply the SuppressUnmanagedCodeSecurity attribute to a Platform Invocation Services (P/Invoke) method declaration.

public NativeMethods
{
  // The use of SuppressUnmanagedCodeSecurity here applies only to FormatMessage
  [DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity]
  private unsafe static extern int FormatMessage(
                                      int dwFlags, 
                                      ref IntPtr lpSource, 
                                      int dwMessageId,
                                      int dwLanguageId, 
                                      ref String lpBuffer, int nSize, 
                                      IntPtr *Arguments);
}

Using SuppressUnmanagedCodeSecurity with COM Interop

For COM interop calls, the attribute must be used at the interface level, as shown in the following example.

[SuppressUnmanagedCodeSecurity]
public interface IComInterface
{
}

Delegates

There is no way of knowing in advance what a delegate method is going to do when you invoke it. If your assembly supports partial trust callers, you need to take extra precautions when you invoke a delegate. You can use code access security to further improve security.

  • Consider restricting permissions for the delegate.
  • Do not assert a permission before calling a delegate.

Consider Restricting Permissions for the Delegate

The permissions granted to the code that calls the delegate determine the capabilities of the delegate. If your code has more permissions than the code that gives you the delegate, this provides a way for the caller to execute code using elevated permissions. To address this issue, you can either authorize the external code at the point it passes you the delegate with an appropriate permission demand, or you can restrict the permissions of the delegate just prior to calling it by using a deny or permit only stack modifier. For example, the following code only grants the delegate code execution permission to constrain its capabilities.

// Delegate definition
public delegate void SomeDelegate();
. . . 
// Permit only execution, prior to calling the delegate. This prevents the
// delegate code accessing resources or performing other privileged
// operations
new SecurityPermission(SecurityPermissionFlag.Execution).PermitOnly();
// Now call the "constrained" delegate
SomeDelegate();
// Revert the permit only stack modifier
CodeAccessPermission.RevertPermitOnly();

Do Not Assert a Permission Before Calling a Delegate

Asserting a permission before calling a delegate is dangerous to do because you have no knowledge about the nature or trust level of the code that will be executed when you invoke the delegate. The code that passes you the delegate is on the call stack and can therefore be checked with an appropriate security demand. However, there is no way of knowing the trust level or permissions granted to the delegate code itself.

For more guidelines about using delegates securely, see the "Delegates" section in Chapter 7, "Building Secure Assemblies."

Serialization

Code that supports serialization must be granted a SecurityPermission with its Flag attribute set to SerializationFormatter. If you develop classes that support serialization and your code supports partial trust callers, you should consider using additional permission demands to place restrictions on which code can serialize your object's state.

Restricting Serialization

If you create a class that implements the ISerializable interface, which allows your object to be serialized, you can add a permission demand to your ISerializable.GetObjectData implementation to authorize the code that is attempting to serialize your object. This is particularly important if your code supports partial trust callers.

For example, the following code fragment uses a StrongNameIdentityPermission demand to ensure that only code signed with a particular private key corresponding to the public key in the demand can serialize your object's state.

[StrongNameIdentityPermission(SecurityAction.Demand, 
                              PublicKey="00240000048...97e85d098615")]
public override void GetObjectData(SerializationInfo info, 
                                   StreamingContext context)

For more guidelines about using serialization securely, see the "Serialization" section in Chapter 7, "Building Secure Assemblies."

Note   In .NET 2.0, StrongNameIdentityPermission only works for partial trust callers. Any demand, including a link demand, will always succeed for full trust callers regardless of the strong name of the calling code.

Summary

Code access security allows you to restrict what your code can do, restrict which code can call your code, and identify code. In full trust environments where your code and the code that calls you have the unrestricted set of all permissions, code access security is of less significance.

If your code supports partial trust callers, the security risks are that much greater. In partial trust scenarios, code access security enables you to mitigate some of the additional risks and allows you to constrain privileged code.

Additional Resources

For more information, see the following resources:

patterns & practices Developer Center

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.

© Microsoft Corporation. All rights reserved.