Understanding Code Access Security in .NET
 
Published: 10 Dec 2007
Abstract
This article discusses the concept of Code Access Security, its functions and the permissions related concepts.
by Joydip Kanjilal
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 52443/ 110

Introduction

In Microsoft .NET you have two types of security, Code Access Security and Role Based Security. In this article we will explore what the latter implies, how it can be used, etc. So, what is Code Access Security then? Code Access Security (CAS) is a security feature of .NET that enables the code that is to be executed to be properly verified and different levels of trust granted accordingly. It actually provides the flexibility to define what the code could perform and what not to perform. This article discusses CAS, the functions and benefits of CAS, permissions and permission sets.

What is Code Access Security?

Code Access Security (CAS) is a feature in .NET which imposes security on the code under execution based on that has created it, from where was it downloaded or copied and what are the evidences of the code. Evidences tell where the code is supposed to execute. So based on the evidence, permissions are set and these permissions are managed by policies. There are other definitions of CAS too.

According to the MSDN, "Code access security allows code to be trusted to varying degrees depending on where the code originates and on other aspects of the code's identity. Code access security also enforces the varying levels of trust on code, which minimizes the amount of code that must be fully trusted in order to run. Using code access security can reduce the likelihood that your code can be misused by malicious or error-filled code. It can reduce your liability because you can specify the set of operations your code should be allowed to perform as well as the operations your code should never be allowed to perform. Code access security can also help minimize the damage that can result from security vulnerabilities in your code."

According to Wikipedia, "Code Access Security (CAS), in the Microsoft .NET Framework, is Microsoft's solution to prevent untrusted code from performing privileged actions. When the CLR loads an assembly it will obtain evidence for the assembly and use this to identify the code group that the assembly belongs to. A code group contains a permission set (one or more permissions). Code that performs a privileged action will perform a code access demand which will cause the CLR to walk up the call stack and examine the permission set granted to the assembly of each method in the call stack. The code groups and permission sets are determined by the administrator of the machine who defines the security policy."

Benefits

The benefits of CAS are many. Here is a list of the major benefits of CAS.

·         It defines a set of permissions that would specify how the system resources should be accessed by verifying the demanded permissions with the one that is granted.

·         Permission sets defined are configured into code groups, through which an administrator can configure the security policy.

·         Provides a control over the resource acquisition by the code by strictly adhering to the permission sets configured.

·         Provides the code to demand if its callers have required permissions.

How does it work?

The security system which is built into the CLR defines a permission set which could apply to a particular resource. To access a resource, the code needs to undergo authentication and authorization process. This is done by traversing through the code and tracking the identity beneath which is usually termed as stack walk.

In managed code, any permission demanded is verified by the CLR security manager. The CLR security manager walks through the call stack by mapping the permissions demanded and the permissions granted. A SecurityException is thrown if the permission demanded is not found in the call stack. So the actual permissions are checked depending on the evidences. So evidences provide information on where the code was actually executed. Below is the diagram (figure taken from MSDN Article) which depicts the mechanism based on which the identity is traced across all the referenced assemblies by performing a call stack.

Figure 1

(Adapted from an article at MSDN)

What are Policies and Policy Levels?

Policies represent the user role which could be categorized as: Application Domain Policy, User Policy, Machine Policy and Enterprise Policy. The purpose of each is defined below.

Enterprise Policy – This policy is specified by the enterprise administrator. This would target to all managed code in an enterprise.

Machine Policy - This policy is specified by the enterprise administrator. This would target to all managed code in the local computer.

User Policy - This policy is specified by the enterprise administrator or the user of the machine.  This would target to all managed code associated with the current user.

Application Domain Policy - The policy is specified by the enterprise administrator. This would target all managed code in an application.

A policy is a configuration file which holds details on what the assembly is permitted to do depending upon the evidence collected. A common overlap of permission sets between various policies is termed as an Intersection. These policies are defined in a hierarchical structure and before applying permission all of the policies in the hierarchic should accept it.  

What is a Code Group?

Policies are configured based on a hierarchy of code groups. Code groups are structured at every policy level. So if the evidence gathered matches with the membership of a code group within a policy level, the associated set of permissions is granted to the assembly. The MSDN defines the code; groups are defined as follows:

"A code group is a logical grouping of code that has a specified condition for membership. Any code that meets the membership condition is included in the group. Code groups have associated permission sets that are evaluated during a policy grant."

Permission

Permission defines the ability of an operation to have access on particular resources. They impose specific levels of authorization to a protected resource. The runtime security system defines permissions like File IO, Printing, Socket Access, Event Log, Web Access, User Interface and so forth.

What are Permission Sets?

Permission set is a collection of permissions which are used to modify the security policy for code groups. The CLR provides the following built-in permission sets.

Nothing        

Implies that the code cannot be executed.

Execution     

Implies that code does not have permission to access protected resources, but can execute.

Internet       

Implies that code downloaded from a location and has limited permissions.

LocalIntranet

Implies that the code holds permissions defined in the default enterprise policy.

Everything

Implies that the code has all permissions except to skip verification.

FullTrust

Code has full access to all system resources.

Using Code Access Security in .NET

In this section we will explore how we can use CAS from our applications programmatically. Here is a sample code.

Listing 1

private void btnConnectDB_Click(object sender, EventArgs e)
{
  DataSet dsContact = new DataSet();
  SqlConnection dbConnection = new SqlConnection(
    "Server=.;Database=Adventureworks;Trusted_Connection=true");
  SqlCommand dbCommand = new SqlCommand("SELECT * FROM Person.Contact",
    dbConnection);
  dbConnection.Open();
  SqlDataAdapter dataAdapter = new SqlDataAdapter(dbCommand);
  dataAdapter.Fill(dsContact);
  dbConnection.Close();
  MessageBox.Show(String.Format("{0} Rows fetched from the database.",
    dsContact.Tables[0].Rows.Count));
}
 
private void btnReadFile_Click(object sender, EventArgs e)
{
  StreamReader sr = File.OpenText(@"D:\Sample.txt");
  string content = sr.ReadToEnd();
  MessageBox.Show(content);
  sr.Close();
}
 
private void btnWriteFile_Click(object sender, EventArgs e)
{
  StreamWriter sw = new StreamWriter(@"D:\Sample.txt");
  sw.WriteLine("Hello World!");
  sw.Close();
}

Refer to the code snippet given above. What we are trying to do is an attempt to connect to the database in the click event of the button btnConnectDB, and then we are trying to read and write in the subsequent buttons. Now you need to compile this project with a strong name and then configure the security permissions using the .NET Configuration Tool. In the section that follows we will explore how we can use the .NET Configuration Tool to configure security permissions.

Using the Microsoft .NET Framework 2.0 Configuration Tool

Go to Visual Studio command prompt and type mscorcfg.msc  <Enter>.  Refer to the screen shot below.

Figure 2

 

When you click on the option "Microsoft .NET Framework 2.0Configuration," you should see the following dialog.

Figure 3

 

Right click on the Permission Sets and select New.

Figure 4

 

Now, enter a name for the permission set and click next.

Figure 5

From the available permissions shown in the screen shot above, select one or more permissions and set the required attributes. In our case we will choose SQLClient, File IO, User Interface and Security. Refer to the screen shots below.

Figure 6

Figure 7

 

Figure 8

 

Figure 9

 

Observe that we have set permissions for all the selected permissions. Now, click on finish. You should be able to see the following screen.

Figure 10

Next, go to the code groups, right click on All_Code and select New.

Figure 11

Specify a new to the new code group as shown below.

Figure 12

Select the option "Strong Name" as the condition type. Remember that at this stage you should be ready to create a strong key and compiling your project with this key. Once that is done, click the Import button to import the assembly. You will notice that the public key is copied into the box. Refer to the screen shot given below.

Figure 13

Then you need to select a permission set from the lost of the permission sets. We shall select the one we had created earlier. Here is the screen shot that illustrates the same.

Figure 14

Once that is done, you need to select the first check box in the membership group. This is because permissions will be a union for more than one set of policies. By checking the first check box, we are saying to use only the permissions that we specified.

Figure 15

 

Now, when you try to execute your project, you will see the following exception. This exception is thrown because you do not have permission to access the SqlClient assemblies. Here is the screen shot that illustrates this.

Figure 16

Note that we have set the read permissions to the Sample.txt file, but not the write permissions to it. When you click on the next screen, here is the output that you will see.

Figure 17

 

Reckoning of Permissions

We have discussed the policy levels and code groups. But, how would the run time calculate the permissions when the code belongs to multiple policies or code groups in the same policy? The run time does it in the following manner.

·         Initially, the entire All_Code group and all its child code groups are checked for any matching permissions. As said, permissions are identified by evidences.

·         Then a union of permissions is performed for those identified in various groups.

The above procedure is repeated for each policy level and the outcome of permissions at each level are then intersected.

Requesting for Permissions

You might have encountered a time when you wished to check the permissions for an assembly before actually performing any operations. For instance, you might have a small windows application that reads configuration data from the UI and creates an XML file to store that information. But at the time of saving, the assembly might have not gotten any permission on the C:, and a security exception was thrown. To handle this, CAS provides three approaches which can be applied at the assembly level using declarative syntax.

·         RequestMinimum

·         RequestOptional

·         RequestRefuse

RequestMinimum defines the permissions the code requires to execute. For example, the code below makes an attempt to write into a file. If the code does not have any permissions for writing then the assembly will not be loaded.

Listing 2

using System.IO;
using System.Security;
using System.Windows.Forms;
using System.Security.Permissions;
 
// This is a declarative security call at assembly level.
 
[FileIOPermissionAttribute(SecurityAction.RequestRefuse, "C:")]
namespace DemoApp
{
  public class MyClass 
  {
    //TODO:....
  }
}

RequestOptional defines permissions that the code can use, but is not necessarily required to execute. In this case the assembly is loaded, but if the required permissions are not set then a security exception is thrown by the run time.

RequestRefuse defines a set of permissions that you might never want to grant to the code even though the security policy allows the permissions. Suppose that you need to ensure that you want to read the files and not even accidentally write into those files, then you could use the following.

Listing 3

[assembly:FileIOPermission(SecurityAction.RequestRefuse, Write="C:")] 
Imperative versus Declarative Security

The code security can be implemented by either using the Declarative Security or the Imperative Security. Let us now understand how these two differ.

Declarative Security

This is accomplished by placing security attributes at the assembly level, class level or member level. The attribute indicates the request type, overrides and demands. Each permission object has a state data, and this needs to be initialized to use that permission. Also, each permission has an attribute to which the type of security action, which is an enumeration called SecurityAction, is passed to the attribute. In the example below, all the members of the class are restricted accessing the "Program Files" folder.

Listing 4

[FileIOPermissionAttribute(SecurityAction.RequestRefuse, "C:\Program Files")]
public class RestrictPF
{
   public RestrictPF()
   {
      //security call protects the constructor.
   }
 
   public void SomeMethod()      
   {
            //security call also protects this method.
   }
}

If you want to restrict the permission on the assembly level, you can use the following.

Listing 5

[assembly: FileIOPermissionAttribute(SecurityAction.RequestRefuse, "C:\Program Files")]

If you want to restrict any registry access from the assembly level, you can use the following.

Listing 6

[assembly: RegistryPermissionAttribute(SecurityAction.RequestRefuse, Unrestricted = true)]

So even though the code runs in an environment that allows access to the registry or perform FileIO operations, the assembly will not be granted any kind of permissions.

Imperative Security

This kind of security could be used to perform demands and overrides. This helps in situations where you want to check the permissions at runtime. However, this kind of security cannot be used to perform requests. In imperative syntax, a new instance of the security permission object needs to be created before calling. Also, you need to initialize the permission set to invoke a security object. A permission set consists of a group of permissions; initializing a permission group provides a means to perform assert calls on multiple permissions in one method. For this purpose you could use the NamedPermissionSet and PermissionSet classes for grouping of permissions. You can then call the required method to invoke the appropriate security call.

Listing 7

public class RestrictPF
{
  public RestrictPF(){}
 
  public void SomeMethod()
  {
    //Here the FileIOPermissionAttribute is demanded using the imperative syntax.
    //Method is protected by security call.
    FileIOPermissionAttribute flsIOPerm = new FileIOPermissionAttribute();
    flsIOPerm.Demand();
  }
 
  public void SomeOtherMethod()
  {
    //Method not protected by security call.
  }
}
References

Conclusion

Code Access Security provides the flexibility to define what the code could and could not perform. Configuration of security could be performed using the four policy levels which are further split into code groups. A union of permission set in the code groups is done and then an intersection at the policy level is performed to calculate the actual permissions. Permissions could be requested in order to avoid any unhandled exceptions or surprises. Code Access Security also provides imperative and declarative security modes to define permissions on your code.



User Comments

No comments posted yet.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-20 6:21:12 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search