Search Forum
(53671 Postings)
Search Site/Articles

Archived Articles
712 Articles

C# Books
C# Consultants
What Is C#?
Download Compiler
Code Archive
Archived Articles
Advertise
Contribute
C# Jobs
Beginners Tutorial
C# Contractors
C# Consulting
Links
C# Manual
Contact Us
Legal

GoDiagram for .NET from Northwoods Software www.nwoods.com


 
Printable Version

Code Access Security Using C#
By Sitaraman Lakshminarayanan

In today’s fast growing Information Technology world, security is a major concern. Security is important not just to authenticate users but also to authorize their actions. Common business scenarios where we need more control over security are

1. To restrict the user’s access to the application, based on their identity
2. To restrict the user’s access to protected resources and certain functions of the application.
3. To verify the identity of the calling program, to check if the caller is trusted or not.

In this article I will be taking a close look at .NET Security. Code Access Security addresses these issues. The foundation of CAS is the Permission Object.

Download codeaccesssecurity.zip

Permission:
CLR requires that the code should have required permissions to access the protected resources. For example if the code has to read from an environment variable or write information to a file, the code should have enough permission to perform these operations. Permission Objects are used to enforce these security issues. Lets take a look at what kind of Permission Objects are available in .Net and how we can use them to secure our application. There are three different kinds of permission objects, each serving a specific purpose: Identity Permission, Code Access Permission and Role Based Security Permission.

Identity Permission:
CLR validates the identity of the assembly provided to it by the loader or host. The host can be a server host (Asp.Net) or a shell host (command line). The identity, called the Evidence, can be a strong name (StrongNameIdentityPermission), originating Zone that is Intranet, Internet or Trusted site (ZoneIdentitypermission), publishers digital signature (PublisherIdentityPermission), originating URL (URLIdentityPermission) or originating website (WebSiteIdentityPermission). I will explain in detail with examples the former two

StrongNameIdentityPermission:
This permission object identifies the caller with a strong name. In order to have a strong name for your assembly, the code should be signed by a key pair. .Net provides the utility, sn.exe, to generate a key pair. sn -k KeyPairFileName.snk Once the keyfile is generated, you can place the following line of code in AssemblyInfo.cs [assembly: AssemblyKeyFile (@"..\..\KeyPair.snk")] The above line will make sure that the assembly is signed with a strong name.

Let us look at an example to understand how we can use StrongNameIdentity Permission to identify the caller. Create a console application called TestPermissionObjects.exe. The assembly is signed with a strong name as shown above. We can demand the strong name identity permission in our component using the public key counter part of the key pair generated.

public bool TestStrongNameIDentityPermission(string strongname,string version)
{
 try
 {
  byte[] publickey = { 0, 36, 0, 0, 4, 128, 0, 0, 148, 0, 0, 0, 6, 2, 0, 0, 0, 36, 0, 0, 82, 83, 
  65, 49, 0, 4, 0, 0, 1, 0, 1, 0, 45, 25, 102, 36, 141, 31, 69, 83, 150, 31, 81, 170, 101, 131, 
  2, 136, 254, 120, 34, 55, 22, 245, 242, 91, 151, 25, 32, 206, 224, 156, 198, 240, 123, 2, 52, 
  230, 50, 196, 88, 84, 15, 86, 232, 53, 147, 140, 161, 64, 59, 200, 217, 15, 237, 100, 152, 230, 
  19, 148, 160, 187, 218, 36, 45, 168, 159, 92, 13, 58, 46, 43, 195, 106, 134, 98, 68, 226, 206, 
  166, 236, 88, 33, 160, 82, 254, 165, 38, 19, 22, 64, 28, 247, 127, 175, 225, 92, 214, 63, 102, 
  232, 124, 196, 242, 22, 144, 31, 64, 92, 248, 164, 148, 109, 130, 18, 103, 206, 177, 173, 104, 
  190, 221, 164, 102, 34, 150, 110, 25, 127, 189 } ; 
  // publickey can be extracted using utility secutil. 
  // secutil -strongname AssemblyNameWithFulpath
  StrongNamePublicKeyBlob publickeyblob = new StrongNamePublicKeyBlob (publickey); 
  System.Version ver = new Version(version); 
  StrongNameIdentityPermission sip = new StrongNameIdentityPermission(publickeyblob,strongname,ver);
  sip.Demand();
  return true ; 
  }
  catch(SecurityException se)
  {
   Console.WriteLine ("Identity Permission Failed -- Strong Name " + strongname + "--version -- " + version );
   return false;
  }
 }
Any client that is calling the above method of the component should have the strong name, key pair and correct version otherwise the call to this method will throw a Security Exception.

ZoneIdentitytPermission:
This permission object identifies the caller with the zone the call originates from. Defined Security Zones are Internet, Intranet, MyComputer, NoZone, Trusted, and Untrusted. Code can demand ZoneIdentityPermission to ensure that the caller is from the specified zone. The following example explains how to use ZoneIdentityPermission object to ensure that the callers are from the local computer, that is from the MyComputer zone.

public string TestZoneIdentityPermission()
{
ZoneIDentityPermission zip = new ZoneIdentityPermission(SecurityZone.MyComputer)
zip.Demand();
//The line below will succeed only if the calling code is from the zone MyComputer. 
// If the same component is called from Intranet the call will fail. 
StreamWriter sw = new StreamWriter("TestPermission.txt")

sw.write("Sucess ZoneIDentityPermisson");

sw.close(); 
}
PublisherIDentityPermission:
This permission object identifies the caller with the Digital signature. The caller should be signed with a Digital Certificate.

URLIdentityPermission:
This permission object identifies the caller with the URL from which the call originates.

SiteIdentityPermission: This permission object identifies the caller with the website from which the call originates.

Code Access Permission: Identifying the caller using identity permission is not adequate to secure an application. Various resources like file, environment, variables and message queues should be protected along with the critical operations that the code performs. These Security policies can be enforced using CodeAccessPermission. There are various CodeAcessPermission objects available in .NET such as FileIOPermission, EnvironmentPermission and EventlogPermission. For a list of CodeAcessPermission refer to http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconidentitypermissions.asp

CodeAccessPermission object restricts access to the protected resources, identifies if all the callers in the call stack have sufficient permission and provides functionality to create the object from xml. These are basically the implementations of the interfaces IstackWalk, Ipermission and IsecurityEncodable. IstackInterface implementation in CodeAcessPermission objects ensures that all callers in the hierarchy have enough permission to access the protected resources. IPermission interface has the methods: Demand, Copy, Union, IsSubsetOf and Intersect. Every CodeAccessPermission object has this interface implemented and the client can call the Demand() method of the permission object to make certain that the requested permissions are available.

CodeAccessPermission can be implemented in two ways. One is Imperative syntax and the other is Declarative syntax.

Imperative Syntax:
You can protect access to the resources and functions by creating an instance of appropriate permission object and demanding the permission. Lets take a look at FileIOPermissionObject and how we can confine access to files and folders.

public void TestFileIOPermission(string path , string filename)
{
 try
 {
  FileIOPermission fip = new FileIOPermission(FileIOPermissionAccess.Read , path);
  fip.PermitOnly();
  StreamWriter sr = new StreamWriter (path +"\"+ filename); 
  sr.WriteLine ("FileIOPermision success"); 
  sr.Close();
 }
 catch(SecurityException se)
 {
  Console.WriteLine (se.StackTrace); 
 }
}
Declarative Syntax:
You can control access to resources and functions by placing a Security Attribute at Method, Class or Assembly Level. Security information defined using attributes are placed in the metadata of the code. Each CodeAccessPermission has its corresponding Security Attribute. Each Security Attribute takes at least SecurityAction as parameter. SecurityAction enumeration can take values depending on where the security is enforced. For example, RequestMinimum is supported only at Assembly level. Let us take a look at the following example, which refuses access to winnt folder.

[assembly: FileIOPermissionAttribute(SecurityAction.RequestRefuse, "c:\winnt")]

Imperative Syntax won’t allow the user to request permission as opposed to Declarative syntax where you can request the permission.

PermissionSet:
PermissionSet is a set of Permissions .You can group the permission and add it to PermissionSet. PermissionSet implements the same Ipermission Interface. Security calls on a permission set will in turn make calls to permission object of the permission set. For example making a call to perform Deny() on permission set will deny access to permission objects that are members of the Permission set. The following example explains the use of Permission set

public void TestPermissionSet()
{
 PermissionSet ps = new PermissionSet(PermissionState.None) ; 
 ps.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read ,"c:\C#") );
 ps.AddPermission(new EnvironmentPermission(EnvironmentPermissionAccess.Read ,"TEMP"));
 ps.PermitOnly();
 Console.WriteLine(Environment.GetEnvironmentVariable("TEMP"));
 Console.WriteLine(Environment.GetEnvironmentVariable("PATH"));
}
The above function will throw a Security Exception while trying to access the environment variable “PATH” which is not granted permission.

You can create your own NamedPermissionSet by grouping the desired pemissions. These PermissionSets can then be added to codegroup, which will be interpreted by CLR while computing the permission. (Codegroup -- Each code belongs to a certain group based on the membership condition. Code that satisfy those conditions become a member of the group. Each group has a permission set associated with it. .Net CLR computes permission based on policy, group and associated permission set.). The following link gives a detailed explanation about computing permission and codegroup attributes. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconpermissions.asp

Let us take a look at a pre-defined permission set LocalIntranet, which has the permission to execute an assembly, read the environment variables TEMP, TMP and UserName (For other permissions of LocalIntranet please refer to security.config file under CLR install directory. Or you may use c:\winnt\Microsoft.net\V*\Framework\bin\ msccorfg.msc). The following code, a sample WindowApplication will throw an exception with the PermissionSet LocalIntranet.

private void TestLocalIntranetZonePermissionSet()
{
 try
 {

  Console.WriteLine("Environment Variable UserName : " + Environment.GetEnvironmentVariable("USERNAME")); 
  Console.WriteLine(Environment.GetEnvironmentVariable("PATH")); 
 }
 catch(SecurityException se)
 {
  Console.WriteLine ("Permission Error -- can't read Environment Variable PATH");
 }
}
Change the permission set associated with zone MyComputer from FullTrust to LocalIntranet.
caspol -chggroup 1.1 -zone MyComputer LocalIntranet

After performing this change, when you execute the exe file, you will get a Security Exception.

You may reset the security back to default security policy using caspol -reset. You can also change the permission set back to Fulltrust using
caspol -chggroup 1.1 -zone MyComputer FullTrust.

CustomPermission:
You can create your custom CodeAccessPermission by inheriting from CodeAccessPermission class. This in turn has the implementations of Ipermission Interface and IstackWalk . Since this derives from CodeAccessPermission , your custom CAP object will also verify that all the callers in hierarchy have enough permission. Once you have defined your custom CodeAccesPermission class, you can demand permissions in your code in a manner similar to other CodeAccessPermission objects. Please refer to the following link for detailed information on creating your own Code Access Permission. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconpermissions.asp

Role Based Security:
Application should allow access to the critical information and critical operations only to authorized users. Users are authorized based on the information provided by the user. Access to the resources can be restricted to users of any group, only to specific group or to all the users of a certain group. .Net defines Principal as the combination of identity and group or role. Identity is nothing but a Windows User (WindowIdentity), userid (GenericIdentity) or it can be custom (custom identity object that implements Iidentity Interface). .Net also provides WindowsPrincipal and GenericPrincipal object . You can define your own Principal object by implementing IPricipal Interface. Role Based security is described in detail at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconnamedpermissionsets.asp

The source code along with this article has the implementations of IIdentity and Iprincipal interfaces to define Custom Identity and Custom Principal.

Note: Custom CodeAccessPermission will check for permissions of all the callers in hierarchy by default because of the implementation of IStackWalk interface. If you prefer not to check the permissions of the callers in code stack, you may implement Ipermision interface to define custom permission class.

Let us define a custom authentication scenario where we can use custom permission, custom identity and custom principal objects. 
a) Gather Evidence ie Userid and password from the user 
b) Authenticate against the data store which can be a database, LDAP, etc
c) Create Identity Object using CustomIdentity class (please see the source code attached) 
d) Create Principal Object using CustomPrincipal class 
e) Set the Principal as Thread.CurrentPrincipal. 
By Setting Principal as Thread.CurrentPrincipal, Principal is available at any time by accessing

Thread.CurrentPrincipal property.

Public void TestCustomPermission()
{
 CustomIdentity ci = new CustomIdentity(“TestUser” ,”TestRole”); 

 CustomPrincipal cprincipal 

 CustomPermission cp 
 Cp.Demand();
}
The above code demands the identity to be TestUser and Role to be TestRole.

Let us take a look at Demand() method of CustomPermission Object.

public void Demand()
{
 if ( ( Thread.CurrentPrincipal.Identity.Name == this.userid ) & ( Thread.CurrentPrincipal.IsInRole(this.role ) ) )
 {
  Console.WriteLine("CP...Sucess"); 
 }
 else
 throw new SecurityException("Custom Permission Denied");
}
Demand() method will fail if the provided credentials are not sufficient. Note: Attached Sample Code has implementations of Ipermission and IIdentity and Iprincipal.

Conclusion:
.Net Security has pre defined permission objects which are highly flexible to use and can be extended to address custom permissions. Have fun in securing your application!