The Basics of Code Access Security: minimal coding that offers a huge return – Focus: web services security
Thom Robbins
Remember the old days when we only installed applications that were purchased from the local computer store? Actually, this was the only way to get the application media. Also, because we had mass-produced disks or tapes this provided an additional sense of security.
Of course, there were application bugs and unexpected features, but we never expected an application would intentionally destroy a desktop or server. This model doesn’t work today when most software is downloaded or application code is embedded in other formats. Everything from a simple Web page to e-mails containing scripts are downloaded and run on our local machines.
The sad truth is some of this code is malicious. All companies take the necessary precautions to protect their users and critical data from the malicious code. These include fire-walls, virus checkers, and common sense. Ultimately, today it seems we are always at risk. The .NET Framework provides Code Access Security (CAS) to help solve this problem and secure our systems from malicious code. In this article, I’ll show the basics of how you can use CAS and the .NET Framework to protect your system.
CAS provides a centralized mechanism to assign permission levels for individual sections of code based on their origination and identity. For example, we may want to restrict Internet-downloaded controls to only accessing the user interface and specifically deny any type of access to the local file system. However, for a small set of trusted vendors we want to provide local file system access. The problem with this example is that traditional Windows-based security doesn’t provide this granular level of support. CAS, which is part of the .NET Framework or Common Language Runtime (CLR), provides the ability to set and maintain this type of system security policy.
CAS Basics
All applications that target the CLR operate in a managed environment. Among other things, this means that all code must interact with the CAS system runtime. System administrators are able to manage and uniquely specify permissions granted to each managed code assembly. Any time managed code attempts to access a protected resource (i.e., opening a file) the assigned permissions are checked. It is important to remember that the CLR walks the entire call stack when performing these checks. This prevents untrusted top-level assemblies from bypassing security checks on the lower end of the stack and gaining additional unauthorized access. The bad news is that CAS and the CLR don’t have the ability to control and secure traditional unmanaged code such as COM (Component Object Model) objects. These objects deal directly with the WIN32 operating system and execute outside the control of the CLR. The good news is that CAS does offer the ability to turn unmanaged code execution on or off.
During the installation of the .NET Framework a default security policy is implemented for CAS. This policy defines the basic rules that all applications are granted by default. Actually, this is a pretty good starter set but should be thoroughly reviewed for specific concerns that companies may have. As you would expect, the policy is less restrictive for locally running applications and more restrictive for Internet-based applications. There are two ways to modify the default security framework. First is a standard MMC console (mscorefg.msc). This is the preferred method for modifying application security (see Figure 1). Second is a command-line utility called caspol.exe. This utility provides an easy to script security batch upload interface.
[FIGURE 1 OMITTED]
Assembly Requests
Essentially, Code Access Security allows each .NET assembly the ability to run with different assigned levels of privileges. Each assembly must provide specific evidence that includes its origination (Internet, intranet, or local) and optionally a public key to support its security request. CAS then evaluates these against the local security policy to determine the effective security. If at any point the assembly attempts to gain additional unauthorized permission, the CLR rejects the request and raises a security exception. One of the real benefits for end users is that they are no longer asked whether or not to trust code during application execution. Effectively, the defined security policy and assembly evidence determine the authorization level.
Developers don’t have to request permissions for their code to compile and run within the managed environment of the CLR. Ideally, it is a good practice that can make end users and system administrators better able to manage your applications. Of course, the real benefit is that by requesting permissions you are increasing the chance that your code will be allowed to execute. Additionally, if you don’t identify a minimum set of permissions it becomes the developer’s responsibility to gracefully handle all runtime security errors. System administrators are also able to provide a base set of permissions that meet your requested applications, requirements. Administrators can use the Permission View (per-mview.exe) tool to examine an assembly and issue the requested permissions. If you don’t provide a base permission request, administrators can’t effectively determine base permissions, which can make your application difficult to administer.
Permissions are requested by applying attributes at the assembly level. These attributes vary depending on the specific permissions requested. These requests are compiled into the metadata of the assembly manifest and evaluated when code is loaded into memory for execution. Probably the most common permission request is to access unmanaged code. Listing 1 is an example of the assembly level request that uses the SecurityPermission attribute to request this access.
Listing 1: Imports System
Imports System.Security.Permissions
Imports System.Runtime.InteropServices
‘The request is placed on the assembly level.
<assembly:
SecurityPermissionAttribute(SecurityAction.RequestMinimum,
Flags := SecurityPermissionFlag.UnmanagedCode)>
Namespace MyNamespace
Public Class MyClass1
Public Sub New()
End Sub
Public Sub MyMethod()
‘Perform interoperation with unmanaged code here.
End Sub
End Class
End Namespace
The SecurityPermission attribute requires two values. The first is the SecurityAction that specifies the permission requested (Request Minimum) and a flag that indicates the requested permission (SecurityPermission Flag. UnmanagedCode).
Demand-Based Security
In addition to specifying security requests within the assembly level, you can also issue a security demand with your application. This demand specifies either declaratively or imperatively the permissions level that both direct and indirect callers must have to access your application code. Typically, direct callers are defined as members that call either static or instance methods of your application library. Indirect callers on the other hand, call either static or instance methods of another library that in turn calls yours. Within a demand request, any application that includes your code will execute only if all direct and indirect callers have the necessary security.
Demands are ideal for situations where class libraries access protected resources that you don’t want to provide access to untrusted code. Demands can be placed in code using either imperative or declarative syntax. Remember that most classes in the .NET Framework already have demands associated with them. So by default you don’t have to make demands associated with protected resource access. For example, the Stream Writer class automatically makes a security demand for FileIOPermission whenever it is opened. An additional demand for FileOPermission every time you use the Stream Writer class causes an inefficient stack walk. As a general rule demands are best suited for unique resources that may require custom permissions.
Declarative demands place requests directly into your code’s metadata using attributes. You can use declarative syntax to place a demand in either the class or method level of your code. If you place a declarative security check at the class level, it applies to each member of the class. However, if you place a declarative security check at the member level, it applies to only that member and overrides any permissions specified at the class level. For example, suppose you specify at the class level that PermissionA is required, and for that class’s Method1 you indicate that PermissionB is required. When Method 1 is called, a security check will look only for PermissionB, but other methods of the class will still require PermissionA.
The following example places a declarative demand for a custom permission called CustomPermission on all callers of the ReadData method. The custom permission has a separately defined CustomPermission Attribute that makes the demand. In this case, it takes a SecurityAction.Demand flag in order to specify the type of demand the attribute will perform.
<CustomPermissionAttribute(SecurityAction.
Demand, Unrestricted = True)>Public
Shared Function ReadData() As String
‘Read from a custom resource.
End Function
Imperative demands are placed in the method level of your code by creating a new instance of a permission object and calling that object’s Demand method. The imperative syntax cannot be used to place demands at the class level. The imperative demand that you place in your code effectively protects all the remaining code in the method in which the Demand method is called. The security check is performed when the Demand is executed; if the security check fails, a SecurityException is thrown and the rest of the code in that method or member is never executed unless the SecurityException is caught and properly handled.
The following example uses imperative syntax to place a demand on all callers for the custom permission CustomPermission. This code creates a new instance of the Custom Permission class, passing the PermissionState. Unrestricted flag to the constructor and then calling the demand method.
Public Shared Sub ReadData()
Dim MyPermission As New
CustomPermission(PermissionState.Unrestric
ted)
MyPermission.Demand()
‘Read from a custom resource.
End Sub
Role-Based Security
Up to this point we’ve discussed how Code Access Security revolves around the idea of code and not around the idea of users and roles. There is always a need to define security based on user identity. The CAS runtime also provides role-based security features that have become a standard within many enterprises, especially with the adoption of Active Directory. Within role-based security, two base levels are used: Identity and Principal.
Identity represents the user on whose behalf the code is executed. Keep in mind that this is a logical representation of the user defined by the application or developer. A principal; on the other hand, represents the actual abstraction of the user and the roles that they belong to. Classes that represent a user’s identity implement the Identity interface. An example of a generic class providing a default implementation of this interface within the .NET Framework is Generic Identity. Classes representing Principals implement the IPrincipal interface. An example of a generic class providing a default implementation of this interface within the .NET Framework is GenericPrincipal.
At runtime each thread has only one current principal object associated with it. Based on the specified security requirements, code may access and change this object as needed. Each Principal has only one identity object. For example, Listing 2 shows a generic implementation of role-based security.
Listing 2: Generic implementation of role-based security.
using System;
using System.Threading;
using System.Security;
using System.Security.Principal;
namespace RoleBasedSecurity
{
class Sample
{
static void Main(string[] args)
{
String [] roles = {“Lecturer”, “Student”};
GenericIdentity i = new GenericIdentity(“Thom”);
GenericPrincipal g = new GenericPrincipal(i,
roles);
Thread.CurrentPrincipal = g;
if(Thread.CurrentPrincipal.Identity.Name ==
“Thom”)
Console.WriteLine(“Hello Thom”);
if(Thread.CurrentPrincipal.IsInRole(“Lecturer”))
Console.WriteLine(“Hello Lecturer”);
if(Thread.CurrentPrincipal.IsInRole(“Employee”))
Console.WriteLine(“Hello Employee”);
}
}
}
Download the code at
sys-con.com/webservices
Conclusion
This article has covered quite a bit. There are a couple of things that you should remember. First, by default the security policy gives significantly more trust to code executed from the local hard drive than from other locations. The idea of executing code locally is significantly different from executing from a remote location. Downloading code, storing it to disk, and executing it has a far different set of semantics and requirements than executing code from a remote location. The general idea is that code that is downloaded, saved, and then run locally is usually inspected and reviewed, for example, with a virus scanner before execution. With CAS, the scenario is reversed. Code executed remotely provides more default security than locally executed code. It is important to understand that CAS is not a replacement for standard security precautions. It is designed to provide a new level of protection around security and maintainability of code. Also, now that you have learned the basics, start including and leveraging Code Access Security within your applications. I think you’ll find that it provides a minimum of coding but yields a huge return in terms of security and manageability within your applications.
AUTHOR BIO:
Thom Robbins is a senior technology specialist with Microsoft in New England. He focuses on .NET development and implementing XML-based solutions. Thom is a regular speaker, writer, and presenter at industry events.
TROBBINS@MICROSOFT.COM
RELATED ARTICLE: Evidence-based security.
The .NET Framework introduces a new concept for code security–evidence-based security. Evidence is the inputs to the security policy about the code. Based on the answers to these questions, the security policy can generate an appropriate set of permissions. Evidence is obtained from multiple sources that include the CLR, browser, ASP.NET, and even the operating system shell depending on the source code.
Where was the assembly obtained?
Assemblies are considered the basic building blocks of .NET Framework applications. They are the basis for deployment, version control, reuse, activation, and scoping and security authorization. Assemblies are downloaded to the client from a Web site.
* From what URL was the assembly obtained? A security policy requires the specific address where the assembly was obtained.
* From what zone was the assembly obtained? Zones are the description of the security criteria based on the location of the code and include Internet, intranet, local machine, and others.
* What is the strong name of the assembly? The strong name is the cryptographically strong identifier provided by the author of the assembly. It doesn’t provide any real authentication of the author but uniquely identifies the assembly and ensures that it hasn’t been tampered with.
RELATED ARTICLE: .NET framework permission structure.
The .NET Framework with Code Access Security provides a variety of permissions that you can request within your code. The following is a guide to the various permission requests available.
Permission request Description
Minimum permissions (RequestMinimum) Permissions your code must
have in order to run.
Optional permissions (RequestOptional) Permissions your code
can use, but can run
effectively without.
Refused permissions (RequestRefuse) Permissions that you want
to ensure will never be
granted to your code,
even if security policy allows
them to be granted.
Perform any of the above requests on Built-in permission sets,
built-in permission sets. including: Nothing,
Execution, FullTrust,
Internet, LocalIntranet, and
SkipVerification.
Perform any of the above requests on XML representation
XML-encoded permission sets. (either a string containing the
XML-encoded permission set,
or the location of an
XML file containing the
encoded permission set) of
a desired permission set.
COPYRIGHT 2003 Sys-Con Publications, Inc.
COPYRIGHT 2003 Gale Group