October 30, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Security Through the Lifetime of a Managed Process: Fitting It All Together

  • January 7, 2004
  • By Brian A. LaMacchia
  • Send Email »
  • More Articles »
In this article, we will focus on how the individual pieces of the security system come together and interact to provide a secure environment for executing semitrusted code. After reading this article, you should be able to

  • Describe the security actions that must be made by developers at code authoring time, including declarative permission requests and appropriate permission demands
  • Describe the various mechanisms by which managed code can be installed onto a particular machine
  • Describe the function of the Native Image Generator and PE Verify tools and their relationship to the security system
  • Describe the roles the loader, the policy system, and the Just-In-Time compiler/verifier play in the CLR security system

The lifecycle of any particular managed process can be divided into three distinct stages—development, deployment, and execution. Software authors, administrators, and users make security decisions at each stage of the process that ultimately determine the permissions with which an assembly runs on a particular machine. We begin this article with an overview of the security decisions that face developers at code authoring time and then proceed to deployment and execution-time considerations in later sections.

Development-Time Security Considerations

The security features within the .NET Framework were designed, in part, to make it much easier for developers to write secure code. When authoring code, developers need to consider two main factors—the security requirements of the assemblies they are authoring and the sensitive resources and data (if any) that are potentially exposed by their classes to other code. The two factors are related but distinct, and it is slightly easier to understand the relationship between them if we begin with a discussion of the second factor, protecting sensitive resources, and then go back to investigate how developers indicate and declare security requirements of their assemblies.

The first security-related action a developer must perform when beginning work on a new assembly is to determine whether the assembly will expose any sensitive resources through its classes and methods? That is, will the classes and methods within the assembly expose sensitive resources to callers of those methods. If the answer to this question is yes, the assembly must be a secure assembly. Secure assemblies are discussed in detail in our book, .NET Framework Security but the basic issue is this—if the assembly you are authoring is going to make a new sensitive resource available to semitrusted code, your assembly must perform appropriate security checks within each method that provides access to or operates on the sensitive resource. Essentially, your new assembly is going to be a gatekeeper or guard of the protected resource, and you must treat every request for access to the resource with an appropriate degree of caution.

How do you determine whether your new assembly must be a secure assembly? This basic determination revolves around the list of resources exposed by your assembly to other code and whether those resources are sensitive or already protected. Consider the following scenario. Suppose that you want to write a method that will write a message (we'll use "Hello, World!" for historical reasons) to a file named hello.txt located on the C: drive of the computer. Using the .NET Framework, your code might look as shown in Listing 1. This program creates a FileStream object mapped to the C:\hello.txt file on the hard disk (creating the file if necessary) and writes the string "Hello,World!" to that file.

LISTING 1 Sample Hello, World! Program

using System;using System.IO;public class HelloWorld {  public static void Main(string [] args)){    FileStream fs = new FileStream("C:\\hello.txt ",                                   FileMode.OpenOrCreate,                                   FileAccess.Write);    StreamWriter sw = new StreamWriter(fs);    sw.Write("Hello, World!");    sw.Close();  }}

Does the program in Listing 1 constitute a secure assembly? That is, does this simple program require the addition of any security checks or permission demands? The answer is "No, it does not," because the program, by itself, does not expose any new sensitive resources. The only resource that is used or modified by the HelloWorld program is the c:\hello.txt file that is associated with the FileStream fs, and the FileStream class itself performs the necessary security checks to determine whether callers of its methods (including the HelloWorld program) should be granted access to the file system objects that it exposes.

The class libraries that make up the .NET Framework are secure assemblies; they implement appropriate security checks, in the form of permission demands, for the resources that they expose. Every sensitive resource that is made available to semi-trusted code through the .NET Framework class library is protected by demands for a related security permission. For example, the constructors on the FileStream class demand instances of the FileIOPermission before returning any instances of the class. Similarly, the registry-related classes demand instances of RegistryPermission, and the network-related classes demand instances of SocketPermission, WebPermission, or DNSPermission as appropriate to their function. This is one of the great advantages of writing a program on top of the .NET Framework; if all the resources that you use in your programs are already protected by appropriate permission demands, you do not need to add additional security checks to your own code. Because the HelloWorld program in Listing 1 only uses resources that are exposed through the class libraries of the .NET Framework, no additional security checks need to be made in our code.

NOTE

Even if your assembly does not expose any sensitive resources, if it performs any operations that affect the normal behavior of the .NET Framework security system, it must be a secure Development-Time Security Considerations 167.assembly. For example, if a method in your assembly calls the Assert() method on a permission, that modifies the behavior of the security stack walks and your assembly should be secure. Similarly, if you ever suppress the runtime security check that normally occurs when using platform invoke or COM interoperability via the SuppressUnmanagedCodeSecurityAttribute attribute, your assembly needs to be secure.

Even though our HelloWorld program does not expose any sensitive resources that require protection, it does make use of a protected resource—namely, the FileStream object that represents the c:\hello.txt file. Our program will only run successfully at execution time if it is granted sufficient access rights to write to the c:\hello.txt file. We can indicate this security requirement for our assembly to run through the use of assembly-level declarative permission requests. Basically, declarative security attributes are a mechanism for communicating assembly permission requirements to the policy system. Referring back to Listing 1, because HelloWorld will only operate correctly if it is granted write access to the c:\hello.txt file, we can indicate this requirement by adding the following assembly attribute to our source code:

[assembly:System.Security.Permissions.FileIOPermission(System.Security.Permissions.SecurityAction.RequestMinimum,Write="C:\\hello.txt ")]

This attribute indicates that a minimum grant of the FileIOPermission, including write access to c:\hello.txt, is required for the program to run.

CAUTION

If you determine that your assembly will be exposing a sensitive resource, you must secure that access with appropriate permission demands.
NOTE

There is a subtle interaction that occurs between the Runtime and your source code compiler when you use declarative security attributes within your programs. At compile time, declarative security attributes are checked for correctness by the version of the Runtime installed with your compiler and converted into a different formation before being embedded in the metadata of the output module or assembly.

The final important security-related decision that you must make at code authoring time is whether you want your assembly to have a strong name. Strong names are cryptographically protected names for assemblies. Strong names are built on top of public key cryptography. Strong names are used by the CLR to provide both integrity protection for your assemblies as well as cryptographically strong binding among assemblies. See our book for a description on how to use the strong name tool (Sn.exe) distributed with the .NET Framework SDK to create strong name key pairs and how to build strong names into your assemblies using the AssemblyKeyFile, AssemblyKeyName, and AssemblyDelaySign assembly-level custom attributes.

NOTE

We recommend that all developers take advantage of the strong name features of the CLR and digitally sign their assemblies with strong names. Only strongly named assemblies can be added to the Global Assembly Cache, and strong names provide a very high degree of protection against accidental or malicious tampering with your assemblies. Also, version checking and side-by-side execution are only available for strongly named assemblies. Note that once you strong name your assembly, you will have to annotate it with the AllowPartiallyTrustedCallersAttribute if you want it to be callable from semitrusted assemblies.




Page 1 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel