June 17, 2010, 10:14 a.m.
posted by fractal
Configuring Security
The .NET Framework offers a wide variety of security features. You can choose to run your machine in wide-open mode, with every user allowed to execute any .NET code, or you can lock down things selectively. You can control which programs have access to which resources, or which users have the right to execute which programs. Broadly speaking, .NET security breaks down into two separate areas:
I'll cover both these types of security in this chapter, beginning with code access security. The .NET Framework also includes other security features, notably public key and private key encryption, which are not a part of the 70-320 exam. Understanding Code Access SecurityCode access security controls what code can do on your computer. Code access security is centered around permissions to use resources. The .NET Framework has an entire object-oriented system for managing code access security and the associated permissions. In the following sections, you'll learn about concepts involved in code access security:
You'll also learn how to manage code access security. In particular, code can request permissions on a very fine-grained scale, and the administrator can choose to allow permissions on an equally fine-grained scale. Understanding PermissionsCode access security is based on specific permissions that the Common Language Runtime (CLR) can grant to or deny from code. For example, the authorization to read or write information in the Windows Registry requires the RegistryPermission permission on that part of your code. As you'll see later in the chapter, code can make four different types of permission requests:
The CLR decides, based on a variety of factors (including the origin of the code and the information in the machine and application configuration files), whether a particular permission should be granted. If a piece of code is unable to obtain the minimum permissions that it requires, that piece of code does not execute. The security settings of the computer determine the maximum permissions that code can be granted, but code is allowed to request (and receive) fewer permissions than the maximum. The .NET Framework groups permissions into three types:
Each permission in the .NET Framework is represented by a particular class that derives from System.Security.CodeAccessPermission. Figure lists the permissions, which are the most important for controlling the actions that code can take on a particular computer.
NOTE Custom Permissions If none of the code access permissions in Figure are quite right for your application, you can also define custom permissions. Custom permissions are discussed later in this chapter, in the section "Using Custom Security Attributes." Requesting Minimum PermissionsTo start working in the .NET security framework, your code can request the minimum permissions that it needs to function correctly. Step-by-Step 11.1 demonstrates the syntax for making such a request.
The code in Step-by-Step 11.1 requests permissions by applying an attribute to an assembly. The FileDialogPermissionAttribute enables the assembly to request the FileDialogPermission permission, which in turn allows access to the system's file dialog boxes. In this particular case, the code runs without any problem, which means that it was granted the requested permission. By default you have full permissions to run any code that originates on your own computer. To see code access security in action, you need to learn how to manage the permissions granted to code on your computer. But first, you need to understand the concepts of code groups and permission sets. Code Groups and Permission SetsA code group is a set of assemblies that share a security context. You define a code group by specifying the membership condition for the group. Every assembly in a code group receives the same permissions from that group. However, because an assembly could be a member of multiple code groups, two assemblies in the same group might end up with different permissions. The .NET Framework supports seven different membership conditions for code groups:
Permissions are granted in permission sets. A permission set is a set of one or more code access permissions that are granted as a unit. If you want to grant only a single permission, you must construct a permission set that contains only that single permission; you can't grant permissions directly. The .NET Framework supplies seven built-in permission sets:
You can also create your own custom permission sets, as described in the "Granting Permission" section. Granting PermissionThe easiest way to grant or deny permissions in the .NET Framework is to use the Microsoft .NET Framework Configuration tool, as in Step-by-Step 11.2.
In Step-by-Step 11.2, you first create a permission set (based on the built-in Everything permission set) that includes every permission except for the permission to use the file dialog boxes. You then create a code group that contains the executable file for the StepByStep11.1 example, and then you assign the No FileDialog permission set to this code group. The result is that the code cannot run because it requires at a minimum the one permission that the new security policy cannot grant to it. Imperative SecurityRequesting permissions through the use of attributes is known as declarative security. There's a second method to request permissions, known as imperative security. With imperative security, you create objects to represent the permissions that your code requires (see Step-by-Step 11.3). Guided Practice Exercise 11.1 will give you additional practice with imperative security. NOTE Recalculate Hash If you create a code group with the cryptographic hash membership condition, you need to recalculate the hash whenever you make a change in the code. Compiling the project with changed code results in a different hash value than the one that you calculated previously (while creating a code group). You can recalculate the hash by using the Microsoft .NET Framework Configuration tool. Locate the node for the code group and then right-click the code group and select Properties. On the Membership Condition tab, select Import. Browse to the project's executable file, click Open, and then click OK.
In Step-by-Step 11.3 you construct a FileDialogPermission object that represents unrestricted access to the file dialog boxes. It then calls the Demand() method of that object to demand the permission from the operating system. When the security policy is such that the permission cannot be granted, the code throws an exception. EXAM TIP Imperative Versus Declarative Security The only time you absolutely have to use imperative security is when you need to make security decisions based on factors that are known only at runtime, such as the name of a particular file. In many other cases declarative security is easier to use. Computing PermissionsDetermining the actual permissions applied to any given piece of code is a complex process. To begin the process, you should think about permissions at the Enterprise level only. The CLR starts by examining the evidence that a particular piece of code presents to determine its membership in code groups at that level. Evidence is just an overall term for the various identity permissions (publisher, strong name, hash, and so on) that can go into code group membership. Code groups are organized into a hierarchy; in Step-by-Step 11.2 you create the StepByStep11_1 code group as a child of the All Code code group. In general, the CLR examines all the code groups in the hierarchy to determine membership. However, any code group in the hierarchy may be marked as Exclusive (that's the effect of the check box that you select when creating the StepByStep11_1 code group). The CLR stops checking for group membership if code is found to be a member of an Exclusive code group. Either way, code is determined to be a member of zero or more code groups as a first step. Next, the CLR retrieves the permission set for each code group that contains the code. If the code is a member of an Exclusive code group, only the permission set of that code group is taken into account. If the code is a member of more than one code group and none of them is an Exclusive code group, all the permission sets of those code groups are taken into account. The permission set for the code is the union of the permission sets of all relevant code groups. That is, if the code is a member of two code groups and one code group grants FileDialog permission but the other does not, the code has FileDialog permission from this step. The process accounts for the permissions at one level (the Enterprise level). But there are actually four levels of permissions: Enterprise, Machine, User, and Application Domain. Only the first three of these levels can be managed within the .NET Framework Configuration tool. However, if you need specific security checking within an application domain (which, roughly speaking, is a session in which code runs), you can do this in code. An application domain can reduce the permissions granted to code within that application domain, but it cannot expand them. The CLR determines which of the four levels are relevant by starting at the top (the Enterprise level) and working down. Any given code group can have the LevelFinal property, in which case the examination stops there. For example, if code is a member of a code group on the Machine level and that group has the LevelFinal property, only the Enterprise and Machine levels are considered when security is assigned. The CLR computes the permissions for each level separately and then assigns the code the intersection of the permissions of all relevant levels. That is, if code is granted FileDialog permission on the Enterprise and Machine levels but is not granted FileDialog permission on the User level, the code does not have FileDialog permission. At this point, the CLR knows what permissions should be granted to the code in question, considered in isolation. But code does not run in isolation; it runs as part of an application. The final step of evaluating code access permissions is to perform a stack walk. In a stack walk, the CLR examines all code in the calling chain from the original application to the code being evaluated. The final permission set for the code is the intersection of the permission sets of all code in the calling chain. That is, if code is granted FileDialog permission but the code that called it was not granted FileDialog permission, the code is not granted FileDialog permission. EXAM TIP Determining Permissions The Microsoft .NET Framework Configuration tool can help you determine the effective permissions for a piece of code. To determine the effective permissions, right-click the Runtime Security Policy node and select Evaluate Assembly. You can see the effective permissions for an assembly here, or you can get a list of all the code groups that contribute to the assembly's permissions. Requesting Other Types of PermissionsYou might at times want to request a particular permission even though your application doesn't absolutely require that permission for a user to proceed. Optional permissions are used in such a case. If you refer to the code in Step-by-Step 11.1, you see that part of the permission attribute is the SecurityAction.RequestMinimum action. To request optional permissions, you use the SecurityAction.RequestOptional action. To make use of optional permissions in Visual C# .NET, your code must have a Main() method with a try-catch block. If optional permissions for the assembly can't be granted, this block catches the exception. If minimum permissions can't be granted, the program is shut down, whether this block is present or not. WARNING RequestOptional Versus RequestMinimum When you request minimum permissions, the CLR gives your assembly any other permissions it can in addition to the minimum permissions. When you request optional permissions, the CLR gives the assembly only those permissions, and no others. So if you make an optional permissions request, you must be sure to request all the permissions that your code requires, including user interface permissions if you display any user interface. You can also tell the CLR about permissions that you do not want your code to have. This can be useful if the code is potentially available to untrusted callers (for example, users who invoke the code over the Internet) and you want to limit the potential harm that they can do. The action for this is SecurityAction.RequestRefuse. Finally, you might want to ensure that all the code that calls your code has a particular permission. For example, you might want to raise an exception if any code in the calling stack doesn't have the RegistryPermission permission. You can do this by specifying SecurityAction.Demand in the declaration of the security attribute.
Using Custom Security AttributesIn some cases, you might find that the built-in security permissions do not fit your needs. For example, say you have designed a custom class that retrieves confidential information from your company's database and you'd like to be able to restrict permission on it by a more specific means than by limiting SQL permissions. In such cases, you can create your own custom permissions and add them to the .NET security framework. This requires quite a bit of code, and most developers never need to perform this task. But just in case you do, I outline the process here. You'll find more in-depth information, including all the code for a simple custom permission, in the "Securing Applications" section of the .NET Framework Developer's Guide. To implement a custom permission, you must create a class that inherits from the CodeAccessPermission class. Your new class must override five key methods to provide its own interfaces to the security system:
Your class must support a constructor that accepts an instance of the PermissionState enumeration (which has a value of Unrestricted or None). You might also want to implement custom constructors related to your particular business needs. For example, a database-related permission might require a constructor that accepts a server name, if permissions should be handled differently on test and development servers. Although it's not strictly required, your code should also implement a method named IsUnrestricted(), which returns true if the particular instance represents unrestricted access to the resource. This makes your custom permission more compatible with the built-in permissions in the .NET Framework. To support declarative security, you should also implement an attribute class for your permission. This attribute class should derive from CodeAccessSecurityAttribute (which in turn derives from SecurityAttribute). The class should override the CreatePermission() method of the IPermission interface. Within this function, you should create an instance of your base custom permission class and set its properties according to the parameters from the declarative security invocation. Any attribute class must be marked with the Serializable attribute so that it can be serialized into metadata, along with the class to which it is applied. For your custom permission to actually protect the intended resource, you need to make changes to both the resource and to the .NET Framework on the computers where the permission is to be used. The changes to the resource are simple: Whenever an operation that is protected by the custom permission is about to be performed, the code should demand an instance of the permission. If the calling code can't deliver the permission, your class should refuse to perform the operation. The changes you need to make to the .NET Framework are somewhat more complex than the changes that you make to the resource. First, you need to create an XML representation of your custom permission in the format that is expected by the Code Access Security Policy tool (caspol.exe). You can create this XML representation by instantiating your permission and calling its ToXml() method. Given this XML representation, caspol.exe can add a permission set to the .NET Framework that contains your custom permission. It also adds the assembly that implements the custom permission to the list of trusted assemblies on the computer. You need to perform this step on every computer where your custom permission is to be used.
|
- Comment



