12.1. The .NET Security Architecture.NET component-oriented security is based on an elegant concept: using an administration tool, the system administrator grants assemblies certain permissions to perform operations with external entities such as the filesystem, the Registry, the user interface, and so on. .NET provides the system administrator with multiple ways to identify which assembly gets granted what permission and what evidence the assembly needs to provide in order to establish its identity. At runtime, whenever an assembly tries to perform a privileged operation or access a resource, .NET verifies that the assembly and its calling assemblies have permission to perform that operation. Although the idea is intuitive enough, there are a substantial number of new terms and concepts to understand before configuring .NET security for your own applications. The rest of this section describes the elements of the .NET security architecture. The next sections describe how to administratively configure security and take programmatic control over security. 12.1.1. PermissionsA permission is a grant to perform a specific operation. Permissions have both a type and a scope. A file I/O permission is different from a user-interface permission in type because they control access to different types of resources. Similarly, a reflection permission is different from an unmanaged code access permission because they control the execution of different types of operations. In scope, a permission can be very narrow, wide, or unrestricted. For example, a file I/O permission can allow reading from a particular file, while writing to the same file may be represented by a different file I/O permission (narrow scope). Alternatively, a file I/O permission may grant access to an entire directory (or a drive), or grant unrestricted access to the filesystem. .NET defines 25 types of permissions that govern all operations and resources an application is likely to use (see Table 12-1). Of particular interest is the Security permission type, which controls both sensitive operations and security configuration. The list of privileged operations includes execution, invocation of unmanaged code, creating and controlling app domains, serialization, thread manipulation, and remoting configuration. The security configuration aspect includes permission to assert granted permissions; skip assembly verification; control security policies, evidences, and principals; and extend the security infrastructure. These facets are described later.
12.1.2. Permission SetsIndividual permissions are just thatindividual. To function properly, a given assembly often requires a set of permissions of particular scope and type. .NET allows system administrators to use permission sets, or collections of individual permissions. A permission set can contain as many individual permissions as required. Administrators can construct custom permission sets, or they can use pre-existing, well-known permission sets. .NET provides seven predefined permission sets, also known as named permission sets: Nothing, Execution, Internet, LocalIntranet, Everything, FullTrust, and SkipVerification. Table 12-2 presents the individual permissions granted by each named permission set.
The named permission sets offer a spectrum of trust:
12.1.3. Security EvidenceSystem administrators grant permissions to assemblies based on the assembly's identity. The question is, what sort of evidence should an assembly present to .NET in order to establish its identity? A security evidence is some form of proof that an assembly can provide to substantiate its identity. Evidences are vital for .NET security, because without them rogue assemblies can pretend to be something they aren't and gain unauthorized access to resources or operations. There are two types of evidences: origin-based and content-based evidences. An origin-based evidence simply examines where the assembly is coming from and is independent of the actual content of the assembly. The standard origin-based evidences are Application Directory, GAC, Site, URL, and Zone. A content-based evidence examines the content of the assembly, looking for a specific match with specified criteria. The standard content-based evidences are Strong Name, Publisher, and Hash. There is no relationship between permission sets and evidences. A single assembly can be granted multiple permission sets and satisfy a different evidence for each permission set, or it can satisfy the same evidence associated with multiple permission sets. .NET also defines a wildcardthe All Code evidence. Here is a description of the available evidences and how to select an appropriate security evidence. 12.1.3.1 The All Code evidenceThe All Code evidence is satisfied by all assemblies. 12.1.3.2 The Application Directory evidenceThe Application Directory evidence is satisfied by all assemblies coming from the same directory as or a child directory of the running application. Typically, this evidence allows an application to trust code deployed together with it but distrust other code on the same machine or anywhere else. 12.1.3.3 The GAC evidenceThe GAC evidence is satisfied by all assemblies originating from the GAC. Because only an administrator can install assemblies in the GAC, the GAC evidence is used to implicitly demand that whoever installed the evaluated assembly was an administrator. Assemblies that satisfy the GAC evidence are somewhat more trustworthy than assemblies installed by non-administrators, but to what degree is a question of judgment. 12.1.3.4 The Site evidenceThe Site evidence is satisfied by all assemblies coming from a specified site, such as http://www.somesite.com or ftp://www.somesite.com. The protocol (and port number, if specified) is ignored, and only the top-level domain portion is used. .NET also ignores any subsite specifications, such as the /myfolder in http://www.somesite.com/myfolder, and extracts the domain name only. Sites can also point to a specific machine, as in tcp://somemachine/myfolder. 12.1.3.5 The URL evidenceThe URL evidence is satisfied by all assemblies coming from a specified URL. The URL evidence is more specific than the Site evidence, because .NET takes into account protocol, port number, and subfolders. For example, the following are considered different URL evidences but identical Site evidences: You can use an asterisk at the end of a URL to indicate that the URL evidence applies to all code coming from a sub-URL as well:
12.1.3.6 The Zone evidenceThe Zone evidence is satisfied by all assemblies coming from the specified zone. .NET defines five zones:
12.1.3.7 The Strong Name evidenceThe Strong Name evidence is satisfied by all assemblies whose public keys match a specified key value. The Strong Name evidence is an excellent way to trust all code coming from a particular vendor, assuming the vendor uses the same public key to sign all its assemblies. The Strong Name evidence can optionally contain the assembly names and/or version numbers. As a result, the system administrator can opt to trust only a particular version of a specific assembly coming from a particular vendor identified by a public key. That said, the name and version typically are not supplieddoing so implies that a vendor can be trusted with only that particular assembly or version, which is conceptually inconsistent with the notion of a trustworthy vendor. 12.1.3.8 The Hash evidenceThe Hash evidence grants the permission set associated with it to the single assembly whose computational hash matches a specified hash. The assembly in question need not have a strong name. As a result, the Hash evidence is useful only for uniquely identifying an assembly with a friendly name and granting permissions only to that trusted assembly. The Hash evidence is the most stringent form of evidence, but it is also the most maintenance-intensiveyou need to update it on every revision of the assembly. The Hash evidence can be used to detect changes to an assembly, even if the new assembly has the same strong name and version number. System administrators can configure which cryptographic hashing algorithm to use: either SHA1 (the default) or MD5. 12.1.3.9 The Publisher evidenceThe Publisher evidence is satisfied by all assemblies that are digitally signed with a specified certificate, such as AuthentiCode. To digitally sign an assembly with a certificate, first build it, and then use the SignTool.exe command-line utility, specifying the assembly to sign and the file containing the digital certificate. SignTool.exe can optionally launch a wizard to guide you through the signing process. 12.1.3.10 Selecting a security evidenceChoosing a security level to apply always involves a trade-off between risks and usability. In general, you should prefer content- to origin-based evidence, because content-based evidence is more accurate. For example, the Strong Name evidence safely and consistently identifies an assembly. With origin-based evidence such as the Site evidence, the same assembly may be trusted if it comes from one site but not trusted if it comes from a different site. In addition, origin-based evidence is more susceptible to subversion than content-based evidence. It's next to impossible to fake a strong name, but it's possible to fool your machine into thinking that a certain IP address maps to a trusted site by subverting the DNS server. Another breach of origin-based evidence is compromising the proxy server on the local network so that instead of returning an assembly from a trusted site, it returns a different assembly but makes it look like it came from a trusted site. You should do a careful threat analysis, and trust origin-based evidences only as far as the DNS and other network facilities can be trusted. Origin-based evidences let you interact with much wider sets of assemblies than content-based evidences, which require individual configuration. Origin-based evidences also let you apply a generic security blanket, without having intimate knowledge of the assemblies on the client machine; content-based evidences, on the other hand, require you to be intimately aware of the making of the assemblies. Consequently, origin-based evidences are often used by framework developers (such as Microsoft), while application developers favor content-based evidences. 12.1.4. Code Groups and Security Policies.NET uses code groups to classify assemblies when it decides on the security permissions granted for each assembly. A code group is a binding of a single permission set with a particular evidence (see Figure 12-2). Figure 12-2. A code group is a binding of a single permission set and a single evidenceTo be granted the permissions in the permission set associated with the code group, an assembly must first satisfy the evidence of that code group. However, a meaningful security policy needs to be much more granular than what a single code group with a single evidence and permission set can express. A .NET security policy is a collection of code groups. Code groups in a policy are independent of one another in every respect. They can all use the same evidence, different evidences, or a mix. Similarly, different code groups can use different or identical permission sets. The permissions granted by a policy to a given assembly is the union of all the individual permissions granted by the evaluated code groups in that policy whose evidence the assembly satisfies. For example, consider the security policy in Figure 12-3. In this figure, the assembly satisfies the evidences of code groups A, B, and C, but not the evidences required by code groups D and E. As a result, the assembly will be granted only the union of the permissions granted by code groups A, B, and C. Figure 12-3. A security policy12.1.4.1 Combining policies.NET allows administrators to provide multiple security policies. The benefit of having multiple security policies is that it enables policies to have different scopes. Some policies can be restrictive and should be applied only in specific cases, such as with individual users or machines with limited privileges. Some policies can be more permissive and apply to all machines and users in an organization. Therefore, it's quite possible that an assembly is granted some permissions by one policy but is denied the same permissions by another policy. Because all the policies must concur on the allowed permissions, the actual permissions granted to an assembly are the intersection of all the permissions granted by all the security policies (see Figure 12-4). Figure 12-4. An assembly is allowed the intersection of permissions granted by the various polices12.1.4.2 Policy levelsIn actuality, there are only four types (or levels) of security policies, and .NET is aware of these four levels. Although technically administrators can configure these policy levels in any way, the convention is to use them according to their intent. The Enterprise policy should define a policy that affects all machines in the enterprise. Each machine should have a Machine policy defining a policy specific to that machine, and the User policy should define a policy for each individual user. The system administrator configures these three policy levels. The last policy level is the Application Domain policy, which applies only to code running in a specific application domain. You can only configure the Application Domain policy programmatically, by calling the SetAppDomainPolicy( ) method of the AppDomain class. Customizing the Application Domain policy is primarily for advanced casesfor example, for creating an app domain with deliberately low permissions so that you can load untrusted code into that domain. The default Application Domain policy grants all code full trust. Tool vendors can also take advantage of the App Domain policy. For example, Visual Studio 2005 supports partial-trust debugging, as described later in this chapter. Partial-trust debugging relies on installing a custom App Domain security policy. (App Domain security policies are beyond the scope of this chapter. For additional information, see the MSDN Library.) System administrators typically take advantage of the hierarchical nature of the policy levels, placing policies that are more restrictive downstream and the more liberal policies upstream. This allows overall flexibility with granular security policies, tight in some places and looser in others. For example, the Enterprise policy is likely to contain only the known, must-be-blocked web sites or vendors. Other than that, the Enterprise policy can be very liberal, permitting all other operations and zones. Individual machines can be restricted if necessary, via the Machine policy. For instance, a development machine can have more permissions than a public machine in a reception area. Similarly, some users (such as system administrators) can have liberal, if not unrestricted, User policies, while non-technical staff can have very restricted User policies, even if they all share the same machine. 12.1.5. How It All Works TogetherWhen .NET loads an assembly, it computes the permissions that assembly is granted: for each security policy, .NET aggregates the permissions from the evaluated code groups satisfied in that policy, and then .NET intersects the policies to find the combined overall collection of permissions the assembly is granted. That set of permissions is calculated only once (per app domain), and it persists in memory for as long as the assembly remains loaded. Whenever an assembly invokes calls on one of the .NET Framework classes (or any other class, including your own, as explained later), that class may demand from .NET that the assembly calling it have the required security permissions to access it. For example, the file I/O classes demand appropriate file I/O permissions, and Windows Forms applications demand user interface permissions. If the assembly doesn't have the appropriate security permissions, a security exception is thrown. However, it isn't sufficient that the assembly that called the demanding class has the requested permissionsif .NET were to check for permissions only on the assembly immediately up the call chain, that could constitute a security breach. Imagine a malicious assembly that doesn't have the required permissions to access a class such as FileStream. That assembly could work around the lack of permissions by calling instead a benign assembly that has the permissions to do its dirty work for it. Therefore, whenever a class demands security permission checks, .NET traverses the entire call stack, making sure that every assembly up the call chain has the required permissions. This is known as the security permission stack walk. When the first assembly without permissions is found during the stack walk, .NET aborts the stack walk and throws an exception at the point where the original security demand took place.
|