Code-access security, introduced in .NET 1.0, is probably the single differentiating capability-wise aspect of .NET compared with unmanaged code. The core advantage of .NET compared with the unmanaged world of C++ and COM is one of productivity, not capability. With the exception of code-access security, virtually anything that can be done by .NET can be done with unmanaged code. Code-access security is built into the very fabric of .NET, affecting every operation in managed code—something that unmanaged code can never achieve. The first release of WCF offered no support for code-access security. The System.ServiceModel assembly did not allow any partially trusted callers, and by demanding full trust of all its callers, WCF disabled code-access security support. This meant that developers wanting to take advantage of code-access security were very limited in their endeavor. Developers could use permission attributes to restrict the permissions granted to their services, but as discussed next, this came at a nontrivial cost and liability. Developers could manipulate the proxy to enable partially trusted clients to call WCF services (by granting it and asserting full trust, as discussed next), but in so doing, they waived all benefits of code-access security toward the clients. Furthermore, developers had no way of hosting a WCF service in a partial-trust environment.
The second release of WCF introduced rudimentary support for code-access security for some of the HTTP bindings, and only for a limited set of scenarios. For this limited support, WCF had to allow for partially trusted callers to the System.ServiceModel assembly. This change enabled me to write a small framework that provides for comprehensive support for code-access security, enabling partially trusted clients, partially trusted services, and partially trusted hosts, all without compromising the WCF programming model or code-access security. That framework and the approach leading to it are the subjects of this appendix. You will also get to see some advanced WCF and .NET programming techniques along the way.[10]
Code-Access Security at a Glance
.NET defines 24 different security permissions, governing almost any type of operation. There are file I/O permissions, UI permissions, reflection permissions, security permissions, network permissions, data access permissions, and so on. Each permission type can be instantiated for a particular permission instance, such as permission to read from a specific file in the case of the file I/O permission or permission to display specific types of windows with the UI permission. Each permission type may also be completely denied (such as no file I/O operations at all) or completely granted (such as unrestricted file I/O access).
Permissions are grouped into permission sets, and every assembly is always assigned a set of permissions. .NET defines five standard permission sets, such as FullTrust (implies all permissions) and Execution (permission to access only the CPU). Administrators can use the .NET configuration tool to define custom permission sets, and developers can either define custom permission sets programmatically, use a permission set file, or define a ClickOnce application manifest with the permission set required by their application. Upon loading an assembly, the CLR assigns that assembly its permissions. Assemblies are granted these permissions based on some form of evidence substantiating their identities. The evidence may be origin-based, requiring an examination of where the assembly is loaded from (for example, all code coming from the global assembly cache [GAC] is granted full trust), or the permissions may be granted on the basis of some form of content-based evidence, such as the strong name of the assembly.
Each app domain is always assigned a permission set called the app domain security policy, and any assembly loaded in that app domain can only perform operations allowed by that permission set. Attempts to perform other operations will result in a security exception. By default, new app domains are launched with the FullTrust permission set, and since all code originating from the local machine also gets FullTrust by default, most .NET applications just work out of the box, while in effect not utilizing code-access security at all. This renders the code (and the user, the data, the machine, or even the network) susceptible to a variety of problems, from security attacks such as viruses or worms to plain user mistakes—just like unmanaged code.
Code that is executing in less than full trust is called partially trusted code. Whenever any piece of managed code tries to access any resource or perform any operation against the .NET Framework (including interoperating with unmanaged code), .NET verifies that the assembly containing that code has the required permissions to perform the operation. If that assembly lacks a demanded permission, .NET throws a security exception, thus aborting the operation. However, since a trusted assembly can be lured by a malicious less-trusted assembly into performing operations the latter does not itself have permission to execute, it is insufficient to demand the permissions of only the assembly performing the operations. .NET therefore walks the entire stack of callers, verifying that every caller up the stack has the required permissions. This stack walk is called a security demand, and it is performed regardless of the assembly's permissions. Your code can also assert a permission—that is, stop a stack walk by asserting that every caller up the stack has the demanded permissions. You can only assert permissions you already have, and only if you have the special security assertion permission. It is always a good idea when asserting one permission to demand another in its place.
Developers can demand or assert permissions programmatically using dedicated permission classes, or use a matching set of attributes. Developers can also actively refuse permissions at the assembly, class, or method level. Refusing permissions or permitting only the limited set of permissions the code requires to execute reduces the cross section for a luring attack. For more on code-access security, see Chapter 12 of my book Programming .NET Components, Second Edition (O'Reilly), where I devote more than 100 pages to this fundamental technology and its application.
[10] I first published my technique for supporting CAS in WCF in the articles "Code Access Security in WCF, Part 1" and "Code Access Security in WCF, Part 2" (MSDN Magazine, April and July 2008).
13.58.209.201