5. Code Access Security
If I were to choose one reason for people to use .NET I would say Code
Access Security. I mentioned on an earlier
page that Win32 security works by giving permissions to users through an
access control list. If your account is given access through an ACL (and not
simultaneously denied that permission, which is possible) then any code you
run will have that permission. Consider this situation. You are an
administrator on the machine and so you can format the hard disks you have
installed. You start Internet Explorer and this process gets your
access token. You browse to a web site which has an ActiveX control on it. IE
downloads the control which shows you a nice tree view image of the site,
however at the same time it iterates through all the hard disks on your
machine and calls format on each. The problem is that an ActiveX
control is a DLL, ActiveX (or more accurately, COM) provides a mechanism to
load the DLL dynamically and load its classes. Code in a DLL will get the
access token that is used by the current thread, the thread that loaded the
DLL. This means that the ActiveX control will get your access token through IE
and it is this token that is passed to the Win32 API that access secured
objects.
ActiveX controls are a good example of a situation when Microsoft marketing has got out of control and influence a decision that has no technological merit. Even the name is meaningless - ActiveX controls are COM (Component Object Model) controls and should have been named as such. At the time when ActiveX controls were released Microsoft had a serious competitor in Java. Java had Applets which were sandboxed, embedded code that were usually run as part of a web page. Microsoft Marketing was desperate for a Microsoft equivalent and OLE Controls (the replacement for Visual Basic VBX controls) were available. Someone must have made a snap decision to rename OLE Controls to ActiveX Controls and then, without considering the danger, they decided to allow IE to download such a control from any site on the internet and execute it under the current user's access token.
Microsoft fought a lengthy battle to convince the public that ActiveX control could be made 'safe' and released AuthentiCode, a code signing mechanism. However, the big weakness of code signing is that it relies on users checking the signed code's credentials, which few users do. Eventually Microsoft gave in with XPSP2 when it decided to block ActiveX controls as part of its security push.
Code access security solves this issue because it gives permissions based on evidence. There are various types of evidence but the evidence most often used is the source of the code. This means that code downloaded from a site on the internet will have different permissions than code downloaded from a site on the intranet or code that is installed on the local machine. The permissions specify the type of actions that the code can perform. Note that these permissions are applied to evidence obtained from the code and not from the user.
5.1 Jargon
First, some jargon. When an assembly is loaded the runtime gathers evidence about the assembly. Evidence is just information that describes some aspect of the assembly: where it comes from, who published it, etc. This evidence is then passed to policy objects. There are several policies and each determines the permissions that the assembly will have for that policy. Policies are just collections of expressions: if the assembly has this evidence then it should have these permissions. An action that code in an assembly can perform is called a permission. Each policy will provide a collection of permissions. The actual set of permissions that an assembly will get will be the intersection (not the union) of the permissions from each of the policies. To generate the permissions for an assembly a policy object uses code groups. A code group maps evidence to a permission set. A permission set contains zero or more permissions.
When your assembly calls another assembly the called code may demand a permission. A demand is a request to find out whether the calling assembly has the specified permission, in most cases this also involves a check that every assembly further up in the call stack also have the same permission. To do this, the runtime will do a stack walk. If an assembly in the stack does not have the permission then a security exception is thrown.
There are four policies: Enterprise, Machine, User and
Application Domain. The first three of these are administered through
a configuration file, whereas the last policy is administered through code.
The policies that can be configured are done so by editing a configuration
file and to do this the runtime provides two tools: a command line tool called
caspol and an MMC snap-in called the .NET Configuration Tool
(mscorcfg.msc).
5.2 Evidence
Let's investigate evidence and how .NET uses it. Create a strong named library (lib.cs) with the
following code:
using System.Reflection;
using System.Text;
[assembly:AssemblyKeyFile("key.snk")]
public class LibraryClass
{
public string GetInfo()
{
Assembly assem = Assembly.GetExecutingAssembly();
StringBuilder sb = new StringBuilder();
foreach (object part in assem.Evidence)
{
sb.Append(part.ToString());
}
return sb.ToString();
}
}
This will obtain the aggregated evidence object from the current assembly
and iterate over all of the individual evidence items. Create a process to use
this (app.cs):
using System.Reflection;
class App
{
static void Main()
{
LibraryClass lc = new LibraryClass();
Console.WriteLine(lc.GetInfo());
}
}
Compile all of this code and run the process, you'll find lots of output
will be printed on the console, but closer inspection shows that most of this
data is the hash for the assembly. Change the GetInfo method to ignore the
hash evidence:
{
if (part is System.Security.Policy.Hash)
sb.Append("<System.Security.Policy.Hash version=\"1\"/>\r\n");
else
sb.Append(part.ToString());
}
The evidence for the library is now given as:
<Zone>MyComputer</Zone>
</System.Security.Policy.Zone>
<System.Security.Policy.Url version="1">
<Url>file://C:/TestFolder/lib.DLL</Url>
</System.Security.Policy.Url>
<StrongName version="1"
Key="00240000048000009400000006020000002400005253413100040000010
00100754DFDD8549F6966F4497DEC2BDE97D07D6296608882A2D0010191
386375DE71919926E2F3875B58A74D13E4094D1E387C1E1ECE5E0DE5BF6
F8EA73E243DF5C30CAB1F80AA13EB9890F1E84693F0B1D9518F74CC9988
3A3BDC0957C025CD2FB8676BD23001B4038F185B93A5FD2A79103B7EB87
266B7DBB5FB51A071539ED5CF"
Name="lib"
Version="0.0.0.0"/>
<System.Security.Policy.Hash version="1"/>
This illustrates four of the seven types of host evidence: Zone,
Url,
StrongName and Hash. The other three are Site,
ApplicationDirectory and
Publisher.
|
.NET Version 3.0 Version 3.0/2.0 of the framework adds the GacInstalled host evidence.
This evidence is given to assemblies that have been installed in the GAC. |
Zone refers to the IE security zones, Url is the codebase for the library.
Site is the host name of a ftp: or http: protocol URL. ApplicationDirectory is
provided by the application and indicates the base directory for the
application. StrongName evidence is just that: the strong name of the
assembly. However, although the public key portion of the strong name will be
unique for each publisher it does not conclusively identify the publisher,
this is the reason for the Publisher evidence which contains an X.509
certificate. (Note also that in v1.0 and v1.1 of the runtime there is a bug
that allows a cracker to disable strong name validation, so you should not put
much trust in the StrongName evidence.) The Hash is
a cryptographic hash over the assembly and is better way to identify an
assembly than using the StrongName evidence because, as explained
earlier, strong names do not guarantee uniqueness.
5.3 Creating a Permission Set
To show that evidence is changed according to the source of the assembly
let's deploy the assembly to another site. In fact, if you deploy it to IIS on
the local machine that will be sufficient for the runtime to treat the library
as being obtained from a site on the intranet. Create a new folder under the
IIS wwwroot folder called bin and copy the library there:
C:\TestFolder>copy lib.dll \Inetpub\wwwroot\bin
Next create a configuration file (app.exe.config) to indicate that the library should be
loaded from the web site (you can either type this by hand, or you can use the
.NET configuration tool):
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="lib"
publicKeyToken="c7611f8614380ed3"/>
<codeBase version="0.0.0.0"
href="http://localhost/bin/lib.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
The publicKeyToken is created from your public key, to view this, type the
following at the command line and copy the public key token that is printed on
the console:
Now run the process and you'll find that an exception is thrown (this has been edited to show the relevant items):
at [snip...]
The state of the failed permission was:
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="ControlEvidence"/>
The important point here is that because the assembly is deemed to have
come from the intranet it does not have enough permission to view its own
evidence! To verify this, start the .NET Configuration Tool from the
Administrative Tools folder (mscorcfg.msc):

|
.NET Version 3.0 The screenshot, above, is for version 1.1 of the configuration tool, version 3.0/2.0 of the tool has an extra level, the root is called .NET Framework 2.0 Configuration with My Computer as its only child. To show the Runtime Security Policy you need to open the My Computer node. |
Open the Runtime Security Policy, then open Machine to get the policy defined for this machine. Next open Permission Sets to view the default permission sets and select LocalIntranet. Click on the View Permissions link to get a view control with the permissions for the permission set. Double click on the Security entry to list details of that permission. This shows that Allow Evidence Control is set to No. Note that the dialog indicates that the permission is read-only, this is because the default permission sets cannot be changed.
So that we can allow our assembly to show its evidence we must make a new permission set and apply this to the appropriate policy. Before you change the policy you may want to save the current policy. You have two options. If the policy of your machine is the default you do not need to do anything, because after you have made changes you can simply tell the configuration tool to return back to the default policy. If an administrator has already changed the policy on your machine, you should save this policy so that after these tests you can revert back to this policy. Details of how to do this will be given in a later section.
| If your account is a member of the Administrators group then you will be able to edit the Machine and the Enterprise policies. If your account is not an administrator then you'll only be able to edit the Users policy. However, XP provides the RunAs context menu for Start Menu shortcuts that allows you to run a process under a different account than the interactive user. |
The easiest way to create a new permission set is to copy an existing permission set and make modifications to the copy. So close down the Permission Viewer dialog and right click on LocalIntranet and select Duplicate. Now right click on the new permission set (Copy of LocalIntranet) and select Rename; call this MyIntranet. Right click again and select Change Permissions. This will show a dialog with all the permissions in the permission set.
![]() |
|
.NET Version 3.0 Version 3.0/2.0 of the configuration tool shows slightly different permissions for the intranet. This permission set does not contain an Event Log permission. |
Now double click on Security in the Assigned Permissions list box to get the settings for the security permission. You'll see that two flags are selected, and you should check the one that we are interested in: Allow evidence control. Click on OK, and then Finish.

Now you have to ensure that this permission set is used when the evidence indicates that the assembly came from the intranet. To do this you need to create a code group for the intranet that will give the permission set you have created. When the runtime creates evidence it will traverse through the policy matching the evidence of the assembly to code groups in the policy; it will then create a union of all the permissions for all of the code groups obtained.
Open Code Groups, then All_Code (the membership of this group is true for
all code and gives no permissions) and then select LocalIntranet_Zone. This is
the code group for code that originates from the intranet. Right click on this
and select Properties. Peruse the tabs, in particular note that the Membership
Condition tab shows that this code group occurs when the Zone evidence is the
Local Intranet. The Permission Set tab shows that the permission set is
currently set to LocalIntranet, so scroll down the pull down list box,
select MyIntranet and click on OK. You have now indicated that
all assemblies that come from the intranet, regardless of where they come
from, or what assembly it is, will get the ControlEvidence
permission. Run the process from the command line, you'll see that the exception is not thrown
and the library assembly now has the
permission to view its evidence.
<Zone>Intranet</Zone>
</System.Security.Policy.Zone>
<System.Security.Policy.Url version="1">
<Url>http://localhost/bin/lib.dll</Url>
</System.Security.Policy.Url>
<System.Security.Policy.Site version="1">
<Name>localhost</Name>
</System.Security.Policy.Site>
<StrongName version="1"
Key="00240000048000009400000006020000002400005253413100040000010
00100754DFDD8549F6966F4497DEC2BDE97D07D6296608882A2D0010191
386375DE71919926E2F3875B58A74D13E4094D1E387C1E1ECE5E0DE5BF6
F8EA73E243DF5C30CAB1F80AA13EB9890F1E84693F0B1D9518F74CC9988
3A3BDC0957C025CD2FB8676BD23001B4038F185B93A5FD2A79103B7EB87
266B7DBB5FB51A071539ED5CF"
Name="lib"
Version="0.0.0.0"/>
<System.Security.Policy.Hash version="1"/>
The evidence now has a Site item, and this, and the Url item, reflect where
the library was downloaded from. In addition the Zone indicates that the code
comes from the Intranet.
5.4 Creating a Code Group
You have indicated that all assemblies downloaded from the intranet should
have the ControlEvidence permission. This permission is powerful
and it should only be given to assemblies that you trust. The one rule that
you must always follow with security is the principle of least privilege:
| Code should be run under the least privileges that it needs to do its work, and no more. |
To do this we must use the evidence in a more targeted way. For example, I trust myself (mostly <g>) and since I know that I am the only person using my key pair I can check for that evidence.
Again, the changes should be made to the Machine policy. The first thing to do is change the LocalIntranet_Zone back to use the default LocalIntranet permission set. Now create a new permission set called MyCodePermissions, this time you need to create a blank permission set so select Permission Sets and through the context menu, select New. Type in the name, and then click Next. Now double click on the Security item, check the Allow evidence control item and click OK. Click on Finish and you have a new permission set.
Now you need to create a new code group, so select LocalIntranet_Zone under
the All_Code group and from the context menu select New. Type in the name
My_Code and click on Next. In the next dialog you need to
determine the evidence that will be checked. I want to make sure that
assemblies downloaded from the bin virtual folder on my site will
be given the permission to check its evidence. Select URL from the code
group drop down list
box and extra controls will appear pertinent to the evidence type. In the
URL text box type http://localhost/bin/*. Now
click on the Next button. On the next dialog select your MyCodePermissions
permission set from the Use existing permission set dropdown list box. Click
on Next and then Finish.

Now review what you have done: you have reverted to the previous intranet
code group which will deny your code access to the assembly evidence. Next,
you have created a code group under the LocalIntranet_Zone group that allowed any
assembly downloaded from the bin folder of your web site to have permission
to access its evidence. Now run the application and confirm that your assembly
does have the required permissions.
Of course, you only know that you have those permissions because the code works, let's see if we can view those permissions. The configuration file indicates that the library should be downloaded from the intranet, and it will be stored in the download cache. The first thing to do is clear the download cache so that you can change the library:
Next, change the library to add the following lines after the evidence has
been added to the StringBuilder object:
PermissionSet perms = SecurityManager.ResolvePolicy(assem.Evidence);
sb.Append(perms.ToString());
return sb.ToString();
You will also need to add a using statement for
System.Security to the source code. Build the library and the process and copy the library to the appropriate
IIS folder. Run the application. This will dump a load of XML to the console,
but notice the format: this is in the same form as the permission set that was
put into the non-verifiable assembly in the last section. It looks like this:
<!-- other permissions omitted -->
<IPermission
class="System.Security.Permissions.SecurityPermission,
mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
version="1"
Flags="Assertion, Execution, ControlEvidence, BindingRedirects"/>
</PermissionSet>
All permissions implement IPermission which is why this is the name of the
permission element, the type of the permission is given in the class
attribute. The Flags attribute corresponds to the Flags property of the
SecurityPermission class. This property is an enumeration of type
SecurityPermissionFlag and we have been given four:
Assertion |
The code can assert that all the code's callers have this permission. |
Execution |
The code has the permission to run. |
ControlEvidence |
Permission to access and provide evidence. |
BindingRedirects |
Permission to perform binding redirection through the configuration file. |
5.5 Evaluating an Assembly
When you are developing an assembly it is useful to determine the permissions that it will be granted. The configuration tool will do this through the Evaluate Assembly Wizard. To do this, select the Runtime Security Policy and from the context menu select Evaluate Assembly.

The dialog allows you to specify the location of the assembly. Note that
the form of the path you give affects the zone that will be used. If you give
a local path then the assembly will come from My Computer and hence it
will be fully trusted. If the path is a URI and has a computer name (or
localhost) then the zone will be Intranet. If the path is a URI
that uses a dotted format (an IP address, eg 127.0.0.1, or a
dotted name, eg www.microsoft.com) then the zone will be
Internet. In
the figure I have simulated loading the assembly from the intranet by
supplying a URI with a machine name.
You can use this tool to obtain a list of the permission sets and to see the actual permissions it will be granted. In addition you can get a list of the code groups that have been checked, this is important if you use child groups as explained later. You can also select the policy level, so you can check the permission sets obtained from each policy.
For example, select the options to evaluate just the Machine policy and view the code groups and you can verify that the code groups used for this assembly will include the My_Code code group. To see the permissions that will be granted you can use the Back button and then select the option to view the permissions.

Test this dialog to see the permissions that you get for the assembly located from different zones. Use the following table as a guide:
| File Name | Zone | Example |
| Local Path | MyComputer | C:\TestFolder\lib.dll |
| UNC | Intranet | \\MYHOST\C$\TestFolder\lib.dll |
| URI with site name | Intranet | http://localhost/bin/lib.dll |
| URI with dotted name | Internet | http://127.0.0.1/bin/lib.dll |
In this table MYHOST is the name of your machine.
You can do a similar thing with the /resolveperm and /resolvegroup switches
of caspol. For example:
Microsoft (R) .NET Framework CasPol 1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Level = Machine
Code Groups:
1. All code: Nothing
1.2. Zone - Intranet: LocalIntranet
1.2.1. All code: Same site Web.
1.2.2. All code: Same directory FileIO - Read, PathDiscovery
1.2.3. Url - http://localhost/bin/*: MyCodePermissions
Success
This indicates the code groups and the permission sets that have been
granted the assembly when only the machine policy (/m) is applied.
Note that although the UNC format and the URI with the machine name and
with localhost are all treated as if the library comes from the
intranet, only the latter (localhost) resolves to the
My_Code code group because the code group has a URL with
localhost. Similarly, if you change the URL in the code group to be
http://MYHOST/bin/lib.dll (where
MYHOST is the name of your machine) then you'll find that your
permission set will only be used if the library is loaded with that exact
URL. Thus, when you use URL evidence you must ensure that you use the same
format of the URL as will be used in your application configurations.
Note that you will find that for Intranet evidence you will find that the code will also be members of the Intranet_Same_Site_Access and Intranet_Same_Directory_Access code groups and for Internet evidence you will find that the code will be a member of the Internet_Same_Site_Access code group. These are all custom code groups and the permissions are given as raw XML. This XML is given in a label control which means that if the XML is larger than can be displayed in the label no vertical scroll bar will be shown. The only way to scroll is to select the text and drag the cursor down.
5.6 Code Group Levels
As you can see, by default the permission sets from the code groups in a policy will be combined to give the permission set used by the policy. When all policies have been resolved, the intersection of the policy permission sets will be used to generate the permission set for the assembly. The properties for a code group allow you to determine how the permission set for the current code group is combined with other code groups in the policy. The configuration tool will display code groups in a hierarchy to indicate how this combination will occur.
At
the bottom of the General page for a code group are two check boxes. The first check box is
used to set the Level Exclusive property. If this is checked and an assembly
matches the membership condition then no other code group will contribute to
the assembly's permission set. Note however, that if another code group
matches the evidence of the assembly and this group also has the Level Exclusive property then an exception will be thrown.
The Level Exclusive property is useful if you want to set specific
permissions for a particular assembly that you can identify with evidence, the
exclusive property means that the assembly will not get any more permissions
from other code groups. (Note that although other code groups in this policy
do not contribute to the permission set, other policies are still evaluated
and will also contribute to the final permission set.) In the example shown
earlier this check box was deliberately left unchecked because the permission
set granted to this code group only has the ControlEvidence
permission, and it relies on getting addition permissions from the
Local_Intranet_Zone group.

The other check box specifies the Level Final property of the code group. Usually, all the policies are applied to the assembly, the 'level' of the policy is considered to be in the order Enterprise, Machine, User, highest to lowest level. Only enterprise administers can change the Enterprise level, enterprise and machine administrators can change the Machine policy, and any user can change their own User level. A lower policy level cannot add more permissions than granted by the higher policy, but it may reduce the permissions. If you are a higher level policy administer you can prevent lower policies from being evaluated by setting the Level Final property of a code group. You would do this if you want to ensure that lower policies do not reduce the permissions you give to an assembly. If this box is checked it means that code groups below the current group will not be used.
At this point return the security policy back to its original value, as explained in the next section.
5.7 Administering Policies
All of the policy information is held as XML. Security configuration files are held in:
where <framework version> is the version you are interested in (for
example, v1.1.4322 or v2.0.50727). This will contain
enterprisesec.config for the Enterprise policy and
security.config for the Machine policy. The User policy
is held in a file called security.config and it is held in:
The MMC configuration tool and caspol will edit this these
XML files. You can edit these files by hand, but you shouldn't. In spite of
the fact that XML is text, it is not human-readable, and hence it is easy to
make mistakes when editing XML by hand. Use the tools, that is what they are
for.
If you want to try out a security policy but have to ability to return back to a
working policy, you have two options. First, if you have not change the
default policies it means that any changes that you make will be to the
default policies and hence reverting means going back to the default. To do this with caspol you use the
-reset switch with either -machine, -enterprise
or
-user for the Machine, Enterprise and User
policies or -all to revert to the default for all policies. For
the MMC configuration tool you can right click on the policy and select
Reset to revert that paticular policy to the default, or right click on Runtime
Security Policy and select Reset to reset all policies. Whenever you make a change with the caspol tool a backup is
made of the policy file and so you can use the -recover switch
(with an appropriate switch to indicate the configuration level) to revert
back to the previous version.
The second method, and the most flexible option, is to use the MMC configuration tool to create and open a new configuration file. You do this by right clicking on Runtime Security Policy and select New to create a copy of the default settings.

The dialog allows you to indicate the policy that you want to create and specify the location where the policy file will be stored. Once you have closed this dialog, the new policy file will be used and all changes will be made to this new file. When you have finished with this policy you can revert back to the previous security policy through the Open option from right clicking on Runtime Security Policy:
Again, this allows you to select the policy level (in the top half). It also gives you the option of opening the policy file from the default location or open a specific policy file. To revert back to the system policy files you should select the first option. If you want to go back to your test policy at a later stage you can use the second option.
If you are a developer, then the policy that you develop with the configuration file is vital for the correct running of your components. In this case you should start with a default configuration and then copy this configuration to your deployment machines. To do this, right click on Runtime Security Policy and select Create Deployment Package. This will create an msi file for the policy that you specify.
| I hope that you enjoy this tutorial and value the knowledge that you will gain from it. I am always pleased to hear from people who use this tutorial (contact me). If you find this tutorial useful then please also email your comments to mvpga@microsoft.com. |
Errata
If you see an error on this page, please contact me and I will fix the problem.
