1. DEFINING YOUR APPLICATION'S SECURITY NEEDS
Even though basic NT security is a good place to
start your security for an application, modern applications need more,
and you'll find this additional support in the .NET Framework. The
emphasis of NT security is on the user or other principal. A resource
has a lock on it that the user's rights unlock. The idea is simple, but
it doesn't address a critical logic error that the .NET Framework
addresses. Most of the .NET Framework additions address code — limiting
what the code can do with a resource or data regardless of what rights
the user might have. In short, the .NET Framework functionality
balances the security picture and blocks another critical source of
entry that nefarious individuals have relied upon in the past.
Table 1
describes the types of security that the .NET Framework makes available
to you. You may not need every type within your application, but it's a
good idea to use as many types as reasonable. The important issue is to
consider how users work with your application and how the application
interacts with system resources. For example, if your application
communicates with a server or the Internet, it's a good idea to encrypt
the communication to ensure that no one can listen in. Each .NET
Framework type addresses a specific security need.
Table 1. Types of Security Provided by the .NET Framework
SECURITY TYPE | PURPOSE | DESCRIPTION |
---|
Evidence-based Security | This feature determines what rights to grant to code, based on information gathered about it. | The
Common Language Runtime (CLR) examines the information it knows about
an assembly and determines what rights to grant that code based on the
evidence. The evidence is actually matched against a security policy,
which is a series of settings that defines how the administrator wants
to secure a system. |
Code Access Security (CAS) | The
CLR uses this feature to determine whether all the assemblies in a
calling chain (stack) have rights to use a particular resource or
perform a particular task. | All
the code in the calling chain must have the required rights. Otherwise,
CLR generates a security error that you can use to detect security
breaches. The purpose of this check is to ensure that external code
can't intercept rights that it doesn't deserve. Note that the policy
portion of CAS has been removed in the .NET Framework 4. |
Defined Verification Process | The verification process ensures that the code doesn't include any fatal flaws that would keep it from running. | Before
the Just-in-Time (JIT) compiler accepts the Microsoft Intermediate
Language (MSIL) assembly, it checks the code the assembly contains for
type safety and other errors. The checks also determine if an external
force has modified strongly named code. After these checks are
performed, JIT compiles the MSIL into native code. The CLR can run a
verified assembly in isolation so that it doesn't affect any other
assembly (and more importantly, other assemblies can't affect it). |
Role-Based Security | This
feature determines the user's current role and assigns rights
appropriate to that role, rather than ones based on the user's login. | If
you know how Role-Based Security works in COM+, you have a good idea of
how it works in .NET. Instead of assigning security to individuals or
groups, you assign it based on the role that an individual or group
will perform. The Windows Security Identifier (SID) security is limited
in that you can control entire files, but not parts of those files.
Role-Based Security still relies on identifying the user through a
login or other means. The main advantage is that you can ask the
security system about the user's role and allow access to program
features based on that role. An administrator will likely have access
to all the features of a program, but individual users may only have
access to a subset of the features. |
Cryptography | The system uses this feature to keep outsiders (human or computer) from reading data in any location. | The
advantages of cryptography are many. The concept is simple — you make
data unreadable by using an algorithm, coupled with a key, to mix the
information up. When the originator supplies the correct key to another
algorithm, the original data is returned. Over the years, the power of
computers has increased, making old cryptology techniques suspect. The
.NET Framework supports the latest cryptographic techniques, which
ensure your data remains safe. |
Separate Application Domains | This
feature keeps the code in an application separated into parts so that a
less secure part can't interfere with a more secure part. | You
can write .NET code in such a way that some of the pieces run in a
separate domain. It's a COM-type concept, where the code is isolated
from the other code in your program. Many developers use this feature
to load special code, run it, and then unload that code without
stopping the program. For example, a browser could use this technique
to load and unload plug-ins. This feature also works well for security.
It helps you run code at different security levels in separate domains
to ensure true isolation. |
2. CREATING AN APPLICATION WITH ENHANCED SECURITY
You've seen in previous sections that the .NET
Framework provides a number of ways to secure applications using
policies. However, that's just part of the picture. The .NET Framework
also makes it possible to keep the application environment safe by
examining the evidence that other assemblies provide. For example,
knowing the zone that the assembly comes from is important.
Your application might trust an assembly from the
MyComputer zone, but not one from the Internet zone. Likewise, you need
to know the user's role. Remember that a user can wear several hats
(have different roles) and that you need to tune the application to
work within those roles. Finally, permissions, presented as evidence,
tell you what the object at hand (assembly or user) can do. The
following sections examine the use of zones, roles, and permissions in
.NET applications.
2.1. Developing for Zones
Zone membership is important because it gives you a
quick, standardized indicator of the code's source. For example, if the
code comes from the local machine, it'll be part of the MyComputer
zone. The following sections describe how to check the zone for an
assembly using code.
2.1.1. Considering Evidence within an Application
The word "evidence" brings up the vision for many
people of a court with judge and jury. The term is quite appropriate
for the .NET Framework because any code that wants to execute must
present its case before CLR and deliver evidence to validate any
requests. CLR makes a decision about the code based on the evidence and
decides how the evidence fits within the current policies (laws) of the
run time as set by the network administrator. Theoretically,
controlling security with evidence as CLR does allows applications
built upon the .NET Framework to transcend limitations of the
underlying operating system. This view is largely true. However,
remember that CLR is running on top of the underlying operating system
and is therefore subject to its limitations. Here's the typical
evidence-based sequence of events:
The assembly demands access to data, resources, or other protected elements.
CLR requests evidence of the assembly's origins and security documents (such as a digital signature).
After receiving the evidence from the assembly, CLR runs the evidence through a security policy.
The security policy outputs a permission based on the evidence and the network administrator settings.
The
code gains some level of access to the protected element if the
evidence supports such access; otherwise, CLR denies the request.
Note that the assembly must demand access before any
part of the security process occurs. When working with NT security, the
system normally verifies and assigns security at the front end of the
process — when the program first runs. (A program can request
additional rights later or perform other security tasks.) CLR performs
verifications as needed to enhance system performance.
Evidence includes a number of code features. CLR
divides code into verifiable and non-verifiable types. Verifiable code
is type-safe and adheres to all the policies defined by the .NET
Framework. Consequently, code output by Visual Basic is always
verifiable. Visual C# can output non-verifiable code because it
includes direct pointer manipulation features. However, in general, CLR
considers C# code verifiable. Visual C++ is a little less verifiable
because it not only includes pointer support, but also such functions
as reinterpret_cast. Older code, such as that found in most
Windows DLLs and COM objects, is always non-verifiable. Interestingly
enough, loading unverifiable code is a right that CLR grants to local
applications only (as a default). Remote programs have to request this
right.
CLR defines two kinds of evidence: assembly and host. You can create any number of custom evidence types by deriving from the Evidence
class. Any custom evidence resides within the assembly as assembly
evidence. CLR also ships with seven common evidence classes that cover
most needs. These seven classes provide host evidence because Microsoft
implemented them as part of the host (CLR).
ApplicationDirectory
Hash
Publisher
Site
StrongName
URL
Zone
The ApplicationDirectory, Site, URL, and Zone classes show where the code came from. The Publisher and StrongName classes tell who wrote the code. Finally, the Hash
class defines a special number that identifies the assembly as a unique
entity — it shows whether someone has tampered with the content of the
assembly.
2.1.2. Configuring the Check Membership Example
The example begins with a Windows Forms application. You add a button, Test (btnTest), to test the example code. In addition, you need to add the following using statements:
using System.Reflection;
using System.Security;
using System.Security.Policy;
2.1.3. Creating the Check Membership Code
Each of the host evidence classes has an associated membership condition class. For example, the ApplicationDirectory class, which is the evidence presented to the policy, uses the associated ApplicationDirectoryMembershipCondition
class to determine its membership status. When CLR passes evidence to
one of the membership classes, the object determines if the assembly in
question belongs to a particular code group. If the assembly is a
member of the code group, then CLR authorizes the assembly to perform
code group tasks. Listing 2 shows a typical example of membership testing.
Example 2. Discovering code group membership
private void btnTest_Click(object sender, System.EventArgs e) { // Get the current assembly. Assembly Asm;
Asm = Assembly.GetExecutingAssembly();
// Get the evidence from the assembly. Evidence EV; EV = Asm.Evidence;
// Create a membership condition check. ZoneMembershipCondition ZoneMember; ZoneMember = new ZoneMembershipCondition(SecurityZone.MyComputer);
// Check for application directory membership. if (ZoneMember.Check(EV)) MessageBox.Show("Assembly is a member."); else MessageBox.Show("Assembly doesn't belong."); }
|
The code begins by accessing the assembly to get the
evidence needed for this check. The example gains access to the current
assembly using the GetExecutingAssembly() method. However, you could also use calls such as LoadAssembly() to load an external assembly.
Once the code has access to the assembly, it uses the Evidence property to get all the evidence for the assembly. Most assemblies support four kinds of evidence as a minimum: Zone, URL, StrongName, and Hash.
This code checks for Zone class membership using the ZoneMembershipCondition object ZoneMember. As part of creating ZoneMember, you must define the SecurityZone enumeration member to check.
The Check() method returns a simple Boolean value indicating whether the assembly is part of the specified class, which is SecurityZone.MyComputer
in this case. Because you're executing this program from your desktop,
the check likely passes in this case. However, if you were to check for
some other zone, the check would fail. Note that checking membership
doesn't generate a permission object — all this check does is tell you
when an assembly has a particular membership.