An Overview of .NET Framework Enhancements


When Microsoft started designing .NET, the world of computing was moving from the local area network (LAN) and wide area network (WAN) to the Internet. The individual user and group approach Windows used didn’t necessarily reflect the best way to pursue security in a distributed environment. In addition, the current environment was too open to potential attacks from outside.

To overcome the security problems inherent in Windows, Microsoft enhanced the role-based security approach originally found in COM+ to work as a general programming methodology. In addition, the managed environment maintains better control over resources that tend to create problems in the unmanaged Windows environment. These two security changes, along with the object-oriented programming strategy of the .NET Framework, summarize what you’ll find as security enhancements in the .NET Framework. Here’s a list of the critical security enhancements for the .NET Framework.

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 define how the administrator wants to secure a system.

Code Access Security The CLR uses this feature to determine whether all of the assemblies in a calling chain (stack) have rights to use a particular resource or perform a particular task. All of 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 a cracker’s code can’t intercept rights that it doesn’t deserve.

Defined Verification Process 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. This verification process ensures that the code doesn’t include any fatal flaws that would keep it from running. The checks also determine whether 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 important, other assemblies can’t affect it).

Role-based Security 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 task 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 logon 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 of the features of a program, but individual users may only have access to a subset of the features.

Cryptography 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 cryptography techniques suspect. The .NET Framework supports the latest cryptographic techniques, which ensures your data remains safe.

Separate Application Domains 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.

The sections that follow detail the enhancements found in the .NET Framework. There’s a lot more to .NET security than you might think. The major shift in strategy may mean that you have to rework your programs, use a new strategy, or rely on alternatives such as using PInvoke (see Chapters 14 and 15 for details).

Tip

Microsoft has made changes to the security implementation for the .NET Framework 1.1. For example, applications assigned to the Internet zone now receive the constrained rights associated with that zone, rather than no rights at all. See the overview at http://msdn.microsoft.com/netframework/productinfo/ for additional security updates.

Using Role-based Security

Role-based security asks the question of whether some entity (a user, the system, a program) is in a particular role. If it’s in that role, the entity can likely access a system resource or application feature safely. The concept of a role is different from something more absolute like a group. When you’re a member of a group, you have the same access whether you access the system from a local machine or the Internet. A role does include the idea of group membership, but this is membership based on the environment—the kind of access requested in a given situation from a specific location. An entity’s security role changes, rather than being absolute.

Many of the role-based security features you need appear as part of the System.Security.Principal namespace. You’ll learn more about this namespace and the class it contains in Chapter 2. However, let’s look at a simple example, Listing 1.1, of how role-based security can work for checking a user’s information. (You can find this code in the \Chapter 01\ C#\RoleBased or \Chapter 01\VB\RoleBased folder of the source code located on the Sybex Web site.)

Listing 1.1 Using the IsInRole() Method

start example
private void btnTest_Click(object sender, System.EventArgs e) {    WindowsPrincipal  MyPrincipal;   // The role we want to check.    AppDomain         MyDomain;      // The current domain.    StringBuilder     Output;        // Example output data.    Array             RoleTypes;     // Standard role types.    // Set the principal policy for this application.    MyDomain = Thread.GetDomain();    MyDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);    // Get the role and other security information for the current    // user.    MyPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;    // Get the user name.    Output = new StringBuilder();    Output.Append("Name: " + MyPrincipal.Identity.Name);    // Get the authentication type.    Output.Append("\r\nAuthentication: " +       MyPrincipal.Identity.AuthenticationType);    Output.Append("\r\n\r\nRoles:");    // Create an array of built in role types.    RoleTypes = Enum.GetValues(typeof(WindowsBuiltInRole));    // Check the user’s role.    foreach(WindowsBuiltInRole Role in RoleTypes)    {       // Store the role name.       if (Role.ToString().Length <= 5)          Output.Append("\r\n" + Role.ToString() + ":\t\t");       else          Output.Append("\r\n" + Role.ToString() + ":\t");       // Store the role value.       Output.Append(          MyPrincipal.IsInRole(WindowsBuiltInRole.User).ToString());    }    // Output the result.    MessageBox.Show(Output.ToString(),                    "User Role Values",                    MessageBoxButtons.OK,                    MessageBoxIcon.Information); }
end example

The code begins by obtaining the domain for the current thread. The concept of the application domain appears as the Separate Application Domains bullet. This call is the demonstration of how the feature works. The program is executing in an application domain and you can obtain information about that domain. In this case, the code sets the security policy for this domain equal to the same policy Windows uses. The application is now executing with the same policy that the user has when working with Windows. You could theoretically change that policy depending on conditions such as user location.

Now that the code has set the security policy for the thread, it uses that information to create a WindowsPrincipal object, MyPrincipal. This object knows all kinds of security information about the user. The code shows how you can obtain the username and the method of authentication used.

The most important use for MyPrincipal is to determine which roles the user is in. The book hasn’t actually defined any roles yet, so the example uses the WindowsBuiltInRole enumeration to check the standard types. If the user is in the requested role, the IsInRole() method returns true. This value is converted to a string and placed in Output. Figure 1.1 shows typical output from this example. Of course, the results will change when you run the program on your system because the dialog box will reflect your name and rights.


Figure 1.1: A view of the output from an IsInRole() method check

The important concept to take away from this example is that role-based security performs a similar task to standard Windows security, but using a different and more flexible technique. Because of the differences between Windows security and role-based security, you may need to rely on the standard Win32 API version, especially when working in an environment that has a mix of managed and unmanaged code. Chapters 14 and 15 discuss the Win32 API approach for the managed environment.

Executing Code in the Managed Environment

Instead of simply monitoring and managing the user and other entities that want access to resources or applications, the .NET Framework also monitors the code. Code access security is an important feature because it places the security burden on the code itself, which makes circumventing security measures much more difficult. Because the focus is on the code, a cracker can’t use impersonation techniques. It’s still possible to attack the code, but most crackers will look for easier targets.

The .NET Framework uses two techniques to ensure proper code access: imperative and declarative. Imperative security relies on classes that you use within your code and CLR interprets at runtime, while declarative security relies on attributes you place at the head of an element and CLR interprets at link time. You can find a complete description of the differences and usage techniques in Chapter 4. For now, let’s look at the easier of the two: imperative security. Listing 1.2 shows an example of imperative security used for gaining access to a file resource. (You can find this code in the \Chapter 01\C#\Imperative or \Chapter 01\VB\Imperative folder of the source code located on the Sybex Web site—make sure you change the resource file location to match your system.)

Listing 1.2 Relying on Imperative Security for File Security

start example
private void btnDeny_Click(object sender, System.EventArgs e) {    FileIOPermission  FIOP;       // Permission object.    Stream            FS = null;  // A test file stream.    // Create the permission object.    FIOP = new FileIOPermission(FileIOPermissionAccess.Read,                                "D:\\Temp.txt");    // Deny access to the resource.    FIOP.Deny();    // Try to access the object.    try    {       FS = new FileStream("D:\\Temp.txt",                           FileMode.Open,                           FileAccess.Read);    }    catch(SecurityException SE)    {       MessageBox.Show("Access Denied\r\n" +                       SE.Message,                       "File IO Error",                       MessageBoxButtons.OK,                       MessageBoxIcon.Error);       return;    }    // Display a success message.    MessageBox.Show("File is open!",                    "File IO Success",                    MessageBoxButtons.OK,                    MessageBoxIcon.Information);    // Close the file if opened.    FS.Close(); } private void btnAllow_Click(object sender, System.EventArgs e) {    FileIOPermission  FIOP;       // Permission object.    Stream            FS = null;  // A test file stream.    // Create the permission object.    FIOP = new FileIOPermission(FileIOPermissionAccess.Read,                                "D:\\Temp.txt");    // Allow access to the resource.    FIOP.Assert();    // Try to access the object.    try    {       FS = new FileStream("D:\\Temp.txt",                           FileMode.Open,                           FileAccess.Read);    }    catch(SecurityException SE)    {       MessageBox.Show("Access Denied\r\n" +                       SE.Message,                       "File IO Error",                       MessageBoxButtons.OK,                       MessageBoxIcon.Error);       return;    }    // Display a success message.    MessageBox.Show("File is open!",                    "File IO Success",                    MessageBoxButtons.OK,                    MessageBoxIcon.Information);    // Close the file if opened.    FS.Close(); }
end example

The btnDeny_Click() method will always fail because the imperative security call, FIOP.Deny(), denies access to the file. Notice how the code initializes the FileIOPermission object before using it. The code requires a full path to the file in question. The test actually occurs when the code uses the FileStream() constructor to try to open the file. When the code fails, the catch statement traps the error using a SecurityException object, SE.

The btnAllow_Click() method looks similar to the btnDeny_Click() method. However, this method succeeds because the code calls the FIOP.Assert() method. You can use the Assert() or Demand() methods to allow access to an object. One note on this example: make sure you always use the Close() method to close the file after a successful open. Otherwise, your code could potentially cause problems on the host system.




.Net Development Security Solutions
.NET Development Security Solutions
ISBN: 0782142664
EAN: 2147483647
Year: 2003
Pages: 168

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net