Application Security Overview

 

Application Security Overview

The Microsoft .NET Framework implements a two-layer defense against security vulnerabilities. The first layer, called role-based security, allows you to control a user's access to application resources and operations; the second layer, called code access security, can control the code's access to resources and ability to perform privileged operations.

Before we plunge into ADO.NET security, you should understand some basic terms and the layers of security provided by Windows and the .NET Framework. These are the foundation for understanding ADO.NET security.

Authentication

Authentication is the process of obtaining the user's credentials, such as name and password, to identify and validate against an authority. Figure 13-1 shows a typical Web site authentication scenario in which Joe proves that he is Joe by providing a password that only Joe should know. The credentials typically contain an identifier and proof of identity. These can be a user name and password, an e-mail address and password, a user name and digital certificate, and so forth.

image from book
Figure 13-1: Authentication requires an identifier and proof of identity.

Authorization doesn't happen only when you enter an application; it also happens when you cross application boundaries to access resources such as the database server or an e-mail server.

Note that being authenticated means only that the authority believes that you are who you say you are; it does not include the granting of any permission to access resources. This is done though authorization.

Authorization

Authorization is the process of determining whether a user should be granted a specific type of access to a resource. The resource might be a file, a database, the registry, or any other controlled resource. The type of access can be read, write, add, or delete, or it can be resource specific, such as insert, update, or delete. Figure 13-2 shows that Joe, who is already trusted to be Joe based on being authenticated, is attempting to delete an order from the database. If Joe is authorized, he will succeed, but if he doesn't have Delete permission on the Orders table, his attempt will fail.

image from book
Figure 13-2: Authorization means determining whether a user should have a specific type of access to a resource.

Authorization can be performed on a user basis or a role basis, or a combination of the two. Using roles can simplify management of permissions. If a role is created for salespeople, it is easy to assign write permissions to that role instead of assigning permissions separately to each salesperson.

Impersonation

Impersonation means allowing code to execute with the identity of the client. In Chapter 9, the managed code uses the identity of the SQL Server service when executing, which means that the access level you have to resources such as files on the local hard drive might be unacceptable. If authorization is based on permissions granted to the user, you must impersonate the user's credentials. This was also demonstrated in Chapter 9.

Delegation

Impersonation across computers is called delegation. Impersonation works fine on a single machine, but it will fail if you attempt to access resources on a remote computer. Figure 13-3 shows an example in which delegation is used to delete order items from a remote database server, using Joe's credentials, but the call is coming from a database server, not directly from the user. To enable this scenario, the Windows Domain Administrator must enable Joe's account for delegation.

image from book
Figure 13-3: A typical delegation scenario in which a user must access a single server to perform an action but part of the work must be executed on a different server, using the user's credentials

You can implement delegation on Windows 2000 and later, but a Windows Domain Administrator must configure delegation for each user account, which can create security issues. Figure 13-4 shows Joe's user account settings, as viewed from the Active Directory Users And Computers tool. The Account Is Trusted For Delegation setting is selected. If the user is currently logged in when the setting is changed, the user must log off and log back on to receive this setting.


Figure 13-4: You must set the Account Is Trusted For Delegation setting for each user who needs to use delegation.

In addition to configuring delegation on the user account, each computer that is participating in delegation must be trusted for delegation. This can also be configured from the Active Directory Users And Computers tool by opening the Computers node and double-clicking the computer. This computer's properties will be opened as shown in Figure 13-5. Set the Trust Computer For Delegation option. You will need to reboot the computer to activate this setting.

image from book
Figure 13-5: The computers that are performing delegation must be trusted for delegation.

Role-Based Security

Windows provides a directory that lists users and user roles. This list defines all users who are allowed to access the computer, domain, or enterprise.

Workgroup Environment

In a workgroup environment, each computer has its own directory of users who need to access the computer (Figure 13-6). If you have 10 users and each user needs access to all of the computers, matching accounts (with matching passwords) must be created on all computers. As the user and computer count grows, this environment will become unmanageable. You can also create roles or groups on each machine to simplify the assignment of users to resources. For example, you can create a group called salespeople and assign it to all resources that salespeople need access to. When a salesperson is hired, she can be simply placed into the salespeople group. This is much quicker than assigning permissions to each user on a resource-by-resource basis.

image from book
Figure 13-6: In a workgroup environment, accounts are duplicated on each computer, which means account maintenance (such as password changes) must be done on all of the computers.

The Local Users And Groups program in Windows allows you to create new users and add them to groups. In a workgroup environment, account maintenance, such as a password change, must be done on all machines.

Domain Environment

In a domain environment, servers that are configured as domain controllers contain a directory, called Active Directory, that lists users who have access to the domain. Although workstation computers still contain a directory, it should contain only groups, called local groups, which are assigned to resources as shown in Figure 13-7.

image from book
Figure 13-7: In a domain environment, account maintenance needs to be done only once within Active Directory and is replicated to all domain controllers.

Accounts that are created in Active Directory automatically replicate to other domain controllers, which means that account maintenance, such as changing a password or adding or deleting users, needs to be done at only one location.

The Windows Server operating system contains a tool called Active Directory Users And Computers, which is used to edit Active Directory. Active Directory is very extensible and can store other information, such as certificates, user preferences, printer location information, and e-mail information. Although Active Directory is intended to hold thousands of users, it's usually best suited to providing intranet authentication, not performing Internet Web site authentication.

Code Access Security

If you are logged on to your machine and have Administrator permissions, you can do virtually anything on your machine. If you allow me to log on to your machine, I might have little or no permissions. This looks and feels right, until I find a way for you to unknowingly execute malicious code that I have written or to unknowingly give me permission to do something malicious.

This scenario might seem far-fetched, but every time you open a Web page in your browser, you are executing code that has been written by someone else and that someone could be me, the hacker.

I'm not a hacker, but I hope I've made my point. It's relatively easy to get someone to unknowingly run malicious code from the Web, and that code will execute with your permissions, which might be Administrator permissions.

Code access security (CAS) helps to minimize the problem of users unintentionally executing unsafe code. With CAS, you can limit the resources that code has access to. Even if a hacker finds a way to get into the code, no damage can be done if the code has limited access to resources. CAS is implemented by using permissions and permission sets, providing evidence of the code's origin, and applying security policies. Permissions are granted based on a security policy, using evidence information about an assembly and its origin. This granting of permissions is analogous to authorization, and the use of evidence is analogous to authentication. When code needs to access a resource, it issues a request for the appropriate permissions, and the .NET Framework security system determines whether the code can perform the desired operation.

Evidence

Evidence includes the following.

Authentication types have varying strength in proving that you are who you say you are. For example, requiring a three-digit PIN is much weaker than requiring an eight-character password that includes lowercase alphabetic characters, uppercase alphabetic characters, numbers, and special characters. The same is true for the types of evidence. For example, having an assembly with a strong name that came from a UNC path but now resides on the local computer is certainly stronger than an assembly with a strong name that came from the Internet, even if it is currently on the local computer. You can create security policies based on the strength of the evidence.

Code Access Permissions

Code access permissions define the rights to access specific resources. Many of these permissions are implemented within the .NET Framework to secure the system resources. Table 13-1 lists the code access permissions in the .NET Framework.

Table 13-1: Code Access Permissions in the .NET Framework
Open table as spreadsheet

Permission

Description

AspNetHostingPermission

Controls access permissions in ASP.NET-hosted environments

DataProtectionPermission

Controls the ability to access data and memory

DirectoryServicesPermission

Allows control of code access permissions for the System.DirectoryServices namespace

DistributedTransactionPermission

Controls the ability to access resources in a distributed transaction

DnsPermission

Controls the ability to access DNS servers

EnvironmentPermission

Controls access to the system and user environment variables

EventLogPermission

Controls access to the event logs

FileDialogPermission

Controls access to file dialog boxes in the user interface

FileIOPermission

Controls access to files and folders on the file system

GacIdentityPermission

Defines the identity permission for files originating in the global assembly cache (GAC)

IsolatedStorageFilePermission

Controls access to isolated storage

KeyContainerPermission

Controls the ability to access key containers

MessageQueuePermission

Controls access to message queues

NetworkInformationPermission

Controls access to network information and traffic statistics for the local computer

OdbcPermission

Controls database access by the ODBC data access provider

OleDbPermission

Controls database access by the OLEDB data access provider

OraclePermission

Controls database access by the Oracle data access provider

PerformanceCounterPermission

Controls access to performance counters

PrintingPermission

Controls access to printers

PublisherIdentityPermission

Represents the identity of a software publisher

ReflectionPermission

Controls access to type metadata by reflection, which is a mechanism for discovering public and private class information at runtime

RegistryPermission

Controls access to the registry

SecurityPermission

Controls ability to execute code, assert permissions, and call unmanaged code

ServiceControllerPermission

Controls the ability to start or stop services

SiteIdentityPermission

Defines the identity permission for the Web site from which the code originates

SmtpPermission

Controls access to Simple Mail Transport Protocol (SMTP) servers

SocketPermission

Controls the ability to connect to other computers by means of sockets

SqlClientPermission

Controls database access by the Microsoft SQL client

SqlNotificationPermission

Controls whether the user can use SQL notifications

StorePermission

Controls access to stores containing X509 certificates

StrongNameIdentityPermission

Defines the identity permission for strong names

UIPermission

Controls access to windows and other user interface elements

UrlIdentityPermission

Defines the identity permission for the URL from which the code originates

WebBrowserPermission

Obsolete will be removed soon because WebBrowser will not be available in semitrust (see http://go.microsoft.com/fwlink/?linkid=14202)

WebPermission

Controls the ability to connect to other computers by means of HTTP

ZoneIdentityPermission

Defines the identity permission for the zone from which the code originates

Working with CAS

The .NET Framework grants a set of permissions to every assembly when the assembly loads. Permission sets are used to determine when the assembly needs to access resources. The security policy uses the assembly's evidence to determine the permissions that should be assigned to the assembly.

Code Groups

Code groups are used to define the permissions that an assembly should receive. An assembly can be a member of many code groups. When a code group is created, it is assigned a membership condition and a set of permissions that its members should receive. For example, the code group called Internet_Zone has the membership condition that the assembly must be located on the Internet. It has no permissions. Another code group, My_Computer_Zone, has a membership condition that the assembly must be located on the current user's computer. This code group has several permissions assigned to it. Permissions are cumulative across code groups, which means that the assembly receives the union of all the permissions assigned to every code group that it is a member of.

Permission Sets

Permission sets are groupings of permissions that can be assigned to code groups as needed. The .NET Framework has built-in permission sets, and you can create custom permission sets. Table 13-2 describes the built-in permission sets.

Table 13-2: Built-in Permission Sets
Open table as spreadsheet

Permission Set

Description

Everything

Contains all standard permissions except the permission to skip validation.

Execution

Permission for code to run.

FullTrust

Full access to all resources.

Internet

Allows code to execute and create top-level windows and file dialog boxes. This code can also make Internet connections to the same site that the assembly came from and use Isolated Storage with a quota.

LocalIntranet

Allows code to execute and provides unrestricted creation of user interface elements. This permission set allows unrestricted use of Isolated Storage with no quota. It also allows DNS usage and the reading of USERNAME, TEMP, and TMP environment variables. The code can make Internet connections to the same site that the assembly came from. This permission set also allows files to be read that are in the same folder.

Nothing

Provides no permissions; code cannot run with this setting.

Runtime Security Policy Levels

Runtime security policy levels are levels of security that you can apply to the Enterprise, the Local Machine, the User, and the Application Domain. Each policy level contains its own hierarchy of code groups and permission sets, as shown in Figure 13-8. Enterprise policies can be set by the network administrator, and they affect all managed code in the Windows domain.

image from book
Figure 13-8: The .NET Framework 2.0 Configuration tool shows the hierarchy of runtime security policy levels, code groups, and permission sets.

Local Machine policies can be set by the local computer administrator. User policies can be set by the local user or local administrator. Application Domain policies can be programmatically or declaratively defined by a host, such as ASP.NET. Application Domain policies are never skipped during policy evaluation. Notice that in Figure 13-8, this policy is not displayed because the Application Domain policy is not an administrable policy level, whereas the other policies exist in a persisted policy file that administrators can alter using an administration tool like the .NET Framework 2.0 Configuration tool. Application Domain policies are optional and provide isolation, unloading, and security boundaries that are used for managed code execution.

An assembly's effective permissions are calculated by looping through all of the runtime security policy levels. Within each policy level, the assembly's evidence is collected and code group membership is evaluated. Permissions accumulate based on code group membership. The assembly receives the union of the permissions, based on code group membership. The resultant permissions are then intersected with the permissions of the next policy (Enterprise, Local Computer, User, or Application Domain), as shown in Figure 13-9.

image from book
Figure 13-9: An assembly's effective permissions are based on the intersection of the permissions in each level, and each level is evaluated based on the union of the code group permissions.

As you can tell, the ability to change the security on different levels and in different code groups means that an assembly's effective security permissions can be different on every computer, depending on the user.

Changing the Application Domain Security

Application Domain security is defined by the host. You will probably need to work with two CLR hosts: ASP.NET and SQL Server 2005.

Changing ASP.NET Application Domain Security The Application Domain security settings for ASP.NET are in the Web.config file (which is in the same folder as the machine.config file), typically the following location.

%WinDir%\Microsoft.NET\Framework\version\CONFIG\ 

The Web.config file is an XML file that contains the settings for ASP.NET. In the Web.config file, you will see a section that looks like the following.

image from book

Web.Config Security Policy

<location allowOverride="true">    <system.web>       <securityPolicy>          <trustLevel name="Full" policyFile="internal" />          <trustLevel name="High" policyFile="web_hightrust.config" />          <trustLevel name="Medium" policyFile="web_mediumtrust.config" />          <trustLevel name="Low" policyFile="web_lowtrust.config" />          <trustLevel name="Minimal" policyFile="web_minimaltrust.config" />       </securityPolicy>       <trust level="Full" originUrl="" />    </system.web> </location> 
image from book

Notice that different security policy trust levels are defined by default, and each trust level has an associated policy file that contains an XML definition of the policy level. The actual setting that ASP.NET uses is defined with the trust element, which is set to Full here, but you can easily change this to one of the defined settings, such as Medium or Minimal, and you can look at the policy files to find out what permissions each level has.

Changing the SQLCLR Application Domain Security The other host of the CLR is SQL Server 2005. In Chapter 9, we used the following commands to create a SQLCLR assembly with an Application Domain setting that uses a permission set called SAFE.

image from book

SQL: Installing the Visual Basic Assembly

CREATE ASSEMBLY VbProcs FROM 'C:\Projects\Vb\Chapter09\HiVbWorld.dll' WITH PERMISSION_SET=SAFE GO 
image from book

image from book

SQL: Installing the C# Assembly

CREATE ASSEMBLY CsProcs FROM 'C:\Projects\CS\Chapter09\HiCsWorld.dll' WITH PERMISSION_SET=SAFE GO 
image from book

The SQLCLR has three levels of execution permissions that are enforced at the assembly level:

Creating a Security Test Project

This chapter explores and tests many aspects of .NET security, so we'll need a test project. We'll create a simple Windows project called SecurityTest. Add a button to the form, doubleclick the button, and add the following code.

image from book

Visual Basic

Imports System.Security.Permissions Imports System.IO Public Class Form1    Private Sub button1_Click(ByVal sender As System.Object, _          ByVal e As System.EventArgs) Handles button1.Click       Using stream As New StreamReader("CarList.xml")          MessageBox.Show(stream.ReadToEnd())       End Using    End Sub End Class 
image from book

image from book

C#

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Security.Permissions; using System.IO; namespace SecurityTest {    public partial class Form1 : Form    {       public Form1()       {          InitializeComponent();       }       private void button1_Click(object sender, EventArgs e)       {          using (StreamReader stream = new StreamReader("CarList.xml"))          {             MessageBox.Show(stream.ReadToEnd());          }       }    } } 
image from book

Creating an XML Test File This code simply attempts to open a file called CarList.xml and display its contents in a message box. You can add the file to your project by adding an XML file to the project and placing the following XML into that file. Also change the XML file's Copy to Output Directory property to Copy if Newer.

image from book

CarList.Xml File

<?xml version="1.0" encoding="utf-8" ?> <CarList>    <Car Make="Chevy" Model="Impala"/>    <Car Make="Ford" Model="Taurus"/>    <Car Make="Dodge" Model="Caravan"/>    <Car Make="BMW" Model="Z-4"/>    <Car Make="Mazda" Model="Miata"/> </CarList> 
image from book

Assigning a Strong-Name Key File Next you must configure the project as a strong-named assembly. If you have a strong-name key file, use that or create a new strong-name file by using the project's Properties menu to create and assign the strong-name file, as shown in Figure 13-10. This creates a strong-name key file called myKey.pfx.

image from book
Figure 13-10: Creating a new strong-name file and assigning it to the project

Note 

The strong-name file provides evidence that the assembly was created by you. In the sample, the password assigned to the assembly is Hello2u. You should create a single strong-name key file with an assigned password that you use for all assemblies that you sign.

Reducing Permissions Granted to the Assembly Using this strong-named assembly, we will reduce the permissions that this assembly gets without interfering with the rest of the .NET Framework security settings. First be sure to build the SecurityTest application with the key file assigned. Open the .NET Framework 2.0 Configuration tool (from the Start | Control Panel | Administrative Tools menu). Open the My Computer | Runtime Security Policy | User | Code Groups node. Right-click the All Code node, and click New. Set the name of the new code group to SecurityTest and click Next. Set the membership condition type to Strong Name. Click Import, and select the SecurityTest.exe file. This reads the strong-name key. Click Next, and change the permission set to LocalIntranet, which limits access to the file system. Click Next, and then click Finish. This creates a node called Copy Of SecurityTest. Right-click this node, and click Properties. On the General tab, select the two options at the bottom that limit the security policy evaluation, as shown in Figure 13-11, to be sure that only these permissions are set for assemblies with this strong-name key. Click OK or Apply to accept and immediately apply the changes.


Figure 13-11: Select the bottom options to limit the policy evaluation, which ensures that only the permissions that are defined in this policy are assigned.

Testing the Assembly When you run the SecurityTest application and click the button, the following exception is thrown because the code is trying to surpass its permissions.

Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. 

The exception is thrown on the line of code that opens the stream. At this point, you know how to create a code group and assign tight security to an assembly. You also have a test project that has a limited permission set assigned.

Requested Permissions

In the previous section, we assigned permissions administratively to any assemblies having the same strong-name key as our test project. If this application were run with the default security permissions, it would have FullTrust because it would be running on the local computer. As a developer, you might not want your application to receive full trust permissions, so one thing you can do is configure your assembly to run with the least amount of permissions that are necessary and request additional permissions as required.

You can implement permission requests to make your code security-aware. You should include permission requests in applications that access protected resources. Permission requests do two things: they request the minimum permissions that your code must receive to run, and they ensure that your code receives only the permissions it actually needs.

Requests are applied as assembly-level attributes in the AssemblyInfo file, where your code tells the runtime about permissions that it needs in order to run or specific permissions that it does not want. The security requests are evaluated when your code is loaded into memory. Requests cannot obtain more permissions than the runtime would have given your code had the request not been made. In our SecurityTest example, requesting file permissions would fail because the User configuration did not give the assembly File permissions.

The assembly permission request requires a SecurityAction enumeration, which can be RequestMinimum, RequestOptional, or RequestRefuse. These actions are evaluated when the assembly is loaded and operate as follows.

Implementing the Request for Permissions The SecurityTest project is currently not working because we added a code group without file input/output permissions. Delete this code group in the .NET Framework 2.0 Configuration tool to allow the application to run with FullTrust permissions. In the following example, we will ask for the minimum permissions to run the project, by adding the following code to the AssemblyInfo file (in the Properties folder of Solution Explorer).

image from book

Visual Basic

Imports System.Security.Permissions <Assembly: UIPermission(SecurityAction.RequestMinimum, _    Unrestricted:=True)> <Assembly: FileIOPermission(SecurityAction.RequestMinimum, _    Unrestricted:=True)> <Assembly: PermissionSet(SecurityAction.RequestOptional, _    Unrestricted:=False)> 
image from book

image from book

C#

using System.Security.Permissions; [assembly: UIPermission(SecurityAction.RequestMinimum,    Unrestricted=true)] [assembly: FileIOPermission(SecurityAction.RequestMinimum,    Unrestricted=true)] [assembly: PermissionSet(SecurityAction.RequestOptional,    Unrestricted = false)] 
image from book

This attribute requests user interface (UI) and file input/output (FileIO) permissions from the .NET security system. If these permissions are not granted, the assembly does not load. The last line requests a set of permissions, and you can specify the name of a particular permission set. In this example, no permission set name is given, so the Nothing permission set is assigned, and all other permissions not explicitly requested are refused. The permissions explicitly requested were the UI and FileIO permissions.

The permissions requested by the assembly are the union of RequestMinimum (M) and RequestOptional (O) minus RequestRefuse (R). The permissions that are finally granted (F) to the assembly are based on the runtime security policy permissions (RS) intersected with the permissions that were requested, as shown here.

F = RS ? ((M ? O) - R)

Testing the Permission Requests To test the code we just added to the SecurityTest project, run the application and click the button. You should see that the file is opened and the message box displays its contents.

Next try commenting out the statement in the AssemblyInfo file that requests FileIO permissions, and run the SecurityTest project again. You will see the same exception as earlier.

This exception is thrown because the last statement, PermissionSet(SecurityAction.Request-Optional, Unrestricted = false), tells the runtime that no permissions should be given except permissions that are explicitly requested, and the FileIO permission is no longer being requested. If you comment out this last statement, the application will receive the FullTrust permission set and the project will run even with the FileIO permission request commented out.

Placing Demands on the Callers

Your code can demand that callers have specific permissions in order to execute your code. Any method that calls your method, directly or indirectly, is a caller, or more specifically, an upstream caller in the call stack, as shown in Figure 13-12. When you demand that the caller have specific permissions, the complete call stack is walked to verify that the permissions exist. Your code can place demands on the caller either declaratively or imperatively. Declarative demands are implemented by using attributes before the method or class that needs to access a specific resource, or by using an assembly-level attribute. Imperative permissions are implemented in code and can be more granular, but they are not as visible to tools that can identify the permissions that your assembly requires.

image from book
Figure 13-12: Looking at the call stack, upstream callers are direct and indirect callers of the current method, whereas downstream callers are calls that are directly and indirectly made from the current method.

Declarative Demands Declarative permission demands are assigned using attributes, which can be placed at the assembly, class, or class member. Declarative permissions assigned at the class level apply to all class members. If a class has a declarative demand and a class member has a declarative demand, the class member's declarative demand overrides the classlevel declarative demand.

Implementing Declarative Permission Demands In the SecurityTest project, we'll add another button that performs the same action as the first button but has a declarative demand for FileIO permissions. Add a button to the form, double-click the button, and add the following code, which has a declarative demand for the Button object's event handler.

image from book

Visual Basic

<FileIOPermission(SecurityAction.Demand, Unrestricted:=True)> _ Private Sub Button2_Click(ByVal sender As System.Object, _       ByVal e As System.EventArgs) Handles Button2.Click    Using stream As New StreamReader("CarList.xml")       MessageBox.Show(stream.ReadToEnd())    End Using End Sub 
image from book

image from book

C#

[FileIOPermission(SecurityAction.Demand,Unrestricted=true)] private void button2_Click(object sender, EventArgs e) {    using (StreamReader stream = new StreamReader("CarList.xml"))    {       MessageBox.Show(stream.ReadToEnd());    } } 
image from book

This code places a declarative demand on this method only. The difference between this button and the previous button is that the previous button's method is entered and the security exception is thrown on the line of code that opens the file, whereas this button throws the security exception before the method is entered.

Using PermCalc.exe to Find Required Permissions PermCalc is a tool that estimates the permissions that callers must be granted to call the public entry points of an assembly. PermCalc is available in the .NET Framework SDK and can be accessed using the .NET Command Prompt. You can get help on PermCalc by calling PermCalc with the -? option. To view the permissions required for any caller into the SecurityTest project, change to the directory containing your executable and run PermCalc as follows.

PermCalc SecurityTest.exe -Show -Stacks -Under 

The PermCalc tool produces the following XML output, which is normally directed to a file but can be displayed using the -Show option.

image from book

PermCalc Output

<?xml version="1.0"?> <Assembly>    <Namespace Name="SecurityTest">       <Type Name="Form1">          <Method Sig="instance void .ctor()">             <Demand>                <PermissionSet version="1"                   >                <IPermission version="1"                   BACKGROUND-COLOR: #a5cbff">                  mscorlib, Version=2.0.0.0, Culture=neutral,                   PublicKeyToken=b77a5c561934e089" Unrestricted="true" />                </PermissionSet>             </Demand>             <Sandbox>                <PermissionSet version="1"                   >                <IPermission version="1"                   BACKGROUND-COLOR: #a5cbff">                  mscorlib, Version=2.0.0.0, Culture=neutral,                   PublicKeyToken=b77a5c561934e089" Unrestricted="true" />                </PermissionSet>             </Sandbox>             <Stacks>                <CallStack>                   <IPermission version="1"                      BACKGROUND-COLOR: #a5cbff">                     mscorlib, Version=2.0.0.0, Culture=neutral,                      PublicKeyToken=b77a5c561934e089" Unrestricted="true" />                   <Method Type="System.IO.StreamReader"                      Sig="instance void .ctor(string)" Asm="mscorlib" />                   <Method Type="SecurityTest.Form1"                      Sig="instance void button1_Click(object ,                         class EventArgs)" Asm="SecurityTest" />                   <Method Type="SecurityTest.Form1"                      Sig="instance void InitializeComponent()"                         Asm="SecurityTest" />                   <Method Type="SecurityTest.Form1"                      Sig="instance void .ctor()"                      Asm="SecurityTest" />                </CallStack>             </Stacks>          </Method>       </Type>    </Namespace> </Assembly> 
image from book

This output shows that FileIO permissions are required to call into this assembly.

Imperative Demands You make imperative permission demands in your code by creating a new instance of the permission object you want to demand. You should generally avoid imperative demands in favor of declarative demands, but imperative demands give you the flexibility of being as granular as you need to be.

Implementing Imperative Permission Demands In the SecurityTest project, we'll add yet another button that performs the same action as the previous button but has an imperative demand for FileIO permissions. Add a button to the form, double-click the button, and add the following code.

image from book

Visual Basic

Private Sub button3_Click(ByVal sender As System.Object, _       ByVal e As System.EventArgs) Handles button3.Click    Dim filePerm As New FileIOPermission( _       PermissionState.Unrestricted)    filePerm.Demand()    Using stream As New StreamReader("CarList.xml")       MessageBox.Show(stream.ReadToEnd())    End Using End Sub 
image from book

image from book

C#

private void button3_Click(object sender, EventArgs e) {    FileIOPermission filePerm = new FileIOPermission(       PermissionState.Unrestricted);    filePerm.Demand();    using (StreamReader stream = new StreamReader("CarList.xml"))    {       MessageBox.Show(stream.ReadToEnd());    } } 
image from book

When you run this code, the security exception is thrown on the line that demands the permission. The advantage is that you can place this code in a try/catch block. If the specific exception is thrown, you can display a more user-friendly exception message and/or gracefully recover from the exception.

Being Assertive on Downstream Callers

Assert is a downstream call that you can make on a permission to enable your code and downstream caller's code to execute code that your code has permission to do but your callers might not have permission to do. Any call to a method, directly or indirectly, by your code is considered to be a downstream caller, as shown previously in Figure 13-12. By calling the Assert method on a permission, you are telling the security system to not check the downstream callers for the asserted permission. When your code reaches the Assert, there is a stack walk to verify that you have permission to make the Assert, but if a downstream caller places a demand for a permission that you asserted, no stack walk is made to verify permissions.

Why is the Assert necessary? Sometimes your code needs to call code that is in a component that you didn't write, and you don't have the source code to change. This code might not have requested the permissions that are necessary for the action that you are trying to perform. Your code has the appropriate permission but the component does not. The Assert opens the door for you. Think of the Assert as being a "Just do it!" command.

When you are done with the assertion, call the RevertAssert method, which undoes the assertion. Using the Assert method can create security holes, so think twice about implementing this approach.

Using the AllowPartiallyTrustedCallersAttribute Attribute

By default, strong-named assemblies can be called only by other assemblies that are granted full trust by the runtime security policy. You enforce this restriction by placing a LinkDemand for FullTrust on every public or protected method on every publicly accessible class in the assembly.

A LinkDemand is a permission demand to the assembly that attempts to make a call. LinkDemand causes a security check during just-in-time compilation and checks only the immediate caller of your code. Linking occurs when your code is bound to a type reference, including function pointer references and method calls. If the caller does not have sufficient permission to link to your code, the link is not allowed and a runtime exception is thrown when the code is loaded and run. Note that a stack walk is not performed with a LinkDemand.

Strong-named assemblies that are intended to be called by partially trusted code can declare their intent through the use of the AllowPartiallyTrustedCallersAttribute (APTCA). The attribute is declared at the assembly level. The following is an example of adding the declaration to a strong-named assembly.

image from book

Visual Basic

<assembly:AllowPartiallyTrustedCallers> 
image from book

image from book

C#

[assembly:AllowPartiallyTrustedCallers] 
image from book

If a strong-named assembly does not explicitly apply this attribute at the assembly level, only assemblies that are granted FullTrust by the runtime security policy can call the assembly. If this attribute is present, all other security checks function as intended, including any class- level or method-level declarative security attributes.

 


Programming Microsoft ADO. NET 2.0 Applications. Advanced Topics
Linux Application Development (2nd Edition)
ISBN: 735621411
EAN: 2147483647
Year: 2004
Pages: 85
Authors: Michael K. Johnson, Erik W. Troan
BUY ON AMAZON

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