Configuring Security

   


Implement security for a Windows service, a serviced component, a .NET Remoting object, and an XML Web service.

The .NET Framework offers a wide variety of security features. You can choose to run your machine in wide- open mode, with every user allowed to execute any .NET code, or you can lock down things selectively. You can control which programs have access to which resources, or which users have the right to execute which programs.

Broadly speaking, .NET security breaks down into two separate areas:

  • Code access security manages the security of .NET source code itself. You can tell the .NET Framework the resources your code needs to execute properly, and the .NET Framework will check for permission to access those resources on the machine at runtime. Code access security is very flexible, including the capability to define your own sets of necessary permissions. Code access security can also be used by administrators to ensure that undesired code never gets the chance to run on a system.

  • Role-based security manages the user rather than the code. Using role-based security allows you to provide (or deny) access to resources based on an identity provided by the user running the code. In practical terms, this means that you can limit program execution to particular users or groups on the computer.

I'll cover both these types of security in this chapter, beginning with code-based security. The .NET Framework also includes other security features, notably public-key and private-key encryption, which are not a part of the 70-310 exam.

Understanding Code Access Security

Code access security controls what code can do on your computer. Code access security is centered around permissions to use resources. The .NET Framework has an entire object-oriented system for managing code access security and the associated permissions. In the following sections, you'll learn about concepts involved in code access security:

  • Permissions

  • Code groups

  • Permission sets

You'll also learn how to manage code access security. In particular, code can request permissions on a very fine-grained scale, and the administrator can choose to allow permissions on an equally fine-grained scale.

Understanding Permissions

Code access security is based on specific permissions that the Common Language Runtime can grant to or deny from code. For example, the authorization to read or write information in the Windows Registry requires the Registry permission on the part of your code. As you'll see later in the chapter, code can make four different types of permission requests :

  • It can request the minimum permissions that it requires to run.

  • It can request optional permissions that it would like but does not require.

  • It can refuse permissions to ensure that it does not have access to particular resources.

  • It can demand permissions on the part of calling code.

Based on a variety of factors (including the origin of the code and information in the machine and application configuration files), the Common Language Runtime decides whether a particular permission will be granted. If a piece of code is unable to obtain the minimum permissions that it requires, that piece of code won't execute. The security settings of the computer determine the maximum permissions that code can be granted, but code is allowed to request (and receive) fewer permissions than the maximum.

The .NET Framework groups permissions into three types. Code access permissions represent access to a protected resource or the authorization to perform a protected operation. Identity permissions represent access based on credentials that are a part of the code itself. Role-based permissions represent access based on the user who is running the code. Each permission in the .NET Framework is represented by a particular class that derives from System.Security.CodeAccessPermission. Table 11.1 lists the available code access permissions, which are the most important for controlling the actions that code can take on a particular computer.

Table 11.1. CodeAccessPermissions in the .NET Framework

Permission

Explanation

DirectoryServicesPermission

Controls access to the System.DirectoryServices namespace.

DnsPermission

Controls access to domain name system (DNS) services.

EnvironmentPermission

Controls access to environment variables .

EventLogPermission

Controls access to the Windows event log.

FileDialogPermission

Controls access to files selected from the Open dialog box.

FileIoPermission

Controls access to reading and writing files and directories.

IsolatedStorageFilePermission

Controls access to private virtual file systems.

IsolatedStoragePermission

Controls access to isolated storage.

MessageQueuePermission

Controls access to message queuing via MSMQ.

OleDbPermission

Controls access to data via the System.Data.OleDb namespace.

PerformanceCounterPermission

Controls access to performance counters.

PrintingPermission

Controls access to printers.

ReflectionPermission

Controls access to the reflection features of .NET.

RegistryPermission

Controls access to the Windows Registry.

SecurityPermission

Controls access to unmanaged code.

ServiceControllerPermission

Controls access to starting and stopping services.

SocketPermission

Controls access to Windows sockets.

SqlClientPermission

Controls access to data via the System.Data.SqlClient namespace.

UiPermission

Controls access to the user interface.

WebPermission

Controls access to making Web connections.

NOTE

Custom Permissions If none of these permissions are quite right for your application, you can also define custom permissions. I'll talk about custom permissions later in the chapter.


Requesting Minimum Permissions

To start working in the .NET security framework, your code can request the minimum permissions that it needs to function correctly. Step by Step 11.1 demonstrates the syntax for making such a request.

STEP BY STEP

11.1 Requesting Minimum Permissions

  1. Create a new Visual Basic .NET Windows Application named 310C11.

  2. Add a new form named StepByStep11-1 to the application.

  3. Place a Label control, a TextBox control named txtFileName , and a Button control named btnGetFile on the form. Add an OpenFileDialog component named dlgOpen . Figure 11.1 shows a design for this form.

    Figure 11.1. Form for testing security.

  4. Double-click the Button control to open the form's module. Add these statements to the top of the module:

     Imports System.Security.Permissions <Assembly: FileDialogPermissionAttribute(_  SecurityAction.RequestMinimum, _  Unrestricted:=True)> 
  5. Add this code to allow the user to browse for a filename when she clicks the button:

     Private Sub btnGetFile_Click(_  ByVal sender As System.Object, _  ByVal e As System.EventArgs) _  Handles btnGetFile.Click     Try         If dlgOpen.ShowDialog = DialogResult.OK Then             txtFileName.Text = dlgOpen.FileName         End If     Catch ex As Exception         MessageBox.Show("Exception: " & ex.Message)     End Try End Sub 
  6. Set the form as the startup object for the project.

  7. Compile the project. Launch the executable file from Windows Explorer (you'll find it in the project's bin directory) and verify that you can browse for a file.

This example requests permissions by applying an attribute to the assembly. The FileDialogPermissionAttribute allows the assembly to request the FileDialogPermission, which in turn allows access to the system's file dialogs. In this particular case, the code runs without any problem, which means that it was granted the requested permission. That's because, by default, you have full permissions to run any code that originates on your own computer. To see code access security in action, you must learn to manage the permissions granted to code on your computer. As a first step, you must understand the concepts of code groups and permission sets.

Code Groups and Permission Sets

A code group is a set of assemblies that share a security context. You define a code group by specifying the membership condition for the group. Every assembly in a code group receives the same permissions from that group ; however, because assemblies can be members of multiple code groups, two assemblies in the same group might end up with different permissions in the end.

The .NET Framework supports seven different membership conditions for code groups:

  • The application directory membership condition Selects all code in the installation directory of the running application.

  • The cryptographic hash membership condition Selects all code matching a specified cryptographic hash. Practically speaking, this is a way to define a code group that consists of a single assembly.

  • The software publisher membership condition Selects all code from a specified publisher, as verified by Authenticode signing.

  • The site membership condition Selects all code from a particular Internet domain.

  • The strong name membership condition Selects all code with a specified strong name.

  • The URL membership condition Selects all code from a specified URL.

  • The zone membership condition Selects all code from a specified security zone (Internet, Local Intranet, Trusted Sites, My Computer, or Untrusted Sites).

NOTE

Cryptographic Hashing To calculate a cryptographic hash, the compiled code of an assembly is run through a cryptographic algorithm that generates a string of digits as a result. The string of digits is much shorter than the original assembly, and therefore easier to evaluate. If you run the same assembly through the algorithm, you'll get the same hash out, but two different assemblies are extremely unlikely to generate an identical hash value.


Permissions are granted in permission sets. A permission set is a set of one or more code access permissions that are granted as a unit. If you only want to grant a single permission, you must construct a permission set containing only that single permission: You can't grant permissions directly. The .NET Framework supplies seven built-in permission sets:

  • The Nothing permission set Grants no permissions.

  • The Execution permission set Grants permission to run but not to access protected resources.

  • The Internet permission set Grants limited permissions designed for code of unknown origin.

  • The LocalIntranet permission set Grants high permissions designed for code within an enterprise.

  • The Everything permission set Grants all permissions except for the permission to skip verification.

  • The SkipVerification permission set Grants the permission to skip security checks.

  • The FullTrust permission set Grants full access to all resources. This permission set includes all permissions.

You can also create your own custom permission sets, as you'll see in the next section.

Granting Permission

The easiest way to grant or deny permissions in the .NET Framework is to use the Microsoft .NET Framework Configuration tool, as in Step By Step 11.2.

STEP BY STEP

11.2 Granting Permissions

  1. Select Start, Programs, Administrative Tools, Microsoft .NET Framework Configuration.

  2. Expand the Runtime Security Policy node, the User node, and then the Permission Sets node to see the built-in .NET permission sets.

  3. Right-click the Everything permission set and select Duplicate to create a new permission set named Copy of Everything.

  4. Right-click the Copy of Everything permission set and select Rename. Rename the permission set No FileDialog .

  5. With the No FileDialog permission set selected, click the Change Permissions link in the right panel of the Configuration tool. In the Create Permission Set dialog box, select File Dialog and click Remove. Click Finish to save your changes.

  6. Expand the Code Groups node and click on the default All_Code code group. Click the Add a Child Code Group link in the right panel of the Configuration tool.

  7. In the Create Code Group dialog box, name the new group Chapter11 . Enter a description and click Next.

  8. Select the Hash condition. Click the Import button and browse to 310C11.exe. Click Open to calculate the hash for this file. Click Next.

  9. Select the No FileDialog permission set and click Next. Click Finish to create the new code group.

  10. Right-click the Chapter11 code group and select Properties. Check the box to make this code group exclusive, as shown in Figure 11.2. Click OK.

    Figure 11.2. Setting properties for a code group.

  11. Run the 310C11.exe application by double-clicking it in Windows Explorer. You'll receive a policy exception error box indicating that the code cannot be run. Click No to dismiss the error.

In Step By Step 11.2, you first created a permission set (based on the built-in Everything permission set) that included every permission except for the permission to use the file dialog boxes. You then created a code group that contained the executable file for this chapter's examples and assigned the No FileDialog permission set to this code group. The result is that the code cannot run because at a minimum it requires the one permission that the new security policy will not grant to it.

Imperative Security

Requesting permissions through the use of attributes is known as declarative security . A second method to request permissions is known as imperative security. With imperative security , you create objects to represent the permissions that your code requires (see Step By Step 11.3). Guided Practice Exercise 11.1 will give you additional practice with imperative security.

EXAM TIP

Command-Line Permissions The .NET Framework SDK also includes a tool, caspol .exe, that can manipulate code groups and permission sets from the command line. If you're trying to automate security operations, you can make good use of this tool from a batch file.


STEP BY STEP

11.3 Imperative Security

  1. Add a new form to your Visual Basic .NET application.

  2. Place a Label control, a TextBox control named txtFileName , and a Button control named btnGetName on the form. Add an OpenFileDialog component named dlgOpen . You can reuse the design from Figure 11.1 for this form.

  3. Double-click the Button control to open the form's module. Add this statement to the top of the module:

     Imports System.Security.Permissions 
  4. Add this code to allow the user to browse for a file name when she clicks the button:

     Private Sub btnGetName_Click(_  ByVal sender As System.Object, _  ByVal e As System.EventArgs) _  Handles btnGetName.Click     Try         Dim fdp As FileDialogPermission = _          New FileDialogPermission(_          PermissionState.Unrestricted)         ' Check to see whether the code         ' has the specified permission         fdp.Demand()         If dlgOpen.ShowDialog = _          DialogResult.OK Then             txtFileName.Text = dlgOpen.FileName         End If     Catch ex As Exception         MessageBox.Show("Exception: " & _          ex.Message)     End Try End Sub 
  5. Comment out the FileDialogPermissionAttribute attribute in the code for Step By Step 11.1, which would otherwise apply to the entire assembly.

  6. Set the form as the startup object for the project.

  7. Compile the project. Launch the executable file from Windows Explorer and verify that you can browse for a file.

  8. Run the Microsoft .NET Framework Configuration tool and locate the node for the Chapter11 code group. Right-click the code group and select Properties. On the Membership Condition tab, select Import. Browse to the 306C11.exe file and click Open. Click OK to dismiss the dialog box. This step is necessary because compiling the project with changed code results in a different hash value than the one that you calculated in Step By Step 11.1.

  9. Launch the 306C11.exe file from Windows Explorer again. Click the Get File button. You'll see the security exception shown in Figure 11.3.

    Figure 11.3. Exception from imperative security.

Step By Step 11.3 constructed a FileDialogPermission object representing unrestricted access to the file dialogs. It then called the demand method of that object to demand the permission from the operating system. When the security policy was such that the permission could not be granted, the code threw an exception.

EXAM TIP

Imperative Versus Declarative Security The only time that you absolutely must use imperative security is when you must make security decisions based on factors only known at runtime, such as the name of a particular file. In most other cases, you'll find that declarative security is easier to use.


Computing Permissions

Determining the actual permissions applied to any given piece of code is a complex process. To begin the process, think about permissions at the Enterprise level only. The Common Language Runtime (CLR) starts by examining the evidence a particular piece of code presents to determine its membership in code groups at that level. Evidence is just an overall term for the various identity permissions (publisher, strong name, hash, and so on) that can go into code group membership.

Code groups are organized into a hierarchy; in Step By Step 11.1, you created the Chapter11 code group as a child of the All Code code group. In general, CLR will examine all the code groups in the hierarchy to determine membership. However, any code group in the hierarchy can be marked as Exclusive (the effect of the check box you checked when creating the Chapter11 code group). CLR stops checking for group membership if code is found to be a member of an Exclusive code group. Either way, code will be determined to be a member of zero or more code groups as a first step.

Next, CLR retrieves the permission set for each code group that contains the code. If the code is a member of an Exclusive code group, only the permission set of that code group is taken into account. If the code is a member of more than one code group and none of them are an Exclusive code group, all the permission sets of those code groups are taken into account. The permission set for the code is the union of the permission sets of all relevant code groups. That is, if code is a member of two code groups, and one code group grants FileDialog permission, but the other does not, the code will have FileDialog permission from this step. This is a "least- restrictive " combination of permissions.

That accounts for the permissions at one level (the Enterprise level). But there are actually four levels of permissions: Enterprise, Machine, User, and Application Domain. Only the first three levels can be managed within the .NET Framework Configuration tool, but if you need specific security checking within an application domain ( roughly speaking, an application domain is a session in which code runs), you can do this in code. An application domain can reduce the permissions granted to code within that application domain, but it cannot expand them.

CLR determines which of the four levels are relevant by starting at the top (the Enterprise level) and working down. Any given code group can have the LevelFinal property; in which case the examination stops there. For example, if code is a member of a code group on the Machine level, and that group has the LevelFinal property, only the Enterprise and Machine levels are considered in assigning security. CLR computes the permissions for each level separately and then assigns the code the intersection of the permissions of all relevant levels. That is, if code is granted FileDialog permission on the Enterprise and Machine levels but is not granted FileDialog permission on the User level, the code will not have FileDialog permission. Across levels, this is a "most-restrictive" combination of permissions.

At this point, CLR knows what permissions should be granted to the code in question, considered in isolation, but code does not run in isolation; it runs as part of an application. The final step of evaluating code access permissions is to perform a stack walk. In a stack walk , CLR examines all code in the calling chain from the original application to the code being evaluated. The final permission set for the code is the intersection of the permission sets of all code in the calling chain. That is, if code is granted FileDialog permission but the code that called it was not, the code will not be granted FileDialog permission.

EXAM TIP

Determining Permissions The Microsoft .NET Framework Configuration tool can help you determine the effective permissions for a piece of code. Right-click the Runtime Security Policy node and select Evaluate Assembly to do so. You can see the effective permissions for an assembly here or get a list of all the code groups that contribute to the assembly's permissions.


Requesting Other Types of Permissions

Sometimes, you might want to request a particular permission even though your application doesn't absolutely require that permission to proceed. That's the purpose of optional permissions. If you refer back to the code in Step By Step 11.1, you'll see that part of the permission attribute is the SecurityAction.RequestMinimum flag. To request optional permissions, use the SecurityAction.RequestOptional flag.

To use optional declarative permissions in Visual Basic .NET, your code should have a Sub Main starting point with a Try...Catch block. If optional permissions for the assembly can't be granted, this block will catch the exception. If minimum permissions can't be granted, the program will be shut down whether this block is present or not.

WARNING

Optional Versus Minimum When you request minimum permissions, CLR will give your assembly any other permissions it can in addition to the minimum permissions. When you request optional permissions, CLR will only give the assembly those permissions, and no others. So if you make an optional permissions request, you must be sure to request all the permissions that your code requires, including user interface permissions if you display any user interface.


You can also tell CLR about permissions that you do not want your code to have. This can be useful if the code is potentially available to untrusted callers (for example, users who invoke the code over the Internet), and you want to limit the potential harm they can do. The flag for this is SecurityAction.RequestRefuse.

Finally, you might want to ensure that all the code that calls your code has a particular permission. For example, you might want to raise an exception if any code in the calling stack doesn't have Registry permissions. You can do this by specifying SecurityAction.Demand in the declaration of the security attribute.

GUIDED PRACTICE EXERCISE 11.1

One reason you might choose to use imperative security rather than declarative security is to be able to easily catch security violations and respond to them automatically. In this exercise, use imperative security to selectively disable part of a user interface that the user isn't able to activate under the current security policy. You can start with the simple "browse for file" example from Step By Step 11.1 or invent your own problem.

Try this on your own first. If you get stuck or want to see one possible solution, follow these steps:

  1. Create a new Visual Basic .NET Windows Application. Rename the default form GuidedPracticeExercise11-1 .

  2. Place two Label controls, a TextBox control named txtFileName , and a Button control named btnGetFile on the form. Name one of the Label controls lblMessage and remove its Text. Add an OpenFileDialog component named dlgOpen .

  3. Double-click the Button control to open the form's module. Add this statement to the top of the module:

     Imports System.Security.Permissions 
  4. Add this code to check security at load time and to allow the user to browse for a filename when she clicks the button:

     Private Sub Form1_Load(ByVal sender As System.Object, _  ByVal e As System.EventArgs) Handles MyBase.Load     Try         Dim fdp As FileDialogPermission = _          New FileDialogPermission(_          PermissionState.Unrestricted)         ' Check to see whether the code         ' has the specified permission         fdp.Demand()     Catch ex As Exception         btnGetFile.Enabled = False         lblMessage.Text = _ "You do not have permission to browse for file names"     End Try End Sub Private Sub btnGetFile_Click(_  ByVal sender As System.Object, _  ByVal e As System.EventArgs) Handles btnGetFile.Click     Try         If dlgOpen.ShowDialog = DialogResult.OK Then             txtFilename.Text = dlgOpen.FileName         End If     Catch ex As Exception         MessageBox.Show("Exception: " & ex.Message)     End Try End Sub 
  5. Set the form as the startup object for the project.

  6. Compile the project. Run the compiled project from Windows Explorer. You should find it possible to browse for a file by clicking the button.

  7. Select Start, Programs, Administrative Tools, Microsoft .NET Framework Configuration. Navigate to the User node beneath the Runtime Security Policy node.

  8. Expand the Code Groups node under the User node and click the default All_Code code group. Click the Add a Child Code Group link in the right panel of the Configuration tool.

  9. In the Create Code Group dialog box, name the new group Chapter11GPE . Enter a description and click Next.

  10. Select the Hash condition. Click the Import button and browse to the executable file from this exercise. Click Open to calculate the hash for this file. Click Next.

  11. Select the No FileDialog permission set and click Next. Click Finish to create the new code group.

  12. Right-click the Chapter11GPE code group and select Properties. Check the box to make this code group exclusive. Click OK.

  13. Run the program from Windows Explorer again. The Button control should be disabled, and the message should appear, as shown in Figure 11.4.

    Figure 11.4. User interface modified by security settings.

In Guided Practice Exercise 11.1, the imperative security object is used to check the permissions that the application has as soon as the form is loaded. If the call to the Demand method fails, you know the user won't be able to invoke the file dialog. In that case, the code disables the button that would otherwise launch the file dialog and puts up a warning message instead.

Using Custom Security Attributes

Configure security for a Windows-based application: Use custom attributes to configure security.

In some cases, the built-in security permissions might not fit your needs. For example, you might have designed a custom class that retrieves confidential information from your company's database, and you want to restrict permission on it by a more specific means than limiting SQL permissions.

In such cases, you can create your own custom permissions and add them to the .NET security framework. This requires quite a bit of code, and most developers will never need to perform this task. But just in case you do, I'll outline the process. You'll find more in-depth information, including all the code for a simple custom permission, in the Securing Applications section of the .NET Framework Combined Help Collection.

To implement a custom permission, you must create a class that inherits from the CodeAccessPermission class. Your new class must override five key methods to provide its own interfaces to the security system:

  • Copy Creates an exact copy of the current instance

  • Intersect Returns the intersection of permissions between the current instance and a passed-in instance of the class

  • IsSubsetOf Returns True if a passed instance includes everything allowed by the current instance.

  • FromXML Decodes an XML representation of the permission

  • ToXml Encodes the current instance as XML

Your class must support a constructor that accepts an instance of the PermissionState enumeration (which has a value of Unrestricted or None). You might also want to implement custom constructors related to your particular business needs. For example, a database- related permission might require a constructor that accepts a server name if permissions should be handled differently on test and development servers.

Although it's not strictly required, your code should also implement a method named IsUnrestricted, which returns True if this particular instance represents unrestricted access to the resource. This will make your custom permission more compatible with the built-in permissions in the .NET Framework.

To support declarative security, you should also implement an attribute class for your permission. This attribute class should derive from CodeAccessSecurityAttribute (which in turn derives from SecurityAttribute). The class should override the CreatePermission member of the IPermission interface. Within this function, you should create an instance of your base custom permission class and set its properties according to the parameters from the declarative security invocation. Any attribute class must be marked with the Serializable attribute so that it can be serialized into metadata along with the class to which it is applied.

For your custom permission to actually protect the intended resource, you must make changes to both the resource and to the .NET Framework on the computers on which the permission will be used. The changes to the resource are simple: Whenever an operation protected by the custom permission is about to be performed, the code should demand an instance of the permission. If the calling code can't deliver the permission, your class should refuse to perform the operation.

The changes to the .NET Framework are somewhat more complex. First, you must create an XML representation of your custom permission in the format expected by the Code Access Security Policy tool, caspol.exe. You can create this XML representation by instantiating your permission and calling its ToXml method. Given this XML representation, caspol.exe can add a permission set to the .NET Framework that contains your custom permission. It will also add the assembly that implements the custom permission to the list of trusted assemblies on the computer. You must perform this step on every computer on which your custom permission will be used.

REVIEW BREAK

  • Permissions control access to resources.

  • Code can request the minimum permissions that it needs to run and optional permissions that it would like to have. It can also refuse permissions and demand permissions on the part of calling code.

  • Code access permissions represent access to resources, whereas identity permissions represent things that the .NET Framework knows about code.

  • The .NET Framework supplies both attribute-based declarative security and class-based imperative security.

  • Code groups are sets of assemblies that share a security context.

  • Permission sets are sets of permissions that can be granted as a unit.

  • The Common Language Runtime computes actual permissions at runtime based on code group membership and the calling chain of the code.

  • Custom permissions allow you to create your own permissions to protect particular resources.


   
Top


MCAD. MCSD Training Guide (Exam 70-310. Developing XML Web Services and Server Components with Visual Basic. NET and the. NET Framework)
MCAD/MCSD Training Guide (70-310): Developing XML Web Services and Server Components with Visual Basic(R) .NET and the .NET Framework
ISBN: 0789728206
EAN: 2147483647
Year: 2002
Pages: 166

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