Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

388 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

<?xml version="1.0" encoding="utf-8" ?> <configuration>

<system.runtime.remoting>

<application>

<!-- Define the remotable types. --> <service>

<wellknown

mode = "SingleCall"

type = "Apress.VisualCSharpRecipes.Chapter10.Recipe10_16,

Recipe10-16"

objectUri = "Recipe10-16.rem" /> </service>

<!-- Define the protocol used for network access. You can use only the http channel. --> <channels>

<channel ref="http"/> </channels>

<!-- Uncomment the following tags to use the binary formatter (instead of the default SOAP formatter). -->

<!-- <serverProviders>

<formatter ref="binary"/> </serverProviders>

-->

</application>

</system.runtime.remoting>

</configuration>

Usage

A client can use an object hosted in IIS in the same way as an object hosted in a custom component host. However, the virtual directory name will become part of the object URI. For example, if the Web.config file shown in the preceding code is hosted in the virtual directory http://localhost/ RemoteObjects, the full URL will be http://localhost/RemoteObjects/Recipe10-16.rem.

Note When hosting an object with IIS, the account used to execute the object is the ASP.NET account defined in the Machine.config file. If this account doesn’t have the rights to access the database (which is the default situation), you will receive an error when you try this example. Look at the .NET Framework for documentation on the

<processModel> tag.

10-19. Control the Lifetime of a Remote Object

Problem

You want to configure how long a singleton or client-activated object lives while not in use.

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

389

Solution

Configure a lease policy by using configuration file settings, override the MarshalByRefObject. InitializeLifetimeService method, or implement a custom lease provider.

How It Works

If a remotable object uses single-call activation, it will be automatically destroyed at the end of each method call. This behavior changes with client-activated and singleton objects, which are given a longer lifetime dictated by a lifetime lease. With the default settings, a remote object will be automatically destroyed if it’s inactive for 2 minutes, provided it has been in existence for at least 5 minutes.

The component host, remote object, and client each have the opportunity to change lifetime settings, as described here:

The component host can specify different lease lifetime defaults in the configuration file using the <lifetime> tag. The leaseTime attribute of the tag specifies the default lifetime for all hosted object. The renewOnCallTime attribute specifies the amount of time by which the lease is extended when a call is made against a hosted object. You can specify the values for both attributes as positive integers with a time unit suffix for days (D), hours (H), minutes (M), or seconds (S). For example, 10 hours is 10H, and 30 seconds is 30S.

The remote class can override its InitializeLifetimeService method (inherited from MarshalByRefObject) to modify its initial lease settings by configuring and returning an object that implements the System.Runtime.Remoting.Lifetime.ILease interface. You obtain an ILease instance by calling the base class method InitializeLifetimeService. Then configure the returned ILease by setting the InitialLeaseTime and RenewOnCallTime properties to the desired values using System.TimeSpan objects. If you want the object to have an unlimited lifetime, simply return a null reference instead of an ILease object. This is most commonly the case if you are creating a singleton object that needs to run independently (and permanently), even if clients aren’t currently using it.

The client can call the MarshalByRefObject.GetLifetimeService method on a specific remote object to retrieve an ILease instance. The client can then call the ILease.Renew method to specify a minimum amount of time the object should be kept alive.

The Code

The following example demonstrates how to use a component host’s configuration file to control lifetime leases. The configuration gives each hosted object an initial lifetime of 10 minutes, and each time a member of the object is invoked, the lifetime is set to be at least 3 minutes.

<configuration>

<system.runtime.remoting>

<application>

<service>

<wellknown

mode = "Singleton" type="Apress.VisualCSharpRecipes.Chapter10.Recipe10_19,Recipe10-19" objectUri = "Recipe10-19" />

</service>

<channels>

<channel ref="tcp" port="19080" /> </channels>

390 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

<lifetime leaseTime = "10M" renewOnCallTime = "3M" />

</application>

</system.runtime.remoting>

</configuration>

The following example demonstrates how to use the second approach outlined where the remotable object overrides the InitializeLifetimeService method and takes control of its own lifetime. The example shows a remotable object that gives itself a default 10-minute lifetime and 5-minute renewal time.

using System; using System.Data;

using System.Data.SqlClient;

using System.Runtime.Remoting.Lifetime;

namespace Apress.VisualCSharpRecipes.Chapter10

{

// Define a class that extends MarshalByRefObject, making it remotable. public class Recipe10_19 : MarshalByRefObject

{

public override object InitializeLifetimeService()

{

ILease lease = (ILease)base.InitializeLifetimeService();

// Lease can only be configured if it is in an initial state. if (lease.CurrentState == LeaseState.Initial)

{

lease.InitialLeaseTime = TimeSpan.FromMinutes(10); lease.RenewOnCallTime = TimeSpan.FromMinutes(5);

}

return lease;

}

...

}

}

10-20. Control Versioning for Remote Objects

Problem

You want to create a component host that can host more than one version of the same object.

Solution

Install all versions of the remotable object into the GAC, and explicitly register each version at a different URI endpoint. See recipe 1-14 for details on how to manage the assemblies in the GAC.

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

391

How It Works

.NET Remoting doesn’t include any intrinsic support for versioning. When a client creates a remote object, the component host automatically uses the version in the local directory or, in the case of a shared assembly, the latest version from the GAC. To support multiple versions, you have three choices:

Create separate component host applications: Each component host will host a different version of the remote object assembly and will register its version with a different URI. This approach forces you to run multiple component host applications at once and is most practical if you are using IIS hosting (as described in recipe 10-18).

Create an entirely new remote object assembly (instead of simply changing the version): You can then register the classes from both assemblies at different URIs by using the same component host.

Install all versions of the remote object assembly in the GAC: You can now create a component host that maps different URIs to specific versions of the remote object assembly.

The Code

The last option is the most flexible in cases where you need to support multiple versions. The following configuration file registers two versions of the RemoteObjects assembly at two different endpoints. Notice that you need to include the exact version number and public key token when using assemblies from the GAC. You can find this information by viewing the assembly in the Windows Explorer GAC plug-in (browse to C:\[WindowsDir]\Assembly). The client configuration file won’t change at all (aside from updating the URI, if required). The client “chooses” the version it wants to use by using the corresponding URI.

<configuration>

<system.runtime.remoting>

<application>

<service>

<!-- The type information is split over two lines to accommodate the bounds of the page. In the configuration file, this information must all be placed on a single line. -->

<wellknown mode="SingleCall"

type="RemoteObjects.RemoteObject, RemoteObjects, Version 1.0.0.1, Culture=neutral, PublicKeyToken=8b5ed84fd25209e1"

objectUri="RemoteObj" />

<wellknown mode="SingleCall"

type="RemoteObjects.RemoteObject, RemoteObjects, Version 2.0.0.1, Culture=neutral, PublicKeyToken=8b5ed84fd25209e1"

objectUri="RemoteObj_2.0" /> </service>

<channels>

<channel ref="tcp" port="19080" /> </channels>

</application>

</system.runtime.remoting>

</configuration>

C H A P T E R 1 1

■ ■ ■

Security and Cryptography

Aprincipal goal of the Microsoft .NET Framework is to make computing more secure, especially with respect to the use of mobile code and distributed systems. Most modern operating systems (including Microsoft Windows) support user-based security, allowing you to control the actions and resources to which a user has access. However, in the highly connected world resulting from the proliferation of computer networks, particularly the Internet, it’s insufficient to base security solely on the identity of a system’s user. In the interest of security, code should not automatically receive the same level of trust that you assign to the person running the code.

The .NET Framework incorporates two complementary security models that address many of the issues associated with user and code security: code access security (CAS) and role-based security (RBS). CAS and RBS do not replace or duplicate the security facilities provided by the underlying operating system. They are platform-independent mechanisms that provide additional security capabilities to augment and enhance the overall security of your managed solutions. CAS uses information about the source and origin of an assembly (evidence) gathered at runtime to determine which actions and resources code from the assembly can access (permissions). The .NET Framework security policy—a hierarchical set of configurable rules—defines the mapping between evidence and permissions. The building blocks of security policy are code groups, which allow you to configure the mapping between evidence and permissions. The set of permissions granted to an assembly as a result of the security policy is known as the assembly’s grant set.

The .NET Framework class library uses permission demands to protect its most important functionality from unauthorized access. A demand forces the common language runtime (CLR) to ensure that the whole stack of code calling a protected method has a specific permission. CAS ensures that the runtime capabilities of code depend on the level of trust you place in the creator and source of the code, not the level of trust you place in the user running the code.

Following a more traditional security model, RBS allows you to make runtime decisions based on the identity and roles of the user on whose behalf an application is running. On the Windows operating system, this equates to making decisions based on the Windows username and the Windows groups to which that user belongs. However, RBS provides a generic security mechanism that is independent of the underlying operating system, allowing you (with some development) to integrate with any user account system.

Another important aspect of the security features provided by the .NET Framework is cryptography. Cryptography is one of the most complex aspects of software development that any developer will use. The theory of modern cryptographic techniques is extremely difficult to understand and requires a level of mathematical knowledge that relatively few people have or need. Fortunately, the Microsoft .NET Framework class library provides easy-to-use implementations of the most commonly used cryptographic techniques and support for the most popular and well-understood algorithms.

393

394 C H A P T E R 1 1 S E C U R I T Y A N D C RY P TO G R A P H Y

This chapter provides a wide variety of recipes that cover some of the more commonly used security capabilities provided by the .NET Framework. As you read the recipes in this chapter and think about how to apply the techniques to your own code, keep in mind that individual security features are rarely effective when implemented in isolation. In particular, cryptography does not equal security; the use of cryptography is merely one small element of creating a secure solution.

The recipes in this chapter describe how to do the following:

Develop strong-named assemblies that can still be called by partially trusted code (recipe 11-1)

Configure the .NET Framework security policy to turn off CAS completely or turn off only execution permission checks (recipes 11-2 and 11-3)

Request specific code access permissions for your assemblies, determine at runtime what permissions the current assembly has, and inspect third-party assemblies to determine what permissions they need in order to run correctly (recipes 11-4, 11-5, 11-6, and 11-7)

Control inheritance and member overrides using CAS (recipe 11-8)

Inspect the evidence presented by an assembly to the runtime when the assembly is loaded (recipe 11-9)

Integrate with Windows security to determine if a user is a member of a specific Windows group, restrict which users can execute your code, and impersonate other Windows users (recipes 11-10, 11-11, and 11-12)

Generate random numbers that are nondeterministic and are suitable for use in securitysensitive applications (recipe 11-13)

Use hash codes and keyed hash codes to store user passwords and determine if files have changed (recipes 11-14, 11-15, 11-16, and 11-17)

Use encryption to protect sensitive data both in memory and when it is stored to disk (recipes 11-18 and 11-19)

Note For a broader explanation of secure programming and where cryptography fits in the overall security landscape, read Writing Secure Code, Second Edition, by Michael Howard and David LeBlanc (Microsoft Press, 2003), a modern classic of computer literature that contains a wealth of practical field-tested information. For more comprehensive coverage of the .NET security classes, see Programming .NET Security by Adam Freeman and Allen Jones (O’Reilly and Associates, 2003). Although not yet updated for .NET Framework 2.0, Programming

.NET Security provides easily understood descriptions of security fundamentals, covers all the .NET security classes in detail, and demonstrates how to extend most aspects of the security framework.

11-1. Allow Partially Trusted Code to Use Your Strong-Named Assembly

Problem

You need to write a shared assembly that is accessible to partially trusted code. (By default, the runtime does not allow partially trusted code to access the types and members contained in a strong-named assembly.)

C H A P T E R 1 1 S E C U R I T Y A N D C RY P TO G R A P H Y

395

Solution

Apply the assembly-level attribute System.Security.AllowPartiallyTrustedCallersAttribute to your shared assembly.

How It Works

To minimize the security risks posed by malicious code, the runtime does not allow assemblies granted only partial trust to access strong-named assemblies. This restriction dramatically reduces the opportunity for malicious code to attack your system, but the reasoning behind such a heavyhanded approach requires some explanation.

Assemblies that contain important functionality that is shared between multiple applications are usually strong-named and often installed in the global assembly cache (GAC). This is particularly true of the assemblies that constitute the .NET Framework class library. Other strong-named assemblies from well-known and widely distributed products will also be in the GAC and accessible to managed applications. The high chance that certain assemblies will be present in the GAC, their easy accessibility, and their importance to many different applications makes strong-named assemblies the most likely target for any type of subversive activity by malicious managed code.

Generally, the code most likely to be malicious is that which is loaded from remote locations, such as the Internet, over which you have little or no control. Under the default security policy in version 1.x and 2.0 of the .NET Framework, all code run from the local machine has full trust, whereas code loaded from remote locations has only partial trust. Stopping partially trusted code from accessing strong-named assemblies means that partially trusted code has no opportunity to use the features of the assembly for malicious purposes, and cannot probe and explore the assembly to find exploitable holes. Of course, this theory hinges on the assumption that you correctly administer your security policy. If you simply assign all code full trust, not only will any assembly be able to access your strong-named assembly, but the code will also be able to access all of the functionality of the .NET Framework and even Win32 or any COM object through P/Invoke and COM Interop. That would be a security disaster!

Note If you design, implement, and test your shared assembly correctly using CAS to restrict access to important members, you do not need to impose a blanket restriction to prevent partially trusted code from using your assembly. However, for an assembly of any significance, it’s impossible to prove there are no security holes that malicious code can exploit. Therefore, you should carefully consider the need to allow partially trusted code to access your strong-named assembly before applying AllowPartiallyTrustedCallersAttribute. However, you might have no choice. If you are exposing public classes that provide events, you must apply this attribute. If you do not, an assembly that is not strong-named will be allowed to register a handler for one of your events, but when it is called, a security exception will be thrown. Code in an assembly that is not strong-named is not allowed to call code in a strong-named assembly.

The runtime stops partially trusted code from accessing strong-named assemblies by placing an implicit LinkDemand for the FullTrust permission set on every public and protected member of every publicly accessible type defined in the assembly. This means that only assemblies granted the permissions equivalent to the FullTrust permission set are able to access the types and members from the strong-named assembly. Applying AllowPartiallyTrustedCallersAttribute to your strongnamed assembly signals the runtime not to enforce the LinkDemand on the contained types and members.

396 C H A P T E R 1 1 S E C U R I T Y A N D C RY P TO G R A P H Y

Note The runtime is responsible for enforcing the implicit LinkDemand security actions required to protect strong-named assemblies. The C# assembler does not generate declarative LinkDemand statements at compile time.

The Code

The following code fragment shows the application of the attribute

AllowPartiallyTrustedCallersAttribute. Notice that you must prefix the attribute with assembly: to signal to the compiler that the target of the attribute is the assembly (also called a global attribute). In addition, you do not need to include the Attribute part of the attribute name, although you can if you want to add it. Because you target the assembly, the attribute must be positioned after any top-level using statements, but before any namespace or type declarations.

using System.Security;

[assembly:AllowPartiallyTrustedCallers]

namespace Apress.VisualCSharpRecipes.Chapter11

{

public class Recipe11-01 {

// Implementation code. . .

}

}

Tip It’s common practice to contain all global attributes in a file separate from the rest of your application code. Microsoft Visual Studio uses this approach, creating a file named AssemblyInfo.cs to contain all global attributes.

Notes

If, after applying AllowPartiallyTrustedCallersAttribute to your assembly, you want to restrict partially trusted code from calling only specific members, you should implement a LinkDemand for the FullTrust permission set on the necessary members, as shown in the following code fragment.

[System.Security.Permissions.PermissionSetAttribute (System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")]

public void SomeMethod() { // Method code. . .

}

11-2. Disable Code Access Security

Problem

You need to turn off all code access security (CAS) checks.

Solution

Use the Code Access Security Policy tool (Caspol.exe) and execute the command caspol –s off from the command line to temporarily disable code access security checks.

C H A P T E R 1 1 S E C U R I T Y A N D C RY P TO G R A P H Y

397

Note You could permanently turn off CAS in .NET Framework versions 1.0 and 1.1 both programmatically and using Caspol.exe. In .NET Framework 2.0, you can turn off CAS only temporarily and only by using Caspol.exe.

How It Works

In some cases, code-level security might not be of interest to you. For example, when you are debugging code, you might want to exclude the possible interference caused by CAS. On rare occasions, the need for performance might outweigh the need for security. CAS is a key element of the .NET runtime’s security model and one that sets it apart from many other computing platforms. Although CAS was implemented with performance in mind and has been used prudently throughout the .NET class library, some overhead is associated with each security demand and resulting stack walk that the runtime must execute to check every caller in the chain of execution.

Note You should disable CAS only for performance reasons after you have exhausted all other possible measures to achieve the performance characteristics your application requires. Profiling your code will usually identify areas where you can improve performance significantly without the need to disable CAS. In addition, you should ensure that your system resources have appropriate protection using operating system security mechanisms, such as Windows access control lists (ACLs), before disabling CAS.

In these situations, you can temporarily disable CAS and remove the overhead and possible interference caused by code-level security checks. Turning off CAS has the effect of giving all code the ability to perform any action supported by the .NET Framework (equivalent to the FullTrust permission set). This includes the ability to load other code, call native libraries, and use pointers to access memory directly.

Caspol.exe is a utility provided with the .NET Framework that allows you to configure all aspects of your code access security policy from the command line. When you enter the command caspol –s off from the command line, you will see the following message indicating that CAS has been temporarily disabled:

Microsoft (r) .NET Framework CasPol 2.0.50727.42

Copyright (c) Microsoft Corporation. Al rights reserved.

CAS enforcement is being turned off temporarily. Press <enter> when you want to restore the setting back on.

As the message states, CAS enforcement is off until you press Enter, or until the console in which Caspol.exe is running terminates.

Notes

In versions 1.0 and 1.1 of the .NET Framework, running the command caspol –s off turned off CAS enforcement permanently until you turned it on again using the command caspol –s on. In addition, it was possible to turn CAS on and off programmatically using the System.Security.SecurityManager class. The SecurityManager class contains a set of static methods that provide access to critical security functionality and data. This includes the SecurityEnabled property, which turns CAS checks on and off.

To disable CAS, your code must run as a Windows Administrator and must have the ControlPolicy element of the permission System.Security.Permissions.SecurityPermission. Naturally, you do not need any specific permissions to enable CAS.

Соседние файлы в предмете Программирование на C++