Windows Authentication

The first thing you need to know about Windows authentication is that it's practical only if you're using IIS to host your remote components. If you're adding authentication to an XML Web service, this level of support is automatic. If you're using .NET Remoting, you must use IIS as the component host (as described in Chapter 11). If you need the freedom to switch your remote component technology in the future, to use a custom component host, or to use a binary-encoded channel, you need the flexibility of custom authentication instead of Windows authentication.

You should also realize that even when you use Windows authentication, your code won't run under the identity of the authenticated user. Instead, your code runs under a local ASP.NET account that has carefully restricted privileges. It is possible to change this behavior using impersonation (discussed later in this chapter), but it's generally not recommended. A better approach is to use the HttpContext.User property to retrieve information about the authenticated user, such as the username and the groups to which the user belongs.

To illustrate how Windows authentication works, it helps to create a simple XML Web service, as shown in Listing 13-1.

Listing 13-1 A simple XML Web service that tests authentication
 Imports System.Web.Services Imports System.Security.Principal Public Class AuthenticationTest     Inherits System.Web.Services.WebService          ' Retrieves the identity used to execute this code.     <WebMethod()> _     Public Function GetCurrentUser() As String         Return WindowsIdentity.GetCurrent.Name     End Function     ' Retrieves the authenticated IIS user.     <WebMethod()> _     Public Function GetIISUser() As String         ' You could also retrieve the same information from         ' System.Threading.Thread.CurrentPrincipal.Identity.Name         Return User.Identity.Name     End Function End Class 

Note

Listing 13-1 uses the User property that is inherited from the base WebService class. User is an instance of an identity object that implements System.Security.Principal.Identity. You can access the same object through the Thread.CurrentPrincipal.Identity property. You use this technique with a component exposed through .NET Remoting because it doesn't derive from WebService.


You can test both of these Web methods using the Internet Explorer test page. If you don't enable IIS directory security, no user will be authenticated. The GetIISUser method returns an empty string. The GetCurrentUser method, however, returns the account used for the ASP.NET worker process. This account is defined in the <processModel> section of the machine.config file on the Web server. The name takes the form [MachineName]\[UserName] or [DomainName]\[UserName]. Typically, GetCurrentUser returns a string such as WEBSERVER\ASPNET. This test demonstrates the default case, in which no client authentication is performed.

IIS Authentication Settings

To enable directory-based authentication, run IIS Manager. (Choose Settings | Control Panel | Administrative Tools | Internet Services Manager from the Start menu.) Find the appropriate virtual directory, right-click on it, and choose Properties. Click on the Directory Security tab (shown in Figure 13-1).

Figure 13-1. Directory security settings

graphics/f13dp01.jpg

You can secure a directory using IIS authentication, IP address restrictions (to restrict or allow clients based on the IP address of their computer), or certificates (which are extremely secure but must be created and distributed to every client). Click the Edit button in the Anonymous Access And Authentication Control section to display options for IIS authentication (as shown in Figure 13-2).

Figure 13-2. IIS directory authentication options

graphics/f13dp02.jpg

By default, anonymous access is enabled. When anonymous access is enabled, IIS never authenticates a client, regardless of what other authentication settings you specify. To change this behavior, deselect Anonymous Access and select one or more of the other authentication methods (listed in Table 13-1). Note that although these methods differ in strength, they all have the same goal: to match a username and password supplied by the user (or client application) with a user account in a valid Windows user account on a domain controller, in Active Directory, or on the local machine. If you enable multiple authentication methods, IIS automatically attempts to use the strongest method first.

Table 13-1. IIS Authentication Options

Authentication Method

Description

Basic authentication

The username and password information are passed to the server, encoded using Base64 encoding. Basic authentication is generally used only for personalization services or in conjunction with SSL encryption because its password encoding is essentially equivalent to clear text and can be "sniffed" from the network by an attacker.

Digest authentication

Similar to basic authentication, but it encrypts the user's password information using a hashing algorithm called Message Digest 5 (MD5) before it sends it over the network. Digest authentication requires a user account to be stored in Active Directory.

Integrated Windows authentication

Uses a challenge/response mechanism (with Kerberos, if Active Directory Services is installed on the server). Unlike basic or digest authentication, password credentials are never sent across the network, making this the most secure option. However, this standard won't work across a proxy computer and requires the opening of additional ports before it will work with a firewall. Therefore, it is best suited to internal clients.

Certificate authentication

Uses signed client-side certificates that map each client to a specific user account. This method is extremely secure but difficult to implement. You must purchase certificates from a third party, such as VeriSign, and deploy them to each client computer. For this reason, certificate authentication is rarely used outside of specialized setups.

Note that these authentication standards have different levels of support in various Internet browsers. However, Table 13-1 doesn't note these differences because they won't apply to XML Web services or remote components. In these scenarios, the client is always a dedicated .NET Web or Windows application, not an Internet browser. .NET clients support all of these authentication methods.

To test IIS authentication, modify the directory settings for the test XML Web service by disabling anonymous access and enabling integrated authentication (as shown in Figure 13-2). Then run the XML Web service with the Internet Explorer test page. You'll discover that GetCurrentUser still returns the ASP.NET account, whereas GetIISUser returns the name of the user account you're currently logged on to (as in MYLOCALCOMPUTER\MATTHEW). Internet Explorer automatically submits the required credentials to validate the user with IIS.

Setting Authentication Information with an XML Web Service Client

The preceding example relies on Internet Explorer to submit the required information to the XML Web service. In a distributed application, however, clients don't interact with a remote XML Web service through a browser instead, they use a dedicated client application.

To pass a user's credentials from a client application to an XML Web service, you have to tell the XML Web service proxy which user information it should transfer. If you're using Windows integrated authentication, you can configure your proxy class to use the security credentials of the current user's account, using the System.Net.CredentialCache class, as shown in Listing 13-2.

Listing 13-2 Transparent authentication using the current user
 ' Create the XML Web service proxy. Dim Proxy As New localhost.AuthenticationTest() ' Assign the current user's credentials to the proxy class. Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials ' Perform an authenticated call to a Web method. MessageBox.Show(Proxy.GetIISUser()) 

This is a simple, elegant approach in which authentication is performed transparently, without the user being aware of any part of the process. If you try to make the same call to GetIISUser without assigning any credential information, the call will fail and an exception will be thrown.

If you aren't using Windows integrated authentication or you want the ability to use a different user account for making remote class, you need to create a NetworkCredential object manually (as shown in Listing 13-3).

Listing 13-3 Manual authentication with a NetworkCredential
 ' Create the XML Web service proxy. Dim Proxy As New localhost.AuthenticationTest() ' Create the credential objects. Dim Credentials As New System.Net.NetworkCredential("name", "password") ' Assign the credentials to the proxy class. Proxy.Credentials = Credentials ' Perform an authenticated call to a Web method. MessageBox.Show(Proxy.GetIISUser()) 

This technique is useful if the computer always runs under a fixed account, but different users use the application to perform Web method calls. In this case, your client application will probably retrieve the user information from a custom login dialog box. Alternatively, you can retrieve the information from the registry or configuration file if you intend to use a fixed account, no matter which user is currently using the application. In this case, remember to encrypt or hash passwords before you store them anywhere, using the cryptography classes discussed later in this chapter.

Setting Authentication Information with a .NET Remoting Client

You have the same two options with a .NET Remoting client, each with slightly different syntax. Because you don't directly interact with the proxy for the remote object, you must use the ChannelServices class (from the System.Run­time.Remoting.Channels namespace) to add the credential information to the channel.

Listing 13-4 shows an example that uses the credentials of the current user. It assumes that you've imported two .NET Remoting namespaces: System.Run­time.Remoting and System.Runtime.Remoting.Channels.

Listing 13-4 Transparent authentication in .NET Remoting
 ' Configure the client using a configuration file. RemotingConfiguration.Configure("Client.exe.config"); ' Create the remote object. Dim RemoteObject As New RemoteClass()  ' Find the properties for the channel used to communicate with ' the remote object. Dim Properties As IDictionary Properties = ChannelServices.GetChannelSinkProperties(RemoteObject) ' Assign the current user's credentials to the channel. Properties("credentials") = CredentialCache.DefaultCredentials ' Perform an authenticated call to the remote object. MessageBox.Show(RemoteObject.GetIISUser()) 

Listing 13-5 shows similar code that assigns the security information using a new NetworkCredential class.

Listing 13-5 Manual authentication in .NET Remoting
 ' Configure the client using a configuration file. RemotingConfiguration.Configure("Client.exe.config"); ' Create the remote object. Dim RemoteObject As New RemoteClass()  ' Find the properties for the channel used to communicate with ' the remote object. Dim Properties As IDictionary Properties = ChannelServices.GetChannelSinkProperties(RemoteObject) ' Assign the credentials to the proxy class. Properties("username") = "name" Properties("password") = "password" ' Perform an authenticated call to a Web method. MessageBox.Show(RemoteObject.GetIISUser()) 

Available User Information

With the approach shown so far, access is allowed for any user who can be authenticated on your server. This is a starting point, but it probably doesn't satisfy your security needs. To provide fine-grained authentication, your server-side object needs to investigate the caller's identity and determine whether the user has sufficient permissions to perform the requested task. This is generally described as role-based security. With Windows authentication, each role corresponds to a Windows group, as shown in Figure 13-3.

Figure 13-3. Sample Windows groups

graphics/f13dp03.jpg

To create new groups or to add or remove users to and from a group, choose Settings, Control Panel, Administrative Tools, Computer Management from the Start menu. The process is quite straightforward: Any Windows user can belong to as many groups as required. Some built-in groups are always available and don't need to be created, including Guests, Administrators, and Power Users.

In your application, groups identify access levels. For example, you might create a group called Project Managers and give it a different level of permissions than the Programmers group in an incident-tracking application. However, using Windows, you can only add the groups and configure group membership, not define the permissions assigned to a group. It's up to your application to assign application-specific privileges to the appropriate group.

To test whether an authenticated user is in a given role, you use the IsInRole method of the current security principal. This method accepts a single string parameter, which represents the group name, and returns True if the user is a member of the requested group. Here's the code you would use in an XML Web service:

 If User.IsInRole("Manager") Then     ' (Perform requested task here.) Else     Throw New SecurityException("Invalid role.") End If 

If you're accessing another server or domain controller, you need to use the path notation introduced earlier, which uses the syntax [MachineName]\[GroupName] or [DomainName]\[GroupName].

The logic is similar in a component over .NET Remoting, although the security principal must be retrieved from the current thread:

 If Thread.CurrentPrincipal.IsInRole("Manager") Then     ' (Perform requested task here.) Else     Throw New SecurityException("Invalid role ") End If 

In both cases, you're accessing the security principal through the IPrincipal interface (found in the System.Security.Principal namespace). The IPrincipal interface provides only a single method (IsInRole) and a single property (Identity). You can gain some more functionality by casting the security principal object to the WindowsPrincipal type, which represents Windows-authenticated information. The WindowsPrincipal object provides three overloaded versions of the IsInRole method: one that takes a string name, one that takes an enumerated value representing one of the built-in Windows groups, and one that takes an integer that represents the unique role ID (RID) assigned to the group.

Listing 13-6 shows a test method you can add to an XML Web service to determine whether a user is an administrator.

Listing 13-6 Manual authentication in .NET Remoting
 <WebMethod()> _ Public Function IsAdministrator() As Boolean     Dim Principal As WindowsPrincipal     Principal = CType(User, WindowsPrincipal)     Return Principal.IsInRole(WindowsBuiltInRole.Administrator) End Function 

What's lacking from all this is a way to separate the security code from the business logic (as you can with COM+ role-based security). Unfortunately, .NET does not provide any equivalent for declarative security. Although the .NET Framework does include a PrincipalPermission attribute that allows only a specific user or role to access a method, it works on the account of the current user (which will be the ASP.NET worker process or the account used to run the component host), not the IIS-authenticated user. This makes it relatively useless unless you're using impersonation.

Impersonation

Impersonation is the process by which certain actions are performed under a different identity. You can use impersonation with a remote component to execute code under the caller's identity rather than the identity of the ASP.NET worker process or the account running the component host.

Impersonation is generally discouraged because it complicates and often weakens security. If you use impersonation with an XML Web service, for example, you also need to grant the user (or the user's group) permissions to server-side resources such as files and databases. This is a slightly uncomfortable situation because it could potentially allow the client to access the server and perform other operations that should be disallowed (such as deleting files). Usually, it's more secure to grant access only to the account used by the server-side object, which can then use these permissions to access server resources on behalf of the client.

In some cases, however, impersonation might be useful. If you have a file-lookup service, for example, you might want to use Windows ACLs to determine whether a given user can retrieve a specific file. Without impersonation, you have to write all the security code yourself, and your service could be "tricked" into accessing a file it shouldn't if the code contains an error. If you use impersonation, however, you don't have to write any security code. If the code attempts to access a disallowed file, an exception will be thrown.

With an XML Web service, you can enable impersonation using the web.config file:

 <configuration>     <system.web>         <!  Other settings omitted.  >         <identity impersonate="true" />     </system.web> </configuration> 

In this case, the entire Web method executes under the caller's security context. However, it's more common to use programmatic impersonation. This gives you the freedom to use impersonation for some methods and not for others. It also enables you to use impersonation just for certain segments of code.

Note

If you must use impersonation, programmatic impersonation is generally much more practical and flexible. Otherwise, you might need to grant additional unrelated permissions to the users who need to access your XML Web service. For example, XML Web service users will require read/write access to the Temporary ASP.NET Files directory where the compiled ASP.NET files are stored. This directory is located under the following path: C:\WINNT\Microsoft.NET\Framework\[version]\Temporary ASP.NET Files.


To use programmatic impersonation, you use the WindowsIdentity.Impersonate method. This enables you to execute some code in the identity of a specific user (such as your file access routine) but run the rest of your code under the local ASP.NET account, which ensures that it won't encounter any problems.

Listing 13-7 presents an example of programmatic impersonation. It retrieves the identity object, performs some actions under its context, and then reverts to the original ASP.NET account. You can use the same process with .NET Remoting; just remember to retrieve the identity from the Thread.CurrentPrincipal property.

Listing 13-7 Using programmatic impersonation
 If Not User.GetType() Is WindowsPrincipal     ' User was not authenticated with Windows authentication.     Throw New SecurityException( _       "Windows authentication was not performed.") Else     ' Retrieve the identity object.     Dim Identity As WindowsIdentity     Identity = CType(User.Identity, WindowsIdentity)     ' Impersonate the identity.     Dim ImpersonateContext As WindowsImpersonationContext     ImpersonateContext = ID.Impersonate()     ' (Perform tasks under the impersonated account.)     ' Revert to the original ID.     ImpersonateContext.Undo() End If 

Note

Other forms of ASP.NET authentication, such as Forms-based authentication and Passport authentication, aren't supported in XML Web services (or .NET Remoting). This limitation results from the fact that there is no way to submit the user credentials with each request.




Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

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