Keeping the Server Safe


It’s easier to keep a problem at bay than it is to resolve the problem once it occurs. For example, it’s much easier to scan incoming email for a virus than it is to clean the virus off the system once it executes. Virus makers are producing new versions of their product every day, so vigilance is an essential element of keeping the virus out of the system. The virus scanner requires constant updates to keep the system safe. However, the media tells network administrators that they don’t have time to keep the virus scanner up-to-date, so the network administrator doesn’t perform the work and the virus arrives—costing the network administrator more time. If the network administrator truly lacks the time required to maintain the virus scanner, where did the time come from to clean up the virus? This simple paragraph illustrates a problem in believing everything you read. Often, if the media tells people something long enough, they tend to believe it. However, the bottom line with security is that if you don’t have time to perform a preventative task, make the time. It’s always going to cost you more time to clean up a problem created by virus than to simply prevent it in the first place.

Tip

The developers that are most successful at creating secure applications often think outside the usual range of security threats. Sometimes security is a study of the absurd. A cracker isn’t going to wait for you to discover the next method of exploiting your system. In fact, the cracker hopes that you never discover the method used to enter your system illegally. To demonstrate just how absurd some cracking methods can become, consider the CNET article at http://news.com.com/2100-1009_3-1001406.html?tag=fd_top. In this case, the cracker uses a simple lamp to introduce an error into a computer system, which allows the cracker to run his code. This technique is very similar to the buffer overrun technique described in the “Understanding How Buffer Overruns Work” section of Chapter 3.

The following sections contain essential coding techniques you need to employ to ensure that the Web server remains safe. These techniques are the ones that you must make time to use, even if you don’t have time to spare. Obviously, they won’t fix every problem, but they’ll help you add essential functionality to your applications. As part of the content in these sections, you’ll also discover a few new development tricks you need to know, such as setting up a Web server to allow remote debugging of an application. Internet Information Server (IIS) doesn’t come set up to provide support for remote debugging, in most cases, and the Microsoft supplied instructions often leave you without a functional setup.

start sidebar
A Case for Using Multiple Machines

I’m a strong proponent of using multiple physical machines for testing and debugging any application with a network task, but especially those designed to work on the Internet. Although testing the application on the local machine using the LocalHost server (or using multiple virtual machines) is very convenient, it’s also not very realistic. When you test an application, especially when security is involved, you need to create the same kind of environment that user will have. A LocalHost setup just doesn’t mimic the Internet very well, so there are going to be holes in your testing if you use it.

When you use two machines, a client and a server, you create an environment that’s different from a single machine setup. For one thing, the information has to travel across a wire to the other machine. You aren’t using the local machine anymore—the information travels to another machine that you haven’t logged on to and even if you do log on, you can set the system up so that you have the same limited rights as any other Internet user. All too often, an application tested on a LocalHost setup fails during deployment because the tester didn’t actually check access conditions on the server using the same setup as the user.

The multiple machine configuration can offer other functionality that you can’t duplicate effectively on a single machine. For example, you can’t duplicate the requirements of passing data through a firewall on a local machine. The data performs an internal loop from the application into the Web server on a local machine, so the application effectively bypasses any firewall. This problem occurred with one developer who wrote an application to use secure communication over a specific port. The application worked fine locally, but failed during deployment because the network administrator didn’t leave that port open on the firewall. Testing on a two-machine setup would have made this problem apparent long before the deployment stage—saving hundreds of hours of debugging time during a critical phase.

Although it might not seem relevant at first, a two-machine setup also lets you test application performance. This question is important because any security you add to the application will affect performance. The internal loop of a LocalHost connection is almost instantaneous, so any performance problems induced by security additions remain hidden. Only when you deploy the application does it become apparent that your application is now so secure that no one can use it because it’s too slow. With a two-machine setup, it’s possible to create the slowest common connection—a dial-up. It’s often a good idea to check your application with this kind of setup to ensure that anyone who needs it can use it.

end sidebar

Authentication Techniques

Remember from previous chapters that authentication is the act of validating the identity of a caller. You authenticate the caller to ensure that they have the right to access the system. Authentication doesn’t give the caller any rights, but it does provide them with a key for accessing resources and services. Consequently, authentication is your first line of defense for a Web server. If someone can’t prove that they have the right to access the system by providing a verifiable identity, the communication with them should stop immediately. Unfortunately, this first line of defense is usually breached without too much trouble because crackers have access to an interesting array of software that helps them discover passwords used for authentication on most Web sites all too easily.

You can reduce the attacks on your system by ensuring that users employ hard to guess, but easy to remember passwords. If the password a user selects is too hard to remember, they’ll write it down, which means reduced, not enhanced, security. I usually recommend a random word, followed by a special character, followed by another random word such as lizZard#fiLe. Notice the use of mixed case lettering to make the password harder to guess—the use of capitalization doesn’t always work well, but it can help. Also, notice that this password is relatively long—use a minimum of a ten-character password. A random list of letters, numbers, and special symbols such as rApu9u##s8E6 won’t work because the password is too hard to remember. For this reason, most automatic password generators won’t work. One exception is Random Password Generator-Pro at http://www.hirtlesoftware.com/p_passpr.htm because you can use it to generate the random word technique I described and it uses a very large dictionary (making the random words harder to guess).

Use hard to guess usernames as well. Some companies use the first initial of the user’s first and middle name, along with their last name. In this situation, my username is JPMueller. That’s too easy for the cracker to guess—you’re giving the cracker half the entry immediately. Try a username followed by some numbers such as John_1234 or Gerry(1212. Again, make sure you keep things hard to guess, but easy for the user. You don’t want the user to write their username down.

Make sure you have strict limits on the number of retries your application will accept. Log a security event in the Event Log when the number of retries exceeds this limit. In addition, disable the account until the network administrator determines the cause of the password entry over the limit. Don’t use automatic resets because crackers will rely on the password resetting itself after a specific time interval (usually 24 hours). Configurable limits are best, but typically three to five password retries work well.

Yes, this advice flies in the face of what many security experts tell you. In fact, crackers can easily access your system if you don’t combine all three techniques. Believe it or not, crackers have special programs that will try various word and special character combinations one at a time until they find the password for your system. Crackers are patient—they’ll keep trying as long as you make it easy for them to do so. That’s why the non-automatic reset password entry limit is important. The administrator must be aware of any attempt to crack the system. When the administrator detects a cracker attempt by monitoring usage patterns and an unusual number of bad password attempts, they can change both the username and the password for the account.

The following sections describe special authentication techniques for Web servers. However, before we discuss any coding techniques, we’ll discuss some of the problems you can run into when setting Visual Studio .NET up for remote debugging. This helpful advice came from a combination of newsgroup discussions, Microsoft sources, and personal research.

Enabling Authentication for Debugging

Microsoft used to ship Windows and IIS with a modicum of security in place and depended on the network administrator to perform the secure configuration such a system requires. Today, both Windows and IIS come with a wealth of security features already in place, which makes the network administrator’s job a lot easier. Unfortunately, these additional security features often prevent your Visual Studio .NET installation from working correctly, especially when you attempt to perform remote debugging.

One of the major problems that developers run into on the server is that they install Visual Studio .NET in the wrong order. Always install IIS on the server first, and then install Visual Studio .NET. The Visual Studio .NET installation makes some changes to the IIS configuration that you need to ensure the two products will work together properly.

Unfortunately, a simple installation order won’t fix completely the problems that many developers have. A number of developers have also reported an IIS configuration issue that you need to resolve. Given a default installation of a single default Web site, the following procedure helps you make this change. If you have multiple Web sites installed, simply change the procedure to use your test Web site, rather than the default Web site.

  1. Start the Internet Information Services console found in the Administrative Tools folder of the Control Panel.

  2. Right-click the Internet Information Services entry and select Connect from the context menu. You’ll see a Connect to Computer dialog box.

  3. Type the name of the remote server and click OK. The Internet Information Services console will create the connection.

  4. Open the Web Sites folder so you can see the Web sites on the server. Right-click the Default Web Site entry and choose Properties from the context menu. You’ll see a Default Web Site Properties dialog box.

  5. Select the Directory Security tab. You’ll see three groups of entries, as shown in Figure 9.1.

    click to expand
    Figure 9.1: The Directory Security tab contains three groups of security entries.

  6. Click Edit in the Anonymous Access and Authentication Control group. You’ll see an Authentication Methods dialog box like the one shown in Figure 9.2. This figure shows a typical default setup.

    click to expand
    Figure 9.2: Check the authentication methods allowed by the Web server.

  7. Check the Integrated Windows Authentication option at the bottom of the dialog box.

Most people set their browser up to reject certain server activities today. The problem is that these settings can interfere with your ability to debug a Web application properly. Make sure you add your test server to the list of trusted sites for the browser on both your server and your development machine. Use the Tools>Internet Options command to display the Internet Options dialog box. Select the Security tab and you’ll see a list of zones. Highlight Trusted, as shown in Figure 9.3, and click Sites. Add the URL of your test site to the list in the Trusted Sites dialog box and click OK twice to close the various dialog boxes.

click to expand
Figure 9.3: Verify that your test Web site is part of the Trusted zone for your browser.

Note

Keep your production and development systems separate. Even though the development system configuration should match the production system as closely as possible, using the same system for both purposes usually leads to security problems. Installing the special components you need for remote debugging makes it easier for crackers to gain access to the system. In addition, the test code tends to make the system unstable, which helps the cracker achieve certain exploits, such as buffer overflows.

Some developers forget that their rights are different when working with a Web application than a desktop application. In some cases, this difference means the application you could access using a LAN connection is no longer available using an Internet connection. Make sure you have sufficient rights to debug your Web application. One of the most commonly missed requirements is ensuring that you’re part of the Debugger Users group (added by Visual Studio .NET to the Users folder of the Active Directory Users and Computers console when using Active Directory).

Make sure you start all of the services required for complete IIS access. For example, if you don’t start the World Wide Web Publishing Service, the debugger won’t start either. In addition, check the Visual Studio Debugger Proxy Service to ensure that it starts when you start the debugger. If you don’t see this service start, try starting it manually. Verify that the ASP.NET State Service starts when using the debugger for remote debugging (you may find it stopped at other times). You can find all of these services in the Services console located in the Administrative Tools folder of the Control Panel.

You’ll also run across a number of project-specific problems. For example, creating a project in a folder that has one or more spaces in the name can cause problems because Visual Studio .NET might continue using the space, rather than insert a %20 to represent the space. Consequently, IIS looks for the part of the URL before the space and fails because it can’t find the resource. In short, don’t use folder names with spaces in them. However, this problem points to the larger issue of project-specific problems. If the debugger suddenly stops working with a specific project, look for errors in that project’s setup, rather than with IIS or the Visual Studio .NET setup.

Using the AuthenticationManager Class

The AuthenticationManager class serves a similar purpose to the SecurityManager class used for several examples in the book such as the one found in the “SecurityManager Class Example” section of Chapter 2. You can use the AuthenticationManager class in a number of ways. Two common ways are to obtain information about the current authentication setup and to manage the authentication modules. For example, you can use this class to install a custom authentication module. You also use the AuthenticationManager class to pre-authenticate and to authenticate client requests.

Most developers understand authentication. The client makes a request, the server challenges the client to provide proof that the client has permission to access the resource, and the client provides the proof. At a minimum, the standard authentication technique requires three trips across the wire. When you’re working on a LAN, three trips isn’t such a big deal. However, if you’re using a dial-up connection, three trips can seem like an eternity.

Tip

Don’t assume that pre-authentication is another in a long line of special Microsoft behaviors for IIS. Microsoft bases this behavior on Section 3.3 of RFC2617 (see http://www.faqs.org/rfcs/rfc2617.html for details).

Pre-authentication is just another method of authenticating the client. However, instead of using the challenge/response system, the client simply acknowledges the need to authenticate at the outset and sends the required credentials. Instead of three trips, the process only requires one trip across the wire. Listing 9.1 shows one method of using the pre-authentication technique within a client application making a request to a Web server. You’ll find this example in the \Chapter 09\C#\AuthManager and \Chapter 09\VB\AuthManager folders of the source code located on the Sybex Web site.

Listing 9.1 Using the AuthenticationManager Class for Pre-Authentication

start example
private void btnTest_Click(object sender, System.EventArgs e) {    IEnumerator             RMods;   // A list of registered modules.    IAuthenticationModule   AuthMod; // Current authentication module.    StringBuilder           ModOut;  // Pre-authentication module list.    WebRequest              WebReq;  // Contains the Web request.    NetworkCredential       Cred;    // Authentication Credentials    Authorization           Auth;    // Authorization if successful.    // Setup the output string.    ModOut = new StringBuilder();    ModOut.Append("Registered Authentication Modules\r\n");       // Create the Web request.    WebReq = WebRequest.Create(txtURL.Text);    WebReq.Method = "Get";    WebReq.PreAuthenticate = true;       // Create the credentials.    Cred =  new NetworkCredential(txtName.Text, txtPassword.Text);    WebReq.Credentials = Cred;    // Get the registered module list.    RMods = AuthenticationManager.RegisteredModules;    // Try to pre-authenticate against each module.    while (RMods.MoveNext())    {       // Get the module name.       AuthMod = (IAuthenticationModule)RMods.Current;       ModOut.Append("\r\n" + AuthMod.AuthenticationType);       // Determine if it allows pre-authentication.       if (AuthMod.CanPreAuthenticate)       {          // The module does allow pre-authentication.          ModOut.Append("\t\tYes");          try          {             // Get the authorization.             Auth = AuthMod.PreAuthenticate(WebReq, Cred);             // Display the authorization information.             if (Auth.Complete)                ModOut.Append("\t" + Auth.Message);          }          catch          {             // The authorization didn’t work.             ModOut.Append("\tNot Authorized");          }       }       else          // The authorization module doesn’t allow pre-authentication.          ModOut.Append("\t\tNo");    }    // Display the authentication module types.    MessageBox.Show(ModOut.ToString(), "Registered Modules",                    MessageBoxButtons.OK, MessageBoxIcon.Information); }
end example

The code begins by creating several objects, including a WebRequest and a NetworkCredential object. The WebRequest object is important because it holds the identity of the specific network object that you’ll access using the NetworkCredential object. Notice that you must set the access method using the WebReq.Method property. Failure to tell IIS how you want to access the resource will result in an authorization failure. You must also tell the code to send the required pre-authorization headers with the request by setting the WebReq.PreAuthenticate to true.

Note

The Web request header requirement actually appears at the protocol level. The client must send a WWW-authenticate header with the request or the server won’t recognize the pre-authenticate requirements (which means the authorization will fail). See the WebClientProtocol.PreAuthenticate property description at http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemWebServicesProtocolsWebClientProtocolClassPreAuthenticateTopic.asp.

The AuthenticationManager has a number of interesting features, including the RegisteredModules property. The code uses this property to initialize an IEnumerator object, RMods. The code enumerates each authentication module in turn and converts it to an IAuthenticationModule. The AuthMod.AuthenticationType property contains the name of the authentication module and the CanPreAuthenticate property determines whether the authentication module can pre-authenticate a request.

At this point, the code finally makes use of the Web request and credentials created earlier in the example to call the AuthMod.PreAuthenticate() method. You must perform this task within a try...catch block because there’s a chance the call will fail. The resulting error message simply says there’s a null object reference somewhere without specifying where or which object. The .NET documentation is less than clear on this subject (it doesn’t tell you what the error message means at all and none of the example code shows the try...catch block in use). It turns out that the null object reference is the Authorization object, Auth. The catch portion of the block simply states that the code couldn’t produce an authorization because that’s all you know. When the authorization is successful, however, you can determine whether the authorization is complete and access the authorization message. Figure 9.4 shows a sample of the output of this example.


Figure 9.4: Determining the ability of various authentication modules to use pre-authentication

The results you obtain will vary from the ones shown in Figure 9.4. You might have different authentication modules installed on your system. In addition, the IIS setup determines the output of the various authentication modules and defines whether an authorization attempt is successful.

Working with HTTP-Specific Classes

You might have reason to work directly with a few of the HTTP-specific classes. For example, you might want to control the flow of data between your application and the client more directly than standard programming techniques will allow. Here’s the list of HTTP-specific classes you should consider for protocol-level authentication purposes:

  • HttpWebClientProtocol

  • HttpSimpleClientProtocol

  • HttpGetClientProtocol

  • HttpPostClientProtocol

  • HttpWebRequest

A number of these classes include special properties and methods for authentication purposes. For example, most of these classes include a PreAuthenticate (determines whether the pre-authentication feature is enabled) and a Credentials (contains the caller’s credentials) property. Listing 9.1 shows how to use these two special properties to create an application that is not only secure, but also more efficient than many applications.

The base class, HttpWebClientProtocol, includes a special property, UnsafeAuthenticatedConnectionSharing, which enables or disables connection sharing when the caller logs in using Window NT LAN Manager (NTLM) authentication. All of the derived classes in this chapter also include this property, as well as the SoapHttpClientProtocol and DiscoveryClientProtocol classes discussed in Chapter 11. The documentation is unclear about using this property, but essentially, it turns off the need for the Web server to authenticate the caller for each request and simply keeps the connection open instead.

The UnsafeAuthenticatedConnectionSharing property comes into play in a high performance scenario. Normally, the HttpWebClientProtocol class (and all its subclasses) relies on HTTP 1.1 standard calling with the Connection: KeepAlive header in place. This is an efficient setup when you have multiple unique requests from individual clients. However, what if you have multiple similar requests (such as requesting quotes for a car insurance program) and want to optimize those requests using a pool of connections that you can control through timeouts, maximum number of connections, and other settings? In this case, you’d use the ConnectionGroupName property to ensure that the calls use the same connection pool. Unfortunately, this property disallows authenticated connections unless you also set the UnsafeAuthenticatedConnectionSharing property to true.

Warning

You can create situations that are even more dangerous by setting properties that work well on their own to specific settings in conjunction with the UnsafeAuthenticatedConnectionSharing property. For example, setting the useDefaultCredentials value in an application configuration file to true also sets the useAuthenticatedConnectionSharing property to true. The combination of these properties essentially removes most protections from your system, even when the Web server requires authentication. See Microsoft Knowledge Base article Q323490 at http://support.microsoft.com/default.aspx?scid=kb;[LN];323490 for additional details.

Once you set these two properties, you can create a high-performance setup that uses connections quite efficiently. However, using the UnsafeAuthenticatedConnectionSharing property also incurs a security penalty. Normally, IIS authenticates the caller for each request. Using this property keeps the connection open. User B (the cracker) could gain access to the connection originally opened by User A by obtaining access to the User A credentials. In short, it’s possible to create a security breach using this technique. Given the current state of technology, however, the risk is very low and most crackers will opt for easier exploits. In addition to the usual secure coding practices, you should take the following precautions:

  • Use the pooled connections for different users to avoid opening the door to use of the connection pool by non-authenticated applications.

  • Run the application in a protected environment to avoid possible connection exploits.

  • Use the Windows Integrated Authentication option to reduce the chance of outside credential stealing using a simple username and password.

Authorization Techniques

From previous chapters you know that authorization is the act of giving a caller access to specific resources and services for specific purposes. This step always occurs after authentication, so it’s your second line of defense. You use the authenticated identity of the caller to verify their right to access a resource or service. Most Web servers provide an error message when someone tries to access a resource or service that they shouldn’t. In many cases, the server should cut off the communication at this point and log the error. However, unless you build this functionality into your application, the Web server will continue to allow a potential cracker continued access to the account and associated access rights.

The .NET Framework and CLR do provide you with some help in the area of authorization. When using Windows NT security, a caller has the same access rights from an Internet connection as they do from the local desktop. This free range of access is usually unacceptable because the caller normally needs just enough rights to work with Web applications, not to perform every task associated with desktop access. Fortunately, .NET provides context-specific rights that are controlled based on the caller’s context—an Internet caller will have fewer rights than the same caller from a desktop location.

Flexible authentication is useful if you want to maintain a strict environment without making things difficult on the user. Listing 9.2 shows one method you can use to build on the authorization technique shown in Listing 9.1. In this case, pre-authorization helps the routine build a fast authentication setup that lets the user obtain access to data the first time and with greater efficiency. You’ll find this example in the \Chapter 09\C#\GetResource and \Chapter 09\VB\GetResource folders of the source code located on the Sybex Web site.

Listing 9.2 Accessing a Secured Web Source

start example
private void btnTest_Click(object sender, System.EventArgs e) {    NetworkCredential Cred;       // Authentication credentials.    Authorization     Auth;       // URL authorization.    String            AuthType;   // Type of credential cache to create.    CredentialCache   CredCache;  // Cached credential information.    HttpWebRequest    Req;        // Resource request.    WebResponse       Resp;       // Response to the request.    Stream            DataStream; // The file data.    StreamReader      SR;         // The I/O stream.    // Create the credentials.    Cred =  new NetworkCredential(txtName.Text, txtPassword.Text);    // Validate the authorization.    Auth = GetAuthorized(txtURL.Text, Cred);    if (Auth == null)    {       MessageBox.Show("You don’t have access to this resource.",                       "Connection Error",                       MessageBoxButtons.OK, MessageBoxIcon.Error);       return;    }    // Define the authorization type.    AuthType = Auth.Message.Substring(0, Auth.Message.IndexOf(" "));    // Use the credential to get content.    Req = (HttpWebRequest) WebRequest.Create(txtURL.Text);    CredCache = new CredentialCache();    CredCache.Add(new Uri(txtURL.Text), AuthType, Cred);    Req.Credentials = CredCache;    // Get the response.    Resp = Req.GetResponse();    // Get the data and display it.    DataStream = Resp.GetResponseStream();    SR = new StreamReader(DataStream);    MessageBox.Show(SR.ReadToEnd());    // Close the communication.    Resp.Close(); } private Authorization GetAuthorized(String URL, ICredentials Cred) {    IEnumerator             RMods;   // A list of registered modules.    IAuthenticationModule   AuthMod; // Current authentication module.    WebRequest              WebReq;  // Contains the Web request.    Authorization           Auth;    // Contains authorization.    // Create the Web request.    WebReq = WebRequest.Create(new Uri(URL));    WebReq.Method = "Get";    WebReq.PreAuthenticate = true;    WebReq.Credentials = Cred;       // Get the registered module list.    RMods = AuthenticationManager.RegisteredModules;    // Try to pre-authenticate against each module.    while (RMods.MoveNext())    {       // Get the current module.       AuthMod = (IAuthenticationModule)RMods.Current;       // Determine if it allows pre-authentication.       if (AuthMod.CanPreAuthenticate)       {          try          {             // Get the authorization.             Auth = AuthMod.PreAuthenticate(WebReq, Cred);             // Return the authorization to the caller.             return Auth;          }          catch          {             // Don’t do anything.          }       }    }    // The authorization failed.    return null; }
end example

The example begins by building a credential that it uses to obtain pre-authorization for the selected Web resource. The GetAuthorized() method is a modified version of the code in Listing 9.1. However, in this case, the code returns the authorization to the calling routine. This technique is extremely helpful because it searches for the correct authentication technique and verifies access before the caller goes through the longer process of requesting a resource. Because of the short message, authentication is nearly instantaneous, even when using a dial-up connection.

Once the code verifies that the caller has the proper rights to access the resource and it determines the authentication method, it uses the Auth.Message property to provide the authentication method to the caller. The caller uses this value to build a CredentialCache object containing just one authentication method, which reduces the authentication time when getting the resource. The server only has to check one credential and you already know that the credential will work because the code has already tested it.

To get a resource, the code has to build an HttpWebRequest object (or it can use a simple WebRequest object if desired. The code uses the Req.GetResponse() method to obtain the actual resource and place it in a WebResponse object. Normally, you’d perform some kind of processing at this point. The example simply places the information in a StreamReader object and displays the information it contains on screen by using the SR.ReadToEnd() method.

Obviously, this isn’t the only way to obtain authorization to use a particular resource, but it does demonstrate how you can combine various .NET features to make the process as fast as possible. The user will know almost immediately whether the access will fail because that part of the process is separate from the act of actually accessing the resource.

Communication with Other Servers

A new security issue for many Web developers is the fact that you normally use more than one server. Desktop applications that rely on a LAN often use a single server because the environment is more secure. The application accesses the server in a secure environment, so you don’t need to move your data behind a defensive wall. Web applications often place the business logic and Database Management System (DBMS) behind a wall to provide an additional level of protection. Consequently, you now need to consider communication between servers with different levels of security as part of your application. Because of the relationship between the Web server and the back end systems, you can actually consider this multiple server setup as another line of defense—another chance to get security for your application right.




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

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