Preventing Data Entry Errors


The most common, yet easily preventable, security errors are those that result from data entry and consequent management. For example, if a user inputs a number when you were looking for standard text, then you need to have code that recognizes the error and alerts the user to the problem. Of course, you don’t want the application to fail, so it’s important to let the user retry after you provide instructions for proper input. However, at some point, you have to consider that multiple attempts fall outside the range of errant input and into the range of a cracker probing for holes in your defenses. Issues such as these cause the majority of security problems and they’re all preventable with good programming practices.

The following sections describe various data entry error issues and provide ideas on how to fix them. Not every problem will require the same fix and the text doesn’t include every potential problem. For example, you may find that range checking requires a simple check against a numeric limits, in some cases, but requires direct comparison to a list of acceptable entries in others. The important concept is that you must perform some type of range checking to ensure the input data is valid.

Putting the Time back into Access

One of the biggest problems that developers face is time. Often, accountants, managers, and others who don’t develop code for a living only understand the ticking of the clock and see how the expenditure of time affects the bottom line. The outside world has tried to quantify developer productivity for years. In a race to deliver code within the time expectations of management, developers often cut corners and leave out the code that we all know should appear in the application.

Would it surprise you to know that most of the errors and security issues in code are there because of time issues? Even the best developers get flustered and fail to provide adequate checks in their code because it’s easier to deliver the code now than to hear marketing whine one more time. Time is the enemy of security. Bowing to the demands of time, rather than exercising prudent programming practices, is the biggest mistake any developer can make. Fortunately, the .NET Framework automatically adds code to perform many required security checks. The Common Language Runtime (CLR) uses these automatic code additions to perform security checks during runtime. Unless you trap the error, however, the user sees an ambiguous security error message, rather than a precise message stating how to fix the problem. The .NET Framework does improve the situation, but can’t fix it completely.

Explaining the dichotomy of time versus quality to your boss isn’t an easy undertaking and will be impossible if you can’t quantify the argument in some way. It’s important to put the science back into the art of programming by creating an underpinning of quality within your organization. For example, how many developers actually use a checklist to ensure they’ve met every programming need? (Such as the need to include security exception checking within the code.) A checklist requires validation, but once validated, it can provide the best means of quantifying your progress to those who need to know.

Checking the Data Range

A data range defines the acceptable values for data input. For example, when you define a value in your code as Int32, it means that the user can enter any value from –2,147,483,648 through +2,147,483,647. However, your application might not find this range acceptable. When this happens, you must include special code in your application to check for potential error conditions. You might want to accept numbers from 0 to 40,000 in your code, which is outside the Int16 value range, but well within the Int32 value range. In short, a data range is specific to the need at hand.

Generally, you’ll find that value data types are the easiest to range check because they have predefined ranges. The .NET Framework supports more value types than you might think. For example, the .NET Framework considers a color used for drawing (System.Drawing.Color) a value type. You can see the full list of .NET Framework value types at http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemvaluetypeclasshierarchy.asp.

Tip

Always use the .NET Framework specific value types, rather than native types, whenever possible. Although the .NET Framework maps native types to their .NET equivalent, using a .NET type at the outset reduces potential confusion. These types are standard across all languages used for .NET development.

Data ranges can encompass a variety of types. For example, it’s possible to create a discontinuous numerical set to address specific configuration needs. Strings naturally fall into a discontinuous set because words don’t fall into any ordered pattern except as defined for language. In many situations, you can overcome this problem by defining an enumeration and then using that enumeration as input as shown in Listing 3.1. (You can find this code in the \Chapter 03\C#\EnumeratedTypes or \Chapter 03\VB\EnumeratedTypes folder of the source code located on the Sybex Web site.)

Listing 3.1 Enumerating Discontinuous Data Ranges

start example
// Use the enumeration names directly for strings. public enum SomeStrings {    One,    Two,    Three,    Four } // Supply a specific value for numbers. public enum SomeValues {    Value1 = 1,    Value10 = 10,    Value100 = 100,    Value1000 = 1000 } public class DoMsg {    public static void Show(SomeStrings Input1, SomeValues Input2)    {       // Convert Input2 to a number.       Int32 IVal;       IVal = (Int32)Input2;       // Display a message box using the inputs.       MessageBox.Show("The selected string is: " + Input1.ToString() +                       "\r\nThe selected number is: " + IVal.ToString(),                       "Selection Results",                       MessageBoxButtons.OK,                       MessageBoxIcon.Information);    } }
end example

This simple example has two enumerations. The Show() method of the DoMsg class uses these enumerations to define acceptable input values. To any external program, the Show() method will appear to use specific data types. This technique does require more time, but the results are worth it. If an external program were to try to access the Show() method like this:

DoMsg.Show(“Hello”, 2);

the program wouldn’t even compile. Figure 3.1 shows the error message the developer would see. This technique, in effect, limits the data input to the enumerated values that you provide, making security less of a problem.

click to expand
Figure 3.1: Use enumerated types to eliminate data range problems whenever possible.

Checking the Data Length

Many of the exploits crackers use depend on the application not checking the length of the incoming data. For example, the buffer overrun technique (discussed in the “Stopping Buffer Overruns” section of the chapter) has become so popular precisely because few developers check the length of incoming data. Fortunately, for the .NET developer, CLR does much of the length checking automatically and raises an exception immediately after it detects data of the wrong length. Even so, CLR only checks for extreme cases—you still have to perform checks for your particular application.

Data that’s too short can be just as much a problem as data that’s too long. For example, if your program needs a string that it will then parse for appropriate data, it could fail if it suddenly runs out of data before the parsing is complete. Ensuring the data is as long as you need before you begin any processing is the best way to avoid certain kinds of problems. Make sure you tell the user about these requirements (length limits) and present clear messages about the error when it does occur. Listing 3.2 shows a typical example of how you can handle a data length issue. (You can find this code in the \Chapter 03\C#\DataLength or \Chapter 03\VB\DataLength folder of the source code located on the Sybex Web site.)

Listing 3.2 Detecting and Handling Data Length Errors

start example
private void btnTest_Click(object sender, System.EventArgs e) {    try    {       // Process the input text.       if (ProcessData(txtInput.Text, 8, 4))          // Display a result message for correct input.          MessageBox.Show("You typed: " + txtInput.Text,                          "Input String",                          MessageBoxButtons.OK,                          MessageBoxIcon.Information);    }    catch (SecurityException SE)    {       // Display an error message for incorrect input.       MessageBox.Show(SE.Message,                       "Input Error",                       MessageBoxButtons.OK,                       MessageBoxIcon.Error);    } } private void btnBadTest_Click(object sender, System.EventArgs e) {    try    {       // Process the input text.       if (ProcessData(txtInput.Text, 4, 8))          // Display a result message for correct input.          MessageBox.Show("You typed: " + txtInput.Text,                          "Input String",                          MessageBoxButtons.OK,                          MessageBoxIcon.Information);    }    catch (ArgumentException AE)    {       // Display an error message for incorrect input.       MessageBox.Show(AE.Message,                       "Argument Error",                       MessageBoxButtons.OK,                       MessageBoxIcon.Error);    } } private Boolean ProcessData(String Input,                             Int32 UpperLimit,                             Int32 LowerLimit) {    // Check for an input error.    if (UpperLimit < LowerLimit)    {       System.ArgumentException   AE;       AE = new ArgumentException("The UpperLimit input must be " +                                  "greater than the LowerLimit number.",                                  "UpperLimit");       throw(AE);    }    // Check for a data length error condition.    if (Input.Length < LowerLimit || Input.Length > UpperLimit)    {       SecurityException SE;       SE = new SecurityException("String is the wrong length. Use a " +                                  "string between 4 and 8 characters " +                                  "long.");       throw(SE);    }    // If the data is correct, return true.    return true; }
end example

This example actually shows two kinds of checking you need to provide—both of which are tested in the ProcessData() method. The first is the user input (demonstrated in the btnTest_Click() method). The input has to be a certain length for this application, and the ProcessData() method checks to ensure it is. The second is the input arguments (demonstrated in the btnBadTest_Click() method). The ProcessData() method uses a separate check for argument errors.

Notice that this example can create one of two exceptions. The ArgumentException object represents cases where the arguments are incorrect, while the SecurityException object represents incorrect user input. It’s important to use the right type of exceptions when you track problems such as data that is the wrong length—even if you have to create new exceptions for the job. Tracking potential security problems such as incorrect data length begins when you provide good error documentation by using different exception objects for each error type.

Keeping Unnecessary Characters Controlled

Unnecessary characters are any characters that your application doesn’t require as input. For example, if your application doesn’t require any slashes, then you shouldn’t accept them as input. Likewise, you should avoid any numeric or control character input unless you actually need them to make the application work properly.

Tip

You may think that checking individual characters in a string isn’t worth the effort. Until you actually see some of the exploits that crackers use, it might seem that character checking is a solution in search of a problem. The Security Focus site articled entitled, “Abusing poor programming techniques in webserver scripts V 1.0” at http://www.der-keiler.de/Mailing-Lists/securityfocus/secprog/2001-07/0001.html shows that this is a significant problem. Pay careful attention to this example. Notice that it breaks several of the rules listed in this chapter, including allowing the use of nonstandard characters.

Crackers have devised a number of interesting exploits over the years to make use of extra characters. The problem is especially severe for Web applications where an application could actually end up combining several fields together if the cracker provides specific input. (It’s easier to create combined fields in a Web application because form data isn’t strongly typed and there is less direct separation of the data through use of individual variables.) However, even desktop applications need to be aware of unwanted characters because some control characters can damage data or cause other problems. Listing 3.3 shows an example of how you can control unwanted characters. (You can find this code in the \Chapter 03\C#\UnwantedInput or \Chapter 03\VB\UnwantedInput folder of the source code located on the Sybex Web site.)

Note

The code used to call the CheckChars() method (see Listing 3.3) is similar to the btnTest_Click code show in Listing 3.2. Check the source listing for details.

Listing 3.3 Avoiding Unwanted Characters in Input

start example
private Boolean CheckChars(String Input) {    // Create a regular expression for match purposes.    Regex R;    R = new Regex("[A-Za-z]");    // Check for a data length error condition.    if (R.Matches(Input).Count < Input.Length)    {       SecurityException SE;       SE = new SecurityException("String contains incorrect " +                                  "characters. Use only A through Z " +                                  "and a through z.");       throw(SE);    }    // If the data is correct, return true.    return true; }
end example

The CheckChars() method relies on a Regex (regular expression) object to perform its task. In this case, the Matches() method counts the number of times the letters in the input string match the allowed letters in R. If the number of matches is equal to the length of Input, then the input string doesn’t contain any unauthorized letters.

Of course, someone could provide characters that don’t match R. This example uses a SecurityException object to raise an exception when the input contains incorrect characters. Your program could react in a number of ways, but displaying the default error message probably works best. The idea is to present the user with the list of acceptable input choices. This is one situation when you might want to track the number of times the user makes incorrect entries to detect crackers looking for weaknesses in your defenses.

Tip

Avoid the problem of free-form text input whenever possible by using any of the controls that provide controlled input such as list boxes, combo boxes, and check boxes. Eliminating the chance of incorrect user input is always better than handling the incorrect input later.

Providing Precise Help

It always pays to provide good help with your application. A good help file can prevent many kinds of user input errors by showing the user precisely what your application expects to receive. Reducing input errors makes it possible to perform thorough analysis of the errors that remain, which reduces security risks from incorrect input in the end.

Some data types present special challenges that your application must handle to ensure data integrity, as well as address security concerns. For example, a date is a common data entry item that could present problems. First, you need to consider the format of the date. A user could type 1 June 2003, 06/01/2003, June 1, 2003, 2003/06/01, or any other acceptable variant. Desktop applications usually don’t have many problems with dates, but they can become a problem with Web applications because the application must parse the date into an acceptable format. Allowing just one type of entry and telling the user about the precise data format will reduce the security problems—it’s easier to determine when the data is correct.

Note that providing precise help won’t eliminate all data entry errors. Some users will insist on attempting to enter data using whatever means they think best. In addition, there are biases to consider. A particular locale may use a specific date form and the user will use that form out of habit. Even so, most users will get the point after a few tries, making good help beneficial. When you begin noticing patterns of constant abuse despite the help, your security screens should go up and you should consider the kind of user making the error.




.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