Every week, it seems like the news media is spewing forth a new list of critical flaws in Windows. On the Internet, messages go out at a blinding pace discussing vulnerabilities in various programs, including those for both Linux and Windows. Despite this attention, it still appears that application security is getting worse, not better. In this article, we'll talk about some of the things you can do to prevent your application from making the news as the next big Internet security threat.
Never trust what you don't control
One of the fundamental tenets of security is not to trust without verification. Applied to application development, this means that you shouldn't trust a value that you don't control. All user input must be tested to make sure it conforms to your expectations.
In a Web application, this can mean form-posted variables, cookies, and query strings. A terrifying number of Web-based applications are constructed to trust without question the information provided by the Web browser. The ways in which users can tamper with the information they submit are nearly limitless. Cookies can be hijacked and changed to include new values. Users can change query string submissions simply by keying a new value into the address bar. Form postings can be changed by modifying the page provided, saving it to a hard disk, and running it from there. All of these techniques are simple.
In client-side applications, you have similar problems when users tamper with your input; but, they are more often about what you store on disk, in a configuration file, or in a database, not about what's crossing the wire. Automatically storing a username and unencrypted password in a configuration file or in the registry can make that information vulnerable to others. Anything that has not been securely encrypted can be manipulated or stolen.
Despite the opportunities for users to modify information, there are ways you can protect your application from malicious users. Here are a few techniques that can make your applications immune to the most common types of attacks.
Don't leave it out unnecessarily
One key security consideration is not leaving your information unnecessarily exposed. This means not transmitting or storing information that you don't need to. In other words, nothing is foolproof. If you don't need to send a value across the Internet or store it in a configuration file—even encrypted—then don't. If you give hackers the opportunity, they will find some flaw in your approach; so, don't give them the opportunity. For example, you shouldn't send the users' GUID for the system or their username and password. Select their unique identifier, and don't bother sending across the username and password, which might be stolen.
Keep it unintelligible
Security by obscurity (not letting someone know about a flaw) is not a valid answer. However, making it easy for someone to find points at which to attack is not the answer either. When you must place a crucial piece of information on the client, consider how best to identify the information. Using meaningful names may make sense; however, they may make attacking your application easier. For instance, in a Web application, if you need to pass the user's internal ID to the client, you may want to avoid calling the variable, UserID. Of course, you should never send the user's ID unencrypted to the client; but, even when you send the encrypted value, you shouldn't make it clear to the client what its purpose is.
Obviously, using some sort of cryptographically sound method of converting the information into unintelligible information is important. Encryption brings with it tamper resistance as well as the ability to obscure the information's meaning. Both are important components of securing your application. However, encryption alone won't prevent every problem from occurring.
Watch for tampering
Being tamper resistant is not the same as being tamper proof. Logging attempts to break through tamper resistance is an important part of an application's defense plan. When designing applications, it is important to create logs of situations when tampering is detected so that system administrators who run your applications can take appropriate responses to tampering attempts.
Logging can be done within the application event log, a database table, or a flat file. The key to logging these types of events is working with system administrators to learn how they will watch for errors. Typically, you'll want to select a way of logging that they are already monitoring—perhaps even using automated tools. Monitoring log files is often one of the most overlooked activities by systems administrators; so, you want to make the process as easy as possible.
Remember that, given enough time unmonitored, the barbarians will crush the gate. They will pour through your defenses and overcome your application's defenses.
One of the best ways to ensure that code is resistant to attack is to have it reviewed by other trusted individuals. An unfortunate fact of software development is that there is still a substantial amount of code that is written by one person and never reviewed or even read by anyone else. As a result, any oversight made by the developer and not caught by testing creates an opportunity for a security problem.
Code reviews allow other developers—with fresh perspectives—to look at the code. When properly focused, code reviews can identify flaws in the code, as well as design flaws, which may allow unintended access to the application. While code reviews are an essential part of good development practice, they are also essential to securing applications.
When performing a code review of someone else's code (or while doing your own coding), you should consider the following:
- Validating Input: What would happen if the value contained in the string wasn't what was expected? What if it had special characters? Could someone cause bad behavior simply by typing something bad into an input field?
- Underflow: What will happen if there aren't enough values? Will the system log an error, or will it accidentally use some random piece of memory? Is it necessary to test how many values are arriving?
- Overflow: What happens when there are more values, or a greater value, than the code is expecting? Does the problem raise an error when detected, or does it randomly fail?
- Looping: A close cousin to overflow is creating situations in which excessive numbers of loops are executed. An intruder might use this to slow down the site so that counter measures can't run, administrators can't be notified, etc.
If you would like to see how input overflow works in a Windows environment, you should read this explanation from the Cult of the Dead Cow.
One often-overlooked task in putting a site together is error handling. Errors, whether system induced or caused by application bugs, are a fact of computing. Unfortunately, most applications don't adequately plan for error handling and, as a result, often end up displaying a generic error message to the client. Although something that may not inspire much trust with your application, it probably won't be a major security risk.
The problem comes in when detailed errors are exposed to the client, whether through a dialog in a client application or through a detailed error message on a Web page. In either case, you may be exposing too much about the internal operation of the application, which someone could use to attack the application.
For instance, while developing a Human Resources application for one organization, we developed a cross-platform encryption, which encrypted the Social Security number of the person we were viewing. This effectively prevented anyone from calling our interface without the appropriate encryption. However, the application that was calling us was not well written and, as a result, it would occasionally dump out quite a bit of information about the encrypted call it was making to us. Fortunately, it never disclosed the actual encryption method or the secret key; still, there were certainly more details disclosed through this error than any of us would have liked.
A proper error is one which communicates to the user that an error was logged and that the system administrator has been notified. It doesn’t have details about the error other than those that might allow a user to resolve the problem. For instance, you should indicate that there was a problem with input, which caused the error; however, you shouldn't indicate that the username wasn't found or that the password didn't match.
Putting it together
Application security isn't something that will be developed in a day; however, small changes in what you do and how you approach development can have a huge impact. Consistency is the true key to getting application security right.