| ★ wanayoo — archive 1999 http://rc3.org/tech/secureForms.html | Nouvelle recherche | Portail wanayoo |
Designing Secure Forms-based Applications
March 1, 1998
Miora Systems Consulting have, in their overstated way, brought to light some significant design flaws that seem to be cropping up in Web applications. Basically, these problems revolve around the use of hidden form fields. These problems stem from the fact that hidden form fields are used to maintain state in Web applications, in other words, they're used to pass data from one form to another without displaying it on the user's screen.
The problem is that, apparently, many Web designers were working under the assumption that users couldn't modify, or perhaps even see the contents of these "hidden" fields. Unfortunately, this is far from true. The bottom line is that all users have to do to see what a designer has stored in a hidden form field is view the page's source. If they want to alter the contents, then they just have to download the source, insert the values that they wish to use, and then submit the form to your CGI script.
Miora breaks down the scope of the vulnerability into five areas. I'm going to simplify these categories a bit, and explain how you can design (or redesign) your applications to avoid these pitfalls. Miora offers a free fix that works around the problem using encryption, but I'm sure you'd rather learn how to write your programs properly.
Let's take a look at the issues, and how to avoid getting burned by them:
- Hidden form fields aren't really hidden.
As I've mentioned previously, hidden form fields aren't really hidden at all. Any information stored in a hidden form field can be seen by anyone who views the source code of the web page. If you've stored runtime options to your CGI script, private user data, or anything else in hidden form fields that you don't want seen (or modified) by users, then you shouldn't use hidden form fields, or take some steps to obscure that information.
- Hidden form fields allow users to gain unauthorized access to application functionality.
Let's say that you've written one CGI script that provides back end functionality to a number of different forms. You use three different forms to access the CGI script, two of them are available to all users, but the other is only available to users who have been authenticated. You tell the CGI script which functionality you want to use through the contents of a hidden form field. Unfortunately, unless you add some sort of authentication to the CGI script itself, any user can access the functionality embedded in the protected form by creating a web page of their own, and inserting the proper value into the hidden form field you used to control the script.
Proper design dictates that you either split the functionality into separate CGI programs, so that you can limit access only to authorized persons, or you build in some authentication in your program to make sure that the person who called the script is, in fact, authorized to do so.
- Hidden form fields allow users to attack CGI programs using bad input.
This problem comes as a direct result of laziness and ignorance on the part of Web programmers. An important fact to realize when you're designing forms based Web applications is that any data that is sent to the Web server through a form can be edited by the user. While it would seem at first glance that items in SELECT fields and HIDDEN fields are hard coded, I must reiterate that all a user has to do to send whatever data they want to the Web server is download the source and insert their own data into the fields.
Because all data in forms is in the hands of the user, you must always check that data to make sure that users aren't trying to send bad input to exploit your CGI script. One important security measure to take under any circumstance is to run your web server as an unprivileged user. Doing so will help to prevent people who do manage to exploit your web applications from taking control over privileged system resources. Even so, you should still take precautions to make sure that you always check input from users to make sure that it's valid. For example, what happens if a user enters 10,000 bytes of data in a text input field in your form and submits it? If your CGI program is an improperly written C program, then it could make you vulnerable to buffer overflow types of attacks.
Let's look at another plausible attack. You've written a CGI script that accepts comment forms on your web site. You want to use the same script no matter who the comment will be sent to, so you specify the recipient in a hidden form field on the page. Because the recipient is hard coded, you don't do any checking on the email address. A user downloads the form, and changes the recipient to include some shell commands encapsulated within backtics (`), which will most likely be executed because you didn't check for them when the form was submitted.
The bottom line here is that you can never, ever assume that you are receiving the data you expect from a form. You should always perform some checking within your application to make sure that a user hasn't slipped you some potentially harmful information.
- Hidden form fields make it easier for users to break your state preservation mechanism.
Any time an application spans multiple forms, it makes sense to use a mechanism to preserve the application's "state." A perfect example is the "shopping cart" metaphor. As a user shops, they may want to select certain items that they wish to purchase. As they browse through the site, a state management mechanism is used to keep track of those items as they move from page to page, so that they can check out whenever they wish. Without such a mechanism, users would have to write down all the items they wanted, and enter them in an order form before checking out.
A number of different solutions are applied to manage user state. Perhaps the most common these days is the use of a cookie, which either contains all of the state information (like the current contents of the shopping cart), or a session ID that is mapped to an entry in a file or database with the full state information. Many ecommerce systems also embed the session ID for the user within the URL for the page. Every time the user moves to another page, the session ID is inserted in the URL and maps to the session data stored somewhere on the server. This method is common because it's fairly secure, and it doesn't rely on cookies to work. The low-tech solution to this problem is keeping the state information inside a hidden form field.
There's a common problem with maintaining state on the Web, but it is most significant when the designer maintains state through hidden form fields. The problem is that if a malicious user finds out a session ID (or whatever other data the designer uses to manage state), they can enter it themselves and hijack the session corresponding to that ID.
Ignoring the shopping cart metaphor for a bit, let's consider the idea of an online conferencing system. When a user first arrives, they must log in. In order to prevent them from having to log in every time, you store some session information on the user. Because you're lazy, you simply store their user ID in a hidden field in every page. If you don't take any more precautions than that, a user could log in as one person, modify the form and insert the user ID of some priveleged user, and continue their session as that person. You can imagine the implications if this were an online banking application.
Fortunately, these types of attacks on a session management system are quite preventable, as long as the system is designed with security in mind. Let's take a look at a few precautions you can take to protect yourself from these sorts of attacks.
- Never use user names, or passwords, or other relevant data as the session ID. This makes it easier for malicious users to hijack your sessions. Also, generating a new session ID for each session allows you to address each session individually.
- Always make sure that your sessions time out after a period of inactivity. You want to give users enough time to process the content of each page before their session times out, but you don't want sessions to linger long after the user finishes with the site.
- Provide users with a "Sign Off" button, which allows them to terminate their session before they leave the site. This also provides a measure of protection that can prevent sessions from lingering after they're out of use.
- Map the session ID to another identifier, like the user's IP address. This prevents users on one computer from hijacking the session from another, although it doesn't make it impossible.
Conclusion
Once you realize the two most important facts about using hidden form fields, which is that they can be viewed by anyone, and that they can be altered by anyone, you can take the proper security stance when designing Web applications.
The MSC fix for this problem build encryption into your applications so that the contents of your hidden form fields are always obscured so that users can't view or change them. While I haven't personally used it, it seems like a good solution to the problem, and one that you may want to use even if your applications are properly designed. However, if you're writing new CGI programs, you should still take into consideration the root problems with form security, and try to design your applications so that they're not vulnerable to them.
Information on my book on CGI programming, Teach Yourself CGI Programming in a Week. Comments? Mail rafeco@rc3.org.
rc3.org
Copyright© 1998 Rafe Colburn.
Site built using Frontier. Last updated 3/22/98; 1:47:15 PM
Email with problems or questions.