EIW Fall 2000 Lecture Notes - Hidden Fields
State Information and CGI programming
Each time a CGI program is run the only information available to the
CGI program comes via the HTTP request. The CGI program has no way of
knowing whether the request is from a user that has "entered" the
system previously unless that information is part of the request.
Consider a Web-Based Pizza Ordering System:
- The user must first identify themselves to the system by entering
a name and password. We might use a form that looks like this:
<FORM ACTION=/cgi-bin/loginpizza.cgi METHOD=POST>
User Name: <INPUT TYPE=TEXT NAME=name> <BR>
Password: <INPUT TYPE=PASSWORD NAME=password> <BR>
<INPUT TYPE=SUBMIT>
</FORM>
|
|
|
|
The HTTP request would look something like this:
POST /cgi-bin/loginpizza.cgi HTTP/1.0
Headers: fooblah
name=whatever&password=somepassword
- Once the user has "logged in" they may order pizza. We might use
a form that looks like this:
<FORM ACTION=/cgi-bin/orderpizza.cgi METHOD=POST>
Type of Pizza:
<INPUT TYPE=RADIO NAME=pizza VALUE=cheese> Cheese
<INPUT TYPE=RADIO NAME=pizza VALUE=veggie> Veggie
<INPUT TYPE=RADIO NAME=pizza VALUE=pepperoni> Pepperoni <P>
Pizza Size:
<INPUT TYPE=RADIO NAME=size VALUE=large> Large
<INPUT TYPE=RADIO NAME=size VALUE=medium> Medium
<INPUT TYPE=RADIO NAME=size VALUE=small> Small <BR>
<INPUT TYPE=SUBMIT>
</FORM>
|
|
|
|
The HTTP request would look something like this:
POST /cgi-bin/orderpizza.cgi HTTP/1.0
Headers: fooblah
pizza=cheese&size=large
The issue that we are concerned with is this: How does system
will know what user is ordering a pizza when the second request is
received?
There is no information in the second query that identifies the user, so
thery is no way the system can tell who is ordering pizza! We've seen this
before - remember when you had to go and change your grade in a student
database by constructing a query string by hand? The problem with that system
is that it assumed that if you knew what kind of query needed to be sent,
you had permission to make changes. Here we have a different problem - we
need to know where to send the pizza!
Using Hidden Fields
One solution to the previous problem is to use a hidden field in the order
form. The hidden field identifies the user (or the session). For
a user named "Hungry Student" we might use the following form:
<FORM ACTION=/cgi-bin/orderpizza.cgi METHOD=POST>
<INPUT TYPE=HIDDEN NAME=name VALUE="Hungry Student">
Type of Pizza:
<INPUT TYPE=RADIO NAME=pizza VALUE=cheese> Cheese
<INPUT TYPE=RADIO NAME=pizza VALUE=veggie> Veggie
<INPUT TYPE=RADIO NAME=pizza VALUE=pepperoni> Pepperoni <P>
Pizza Size:
<INPUT TYPE=RADIO NAME=size VALUE=large> Large
<INPUT TYPE=RADIO NAME=size VALUE=medium> Medium
<INPUT TYPE=RADIO NAME=size VALUE=small> Small <BR>
<INPUT TYPE=SUBMIT>
</FORM>
|
|
|
|
And now the associated query string might look like this:
name=Hungry+Student&pizza=cheese&size=large
Now the CGI program orderpizza.cgi will get all the
information it needs, including the identity of the person ordering
pizza.
NOTE: It is important to realize that our system is not secure, anyone
can look at the HTML source for our pizza order
form and see that requests should include the field "name". If you know
someone's login name you can order 1000 pizzas for them!
A hidden field based pizza serving is running at:
cgi.cs.rpi.edu/~hollingd/eiw/CGI/pizza
Session Keys
Many Web based systems use hidden fields that identify a
session instead of a user. A session includes a
sequence of queries that are all somehow related (typically coming
from the same user over a short span of time). In our case a session
includes the login query and the subsequent order query.
A system that uses session keys requires some kind of
database that is used to store session keys and provide the connection
between the user and a session key. Here is the general idea:
- An initial login form is used and the user provides a name and
password.
- The login CGI checks the name and password, and if everything checks
out a session key is created. Session keys have the following
properties:
- Each session key is unique.
- Each session key has a lifetime - they expire after a fixed
period of time (and the user must login again to use the system).
- It is impossible (or hard) to predict what the session key will
be ahead of time. This makes it tough to "crack" the system.
- New forms sent back by the "system" (in our example a pizza order
form) contain a hidden field with the session key as the value. This means
that all CGI requests other than a login request must include a valid
session key.
Generating session keys that meet the properties mentioned above is
not trival, although there are some well-known methods. Here is one
simple method:
- The system requires that each user can have at most one session
key at a time (this allows us to use the username as part of the session
key and makes it easy to make sure all active session keys are unique).
- A session key is created by concatenating two strings. The first
string is the username. The second string is a randomly generated sequence
of ten digits.
Here is some perl code that will generate a session key using the above
method - check any perl reference if you want more details on how to use
the rand function.
# perl subroutine to generate a session key
# given a username.
# Example usage:
# $key = generate_sessionkey($name);
sub generate_sessionkey {
local($name) = $_[0]; # the user name
# initialize the random number generator
srand();
# generate a sequence of 10 random digits
local($randstring,$i);
for ($i=0;$i<10;$i++) {
$randstring = $randstring . int(rand(10));
}
return $name . $randstring;
}
|
|
If we change our pizza server CGI to use a hidden field containing a
session key instead of a user name and password, the system is much
more secure. Now a pizza order form might look like this:
<FORM ACTION=/cgi-bin/orderpizza.cgi METHOD=POST>
<INPUT TYPE=HIDDEN NAME=sessionkey VALUE="Hungry Student9741189023">
Type of Pizza:
<INPUT TYPE=RADIO NAME=pizza VALUE=cheese> Cheese
<INPUT TYPE=RADIO NAME=pizza VALUE=veggie> Veggie
<INPUT TYPE=RADIO NAME=pizza VALUE=pepperoni> Pepperoni <P>
Pizza Size:
<INPUT TYPE=RADIO NAME=size VALUE=large> Large
<INPUT TYPE=RADIO NAME=size VALUE=medium> Medium
<INPUT TYPE=RADIO NAME=size VALUE=small> Small <BR>
<INPUT TYPE=SUBMIT>
</FORM>
|
|
|
|
And an order query string might look like this:
sessionkey=Hungry+Student9741189023&pizza=cheese&size=large
There is a sessionkey based pizza server running at:
cgi.cs.rpi.edu/~hollingd/eiw/CGI/pizzasession