Cookies from the Oven
November 20, 2000
There is a law, possibly part of the DMCA act of 1998, that any
mention of cookies must include a kitchen or culinary tie-in or pun.
We've done our duty here to stay within the bounds of law. In fact,
PYNTK 18 looked closely at a creating temporary cookie to authenticate
a user's login to our web site. We called this cookie site-auth
and it actually contained three pieces of concatenated data: the user's
identification number drawn from the back-end database, the user's
name also from the database, and a checksum value based on the first
two pieces of data. This checksum is an integrity check which we
can use to verify that the cookie has not been tampered with.
Otherwise, a user might manually edit his or her cookie to a
different user's identification, allowing the "potential for
mischief" as former Secretary of State James Baker is fond of
saying this week.
Therefore, we've coded an Embperl component read-cookie.html
whose purpose is to read the current authentication cookie, perform
an integrity check, and if valid proceed to read in the preferences
cookie that we had also created in PYNTK 18, site-prefs. As
a component, we can then include this script into any page that needs
to read in the user's cookies before taking any further steps.
read-cookie.html
[-
use CGI;
use Digest::HMAC_MD5;
#retrieve user's userid from wedt-auth token,
#verify checksum to prevent cookie tampering
my ($userid,$username,$checksum)=
split(/\t/,$cgiobj->cookie('wedt-auth'));
if ($userid) { #cookie present my $hmac = Digest::HMAC_MD5->new("digest#1pass");
$hmac->add($userid.$username);
if ($checksum ne $hmac->b64digest) {
#checksum integrity failed -- do not proceed!
$http_headers_out{'Location'}=
"http://".$cgiobj->server_name."/login-fail.html";
}
else { #checksum integrity passed -- proceed
%fdat=$cgiobj->cookie('site-prefs'); $fdat{userid}=$userid unless ($fdat{userid}); $fdat{username}=$username unless ($fdat{username});
} }
-]
Any page in our site that makes use of the user's personalization
cookies will execute the above component, read-cookie.html.
This component performs three functions:
- Reads the content of the wedt-auth cookie and splits it into
its three parts, identification, username, and checksum.
- Verifies checksum integrity based on the known password that was
used to calculate the checksum when the cookie was originally created.
- Should integrity check fail, user is re-directed to a page describing
the login as invalid. If the checksum is valid then the keys and values
stored in site-prefs -- the user's preference data -- is loaded
into the built-in Embperl hash %fdat.
In all likelihood, a call to execute read-cookie.html would be
the first line in any page that makes use of these cookies, e.g.
[- Execute ('read-cookie.html') -]
<HTML> ...rest of page code ...
By Invitation Only: Requiring Login
It's possible that pages in our web site can fall under three
categories, with respect to login authentication:
- No login needed at all, page does not use any personalization
features.
- Login optional, page will use personalization features if logged in,
otherwise page will treat user as an unknown guest.
- Login required, page should not display at all unless user has
authenticated.
Category one is self-explanatory and obviously doesn't require reading
this personalization trilogy. Category two is what we just built in the
previous section "Cookies from the Oven". There we saw how we
can include the cookie reading component into a page -- the component
will reject the user if the authentication cookie is present but fails
the integrity check. However the component will not fail if there is
no authentication cookie and this is intentional, because some pages
may fall under category two, where a cookie is not required.
Once read-cookie.html has been executed, any code later in the
page will know if the user has authenticated or not by the presence or
absence of $fdat{userid}. That user id was stored in
%fdat either from the data in site-prefs or, failing
that, the data from site-auth.
For pages under category three, though, we want to reject anyone who
has not been found with a valid authentication cookie. So, we code
another small component, require-login.html.
require-login.html
[$ if not $fdat{userid} $]
[- $http_headers_out{'Location'} =
"http://$ENV{SERVER_NAME}/login-fail.html;" -]
[$ endif $]
This component checks for the presence of $fdat{userid}. Of
course we must execute this component after the cookies have
been read and validated with read-cookie.html. Very simply,
if $fdat{userid} is empty then the user is redirected to
another page of your choosing.
We can now imagine how to begin a page which requires an authenticated
login before any of its content can be seen:
[- Execute ('read-cookie.html') -]
[- Execute ('require-login.html') -]
<HTML>
...rest of page code ...
A Short Course in Embperl
The Perl You Need to Know
User Sensitivity 101
|