Session Cookie Glitch with mod_perl 2.03 and Apache 2.2.6



I have an application originally written for the combination of Apache
1.3.31, mod_perl 1.39, and MySQL 4.0.16 that I'm moving to new servers
so I wanted to take the opportunity to migrate these components to the
latest versions:

Perl v5.8.8 built for i586-linux-thread-multi
Apache 2.2.6
mod_perl 2.0.3
OpenSSL 0.9.8g
libapreq 2-2.08
MySQL 5.0.45

The APIs for mod_perl changed substantially between 1.0 and 2.0 so
I've gone through the code that involves the old 1.0 vintage
Apache::xxxxxx modules and updated them to 2.0 equivalents in the
Apache2::xxxxxx or ModPerl::xxxxxxx modules.

The revamped application seems to work properly from the following
client combinations:

* consumer Windows XP machine with IE 7.0
* consumer Windows XP machine with Firefox

However, my corporate laptop (typical corporate locked down image)
with Windows XP and IE 6.0.2900 cannot establish a session within the

In my application, the only changes required by mod_perl 2.0 were all
localized to the modules that handled authentication and session
management which were contained in modules named MyApp::CookieAuth and
CookieLib. Some of the key "diffs" in the updated code are summarized


< use Apache::Constants qw(DECLINED OK REDIRECT FORBIDDEN);
< use Apache::Cookie;
use Apache2::Cookie;
use Apache2::RequestRec;
use Apache2::Connection;


my %cookies = Apache2::Cookie->fetch();


< $r->connection->auth_type('Basic');
< $r->connection->user($session_key{'username'});
< $r->header_out(
< "Set-Cookie" => &CookieLib::generate(\%session_key)
< );
"Set-Cookie" => &CookieLib::generate(\%session_key)

The generate() method in the CookieLib library that actually sets the
session key data in responses appears as follows:

sub generate {
my ($args) = @_;
my $query = new CGI;
# this call to Crypt::CBC with only two parameters requires
# the older 2.08 version instead of 2.18 -- the new version
# requires a salt value which raises other compatibility issues
my $cipher = new Crypt::CBC($KEYTEXT, $CIPHER);
my $plaintext = join(':',
$args->{'username'}, $ENV{'REMOTE_ADDR'},
($args->{'timestamp'} eq 0) ? 0 : time,
$LIFETIME, rand(), $args->{'password'},
print STDERR "CookieLib generate - $plaintext\n";
my $ciphertext = encode_base64($cipher->encrypt($plaintext), "");
return $query->cookie(
-name => $COOKIE_NAME,
-domain => $COOKIE_DOMAIN,
-path => '/',
-value => $ciphertext

The normal login flow is:

1) surf to the / page of the server root,
2) Apache sees no session in the request so it serves a template with
an empty login form
3) submit a POST with a valid login, pull the login & password from
the POST request and authenticate it
4) if successful, set a cookie in the HTTP response header and send a
template for the logged in main page which is a frame that loads /
top.cgi (for navigation links) and /bottom.html (a static HTML file)
5) when the browser parses the logged in main page, it performs the
GETs for top.cgi and bottom.html giving the user their navigation
commands and a welcome page.

For logins from the corporate laptop, steps 1-4 all happen based upon
print debug statements writing to STDERR. However, when Apache
processes the GET requests for top.cgi and bottom.html, the mod_perl
authentication handler isn't detecting the session data in the headers
(as if they weren't set by Apache when sending the reply in step #4)
so the logic in the authentication handler re-serves the default index
page (the login form) for each of those frame elements.

Logins from the other browsers work correctly and the print statements
for debugging show session data being retrieved from the subsequent
GET requests. That tells me I don't have PERL5LIB envrionment
problems finding the modules, etc.

I suspect the problem is due to some interaction between the Apache
layer <Directory>, <FilesMatch> and <Location> directives (see below)
and the authentication logic used in my application's module named
Apache::CookieAuth. However, I can't figure out why the problem is
browser dependent to figure out exactly where to fix the code.

Any ideas / suggestions would be greatly appreciated.



# These <Directory> directives set explicit rules for actual UNIX
# file paths referenced by Apache to serve incoming requests.
# are subjected to <Directory> rules, then <Files> and <FilesMatch>
# directives, then <Location> filters.

<Directory "/myapp/htdocs">
AddHandler cgi-script pl
Options +Indexes +ExecCGI +FollowSymLinks +Includes +MultiViews
AllowOverride None
Order allow,deny
Allow from all

<Directory "/myapp/templates">
Order allow,deny
Allow from all
AllowOverride none

# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
<IfModule dir_module>
DirectoryIndex index.html index.cgi

# Define pattern match to steer references to CGI scripts to the the
# registry within mod_perl while serving static content the old
# fashioned way
<FilesMatch "\.cgi">
SetHandler perl-script
PerlSetVar Filter On
PerlResponseHandler ModPerl::Registry
PerlSendHeader off
Options +ExecCGI

# Use <Location> to capture all incoming URI references and ensure
# they only get served if user has valid session -- session is managed
# by the Apache::CookieAuth module in the source tree located at
# /myapp/lib/Apache/
<Location />
PerlAuthenHandler Apache::CookieAuth
Options +ExecCGI
AuthName "MYAPP"
AuthType Basic
Require valid-user




I've managed to solve the mystery so I am posting my findings to help
save others some frustration. The core problem involved the code
retrieving cookie data from the HTTP headers. Many of the examples
for using mod_perl 2.x use the following syntax for retrieving cookies
inside the module used for authentication:

my $cookiejar = Apache2::Cookie::Jar->new($r); # where $r is the
Apache2::RequestRec object
my $appcookie = $cookiejar->cookies('MYAPPCOOKIENAME');

Unfortunately, the Jar->new() call in the underlying libapreq module
was abandoned in libapreq 2.05 and higher versions. Changes in that
release now rely upon methods in the APR::Request module so the
following steps are required:

1) create a new APR::Request object from the incoming
Apache2::RequestRec passed to the handler
2) test if the APR::Request object successfully parsed the headers of
the incoming HTTP request
3) retrieve a APR::Request::Cookie::Table object via the jar() method
4) retrieve your desired cookie via a get() call in the Table object

The code looks like this:

my $aprreq = APR::Request::Apache2->handle($r);
if ($aprreq->jar_status()) {
# if jar_status is non-zero, headers could not be parsed, nothing
to do here
# so just return OK and let the Apache engine handle the request
return $Apache2::Const::OK;
my $cookiejar = $aprreq->jar;
my $appcookie = '';
$appcookie = $cookiejar->get('MYAPPCOOKIENAME');

This code would go in the handler() method of the PERL module you
reference in the PerlAuthenHandler configuration of the Apache server

I still have no idea why the old code with the references to broken
calls in libapreq worked on ANY browser clients but the code is now
working for my intended client audience.


Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question