Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Rejected Cookies and Session IDs

by Wally Hartshorn (Hermit)
on Feb 17, 2005 at 23:26 UTC ( [id://432138]=perlquestion: print w/replies, xml ) Need Help??

Wally Hartshorn has asked for the wisdom of the Perl Monks concerning the following question:

We're using a cookie to hold a session ID. However, we need to be able to hold the session ID even if the user rejects our cookie. Presumably the way to do this is to store the session ID in the URL.

We've figured out how to do this, but in order to ensure that we don't attempt to set a cookie again (after it was rejected), we also have to alter the URL on our first attempt, when we're trying to set the cookie. The result is that, if the cookie IS accepted, we're left with an ugly "cookiecheck" flag in our URL. To remove that (just 'cuz it's ugly), we do yet another redirect.

Here's our code.

package DP::Session; use base CGI::Application; $DP::Session::VERSION = '$Id: Session.pm,v 1.5 2005/02/17 22:51:49 wal +lyh Exp $'; use strict; use warnings; =head1 NAME DP::Session - CGI session management web module =head1 SYNOPSIS use base DP::Session; =head1 DESCRIPTION C<DP::Session> provides standard handling for session management in ou +r CGI programs. A program that uses a module based on DP::Session will automatically attempt to retrieve or create a cookie that contains the ID of the current session. If the cookie cannot be created, it will store the ID of the session in the URL. =cut use CGI::Session; use DBI; use CGI::Carp; =head1 METHODS =cut #--------------------------------------------------------------------- +- =head2 cgiapp_init C<cgiapp_init> will connect to the session database, initialize the session, and store the session ID in a cookie or in the URL. =cut sub cgiapp_init { my ($self) = @_; my $cgi = $self->query; my $dbh = DBI->connect("DBI:mysql:dp-session;host=localhost", "xxxx", "xxxx", { RaiseError => 1 } ) or confess "Can't open session database: $DBI::errstr\n"; $self->param(dbh => $dbh); $self->save_session_id($self->initialize_session); return; } #--------------------------------------------------------------------- +- =head2 initialize_session C<initialize_session> will try to retrieve the session ID from the cookie or from the URL. Otherwise it will create a new session. =cut sub initialize_session { my ($self) = @_; my $cgi = $self->query; my $dbh = $self->param("dbh"); # If the user rejects our cookie, # we pass the session ID in the URL. my $sid = $cgi->cookie("CGISESSID"); $sid = $cgi->url_param("CGISESSID") unless ($sid and $sid ne ""); $sid = undef unless ($sid and $sid ne ""); my $session = CGI::Session->new( "driver:MySQL", $sid, {Handle => $dbh, LockHandle => $dbh} ) or confess "Can't create session: $CGI::Session::errstr\n"; $session->expire("+1h"); $self->param(session => $session); return $sid; } #--------------------------------------------------------------------- +- =head2 save_session_id C<save_session_id> will ensure that the session ID is saved, either in a cookie or in the URL. =cut sub save_session_id { my ($self, $sid) = @_; my $cgi = $self->query; my $session = $self->param("session"); if ($sid and $sid eq $session->id) { if ($cgi->url_param("_cookiecheck")) { $self->remove_flag_from_url; $self->exit_early; } else { return; } } elsif ($cgi->url_param("_cookiecheck")) { $self->save_session_id_in_url; $self->exit_early; } else { $self->save_session_id_in_cookie; $self->exit_early; } } #--------------------------------------------------------------------- +- =head2 remove_flag_from_url C<remove_flag_from_url> will clean the URL to remove the parameter tha +t we used to ensure that we did not attempt to set a cookie multiple times. We will only do this if the cookie was accepted. =cut sub remove_flag_from_url { my ($self) = @_; my $cgi = $self->query; $cgi->delete("_cookiecheck"); $self->send_redirect_headers; } #--------------------------------------------------------------------- +- =head2 save_session_id_in_url C<save_session_id_in_url> will alter the URL to add a parameter that contains the session ID, so that we can retrieve the session later. We will only do this if the cookie that we tried to send was rejected. =cut sub save_session_id_in_url { my ($self) = @_; my $cgi = $self->query; my $session = $self->param("session"); $cgi->delete("_cookiecheck"); $cgi->param(CGISESSID => $session->id); $self->send_redirect_headers; } #--------------------------------------------------------------------- +- =head2 save_session_id_in_cookie C<save_session_id_in_cookie> will attempt to send a cookie containing the session ID, so that we can retrieve the session later. =cut sub save_session_id_in_cookie { my ($self) = @_; my $cgi = $self->query; my $session = $self->param("session"); my $cookie = $cgi->cookie ( -name => "CGISESSID", -value => $session->id, -path => $cgi->url(-absolute => 1), ); # If the cookie is rejected, we won't try again to send a cookie. $cgi->param(_cookiecheck => 1); $self->header_props(-cookie => $cookie); $self->send_redirect_headers; } #--------------------------------------------------------------------- +- =head2 send_redirect_headers C<send_redirect_headers> will send the headers needed to perform the redirect that will either send a cookie or alter the URL to contain the session ID. =cut sub send_redirect_headers { my ($self) = @_; my $cgi = $self->query; my $session = $self->param("session"); $self->header_type("redirect"); $self->header_add(-uri => $cgi->self_url); $self->teardown; print $self->_send_headers; } #--------------------------------------------------------------------- +- =head2 exit_early C<exit_early> will exit the program. This is needed so that the redirect headers can be sent. However, this routine can also be overridden by the unit test program, so that the tests don't exit early. =cut sub exit_early { exit; } 1;

Is there a better way to do this? Or better yet, are we busily reinventing the wheel while overlooking some nifty CPAN module?

Wally Hartshorn

Replies are listed 'Best First'.
Re: Rejected Cookies and Session IDs
by Arunbear (Prior) on Feb 18, 2005 at 11:04 UTC
    CGI::Application::Plugin::Session nicely integrates CGI::Application and CGI::Session so take a look at it.

    To handle the possibility of some browsers not accepting cookies, it would be much simpler to just send the session cookies AND put the session ids into your urls (Amazon uses this approach).

    I do not know what I may appear to the world; but to myself I seem to have been only like a boy playing on the seashore, and diverting myself in now and then finding a smoother pebble or a prettier shell than ordinary, whilst the great ocean of truth lay all undiscovered before me.
    -- Isaac Newton
      Arunbear,
      Some people may be concerned about using CGI::Application::Plugin::Session because by self admission, it is alpha software. I recently had an opportunity to have an email exchange with the author about this point. You see, CGI::Session doesn't play well with CGI::Simple but the current work around has already been integrated into CGI::Application::Plugin::Session - what's a hacker to do? I was informed that the alpha label was no longer applicable and that it would be removed from the next release and I shouldn't have any worries about using it in production code now.

      Cheers - L~R

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://432138]
Approved by Limbic~Region
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-24 18:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found