Joey The Saint has asked for the wisdom of the Perl Monks concerning the following question:

Hey Everyone,

So I'm writing a simple CGI in perl and I wanted to use Apache::Session (specifically ::File, though ::DB or ::MySQL isn't out of the question if it comes to that). I have my login/authentication working and am feeding back a session cookie that contains a session ID that I got from Apache::Session. That's all good, I can store and retrieve stuff just fine (I think, so far everything I've tried has worked).

The problem comes when I try to follow my 'logout' link which, among other things, attempts to remove the session by using tied(%session)->delete (which I saw done somewhere here, I think). When I do that I get the following in my error_log file:

Insecure dependency in unlink while running with -T switch at /usr/lib +/perl5/site_perl/5.6.1/Apache/Session/Store/File.pm line 106., refere +r: <referring url>

Now I'm doing my best to make sure I'm not passing tainted values around, but this one has me completely stumped. My existing code is kind of a mess, but I've broken out the specific bits that can reproduce exactly the same symptoms. See below.

I'm not sure what other information to provide that may be helpful, but I'd really appreciate any advice anyone may have. Just to try and head off the first questions, yes the directorys are owned and rwx by the same uid/gid as the script. In fact my script is setuid for reasons that aren't really relevant, I don't think. The test script below will do the same without being setuid provided the directories (foo.com/cgi-bin/.sessionDir and .../.lockDir) are readable/writable/executable to the web server, which happens to be nobody:nogroup in my case.

Oh, and I tried using CGI::Session instead of Apache::Session, but it did awful things like giving me a session id and then creating a session file with a completely different id. I saw on their mailing list that someone else back in December had seen exactly the same thing and there were no replies to his problem, so I'm lead to believe that no solution awaits me down that road. :-)

Thanks. Any suggestions, no matter how off the wall, happily accepted.

-J.

#!/usr/bin/perl -T use strict; package sessionTest; use Apache::Session::File; use CGI::Safe qw/ taint :standard :html3 :html4 /; use CGI::Carp qw/ fatalsToBrowser /; my $query = CGI::Safe->new; my $sessionCookie; my %session; my $id; my $sessionID; my $scriptName = $query->self_url; $scriptName =~ s/\?.*//; $sessionCookie = $query->cookie(-name=>'sessionTest'); if (defined($sessionCookie)) { if ($query->url_param('keywords') =~ /logout/) { # in here $sessionCookie actually contains the value of the co +okie, # below it is the whole cookie. # # This could be done by reusing the $id value, but I think it' +s a bit # clearer here that we're reading a previous session id if we +do it # this way. $sessionCookie =~ /([a-z0-9]+)/; $sessionID = $1; tie %session, 'Apache::Session::File', $sessionID, { Directory => './.sessionDir', LockDirectory => './.lockDir' }; tied(%session)->delete; untie(%session); $sessionCookie = $query->cookie( -name=>'sessionTest', -value=>"", -expires=>'Thu, 31-Dec-1974 00:00:00 GMT' ); print $query->redirect(-uri=>"$scriptName",-cookie=>$sessionCo +okie); } else { # the browser has a session cookie, print out a page that will + let us # remove it. print $query->header(); print $query->start_html(-title=>"Welcome back"); print "Your session id is $sessionCookie."; print "<a href=\"$scriptName?logout\">click here</a> to log ou +t\n"; print $query->end_html(); } } else { # the browser doesn't have a session cookie, feed it one. tie %session, 'Apache::Session::File', $id, { Directory => './.sessionDir', LockDirectory => './.lockDir' }; $sessionCookie = $query->cookie( -name => 'sessionTest', -value => $session{_session_id}, -expires => 0 ); untie(%session); print $query->redirect(-uri=>"$scriptName",-cookie=>$sessionCookie +); }
  • Comment on Cleaning up sessions created by Apache::Session::File when logging out of a CGI application
  • Select or Download Code

Replies are listed 'Best First'.
Re: Cleaning up sessions created by Apache::Session::File when logging out of a CGI application
by cees (Curate) on Feb 09, 2003 at 02:50 UTC

    The problem is not with your code, it is that Apache::Session::File does not work in Taint mode.

    The problem is that the session ID is stored along with the session itself. When you go to delete the session, it first loads the session from disk and unserializes it. This overwrites the session ID you passed it with the one it found in the file (they are both identical so it shouldn't matter). The problem is, the session ID is now tainted.

    A quick fix for this is for you to untaint the session ID yourself after the session has been unserialized. Put the following two lines after you tie the session:

    $session{_session_id} =~ /^([a-zA-Z0-9]+)$/; $session{_session_id} = $1;

    This probably should be fixed in Apache::Session itself as I am sure other people will run into it.

    By the way, you really shouldn't be using Apache::Session::File anyway for performance reasons. At least use Apache::Session::DB_File which most likely doesn't suffer from this taint problem and will be much quicker.

      Thanks, that did the trick. Well, sort of. My session files are getting cleaned up alright and I am probably going to move to a DB_File interface anyway, but when I delete the session, my lock files aren't being cleaned up as well. I thought I could work around this by adding these lines to the top of my script:

      my $l = new Apache::Session::Lock::File; $l->clean('./.lockDir', 20);

      Just above the lines:

      my $scriptName = $query->self_url; $scriptName =~ s/\?.*//;

      The problem is, my page where I do the logout modifies the lock, so no matter what it hasn't timed out when we get to the logout processing code, but when I come back into the script later, there are lock files that are hours old and they don't seem to get cleaned up either. I've read the documentation at http://search.cpan.org/author/JBAKER/Apache-Session-1.54/Session/Lock/File.pm and http://search.cpan.org/author/JBAKER/Apache-Session-1.54/Session/File.pm but I guess I still don't understand how the locking mechanism is supposed to work because I'm obviously using it incorrectly. Should I have an external script, maybe a cron job, that cleans up stale lock files? Figuring out which locks belong to sessions that no longer exist in the session directory wouldn't be hard, but I'd really rather understand the proper usage.

      Thanks again.

      -J.

        Well, if you look carefully at the code for the clean method, you will see that it has a bug as well... The culprit is on line 136 of Apache::Session::Lock::File:

        if ((stat($dir.'/'.$file))[8] >= $time) - $now {

        This says: take the last access time of the lock file, subtract the current time and see if it is bigger than the proposed timelimit ($time). Since the current time will always be bigger than the last accees time, this number will always be negative. So unless you provided a negative timelimit to the clean function, the condition for deletion will never be met. Reversing the subtraction should fix the problem.

        if ($now - (stat($dir.'/'.$file))[8] >= $time) {

        Or you could pass a negative time limit to the clean method, but that could break if this bug ever gets fixed and you upgrade.

        Now, again I will try to convince you to scrap the File based sessions and move to at least the DB_File based one.