Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks, I'm going to attempt to write a message board application where users can log in, write a message, log off. There'd also be a bit of user management stuff...the usual stuff, avatars, banning,etc, eventually
The easy way out is to download any one of the hundred php ones, but that won't teach me Perl, nor allow me to modify it later.
So far I've seen two modules that appear to be the contenders in Perl for this, Apache::Session and CGI::Session.
The big question is, which is the best?
What are the pros and cons of each?

Replies are listed 'Best First'.
Re: session management
by smiffy (Pilgrim) on Sep 04, 2008 at 05:34 UTC

    I won't answer your question as such, but will give you an alternative instead.

    If part of your aim is a learning exercise, why not handle the sessions yourself? In the web applications that I write, I am generally only interested in keeping track of people who are logged (or logging) into a particular system. To do this, I set some cookies:

    • Either the user ID or a hash of the user ID
    • A salted hash of the user password
    • A security hash which I won't describe exactly (otherwise there goes my security through obscurity,) but keeps track of whether the login is still valid, from the same IP address and (possibly) the same user agent.

    Only the first of these is needed to maintain state - everything hangs off the user ID. So, when the user logs in, these cookies get set. Every time a page is loaded, one of the first things to happen is to read in the cookies from the HTTP request; once we've got the user ID cookie, we can then look up any state information that we might have saved in the database. (We could also save that state information directly in cookies; I tend not to do this because I don't want people altering cookies to fool my system.)

    Here's a slightly modified and cut-down version of how I read in cookies (%cookies is declared elsewhere):

    sub pop_cookies { my $rawcookies=$ENV{HTTP_COOKIE}; $rawcookies=~s/Cookie:|\s//ig; for my $nvp (split(/;/,$page{rawcookies})) { my ($n,$v)=split(/=/,$nvp); { $cookies{$n}=$v; } } }

    I really think that if you build this yourself, you will understand how and why state preservation can/does work - and you will code a better application as a result. Modules are great but if, when you build your house, you also learn how to make bricks - you will gain that much more insight and experience.

    Hope this helps!

      I'm no security expert, but wouldn't it be smarter to create a session ID and store that in the cookie instead. Otherwise, I could just create a cookie wherever I am with the user ID I would like to log in as with hopes that they have already logged in somewhere else.

      What I usually like to do is create the session ID, and at each page request, verify that the IP address hasn't changed. That's about as far as I go for security, but since they are usually just quick throw-away/personal apps, it hasn't been an issue.

      Here's some simple code for creating a session ID:
      my @chars = ('a'..'z', 'A'..'Z', 0..9); my $id_len = 20; my $session_id = join '', @chars[map { rand scalar @chars } 1..$id_len + ];

        I probably didn't make my self clear enough (as usual) ;-)

        Whilst I advocate hanging session data of the user ID (for systems with a login), other cookies store data to ensure that the user is who they claim to be, that a valid password has been provided and that other parameters, including the client IP address, have not been changed.

        To prevent a villain from hand-crafting cookies that will get them a login, the state cookies (which are doubling as security cookies) can be cryptographic hashes of the data plus 'salt' values.

        For instance, if my password is 'foobar', setting a cookie of the md5 sum of that value will give me:

        3858f62230ac3c915f300c664312c63f

        If we know the password, it's easy to recreate that cookie. However, if we hash more than just the value of the password, we can make it MUCH harder to create a valid cookie. Here is a slightly over-the-top example*:

        use Digest::MD5 qw( md5_hex ); my $userid='me@example.com'; my $salt1='q398w4hfua9o8has9fp8h'; my $passhash=md5_hex('foobar' . $salt1); my $salt2='q2034rhawpifhasodfha0s0f'; my $ipaddr=$ENV{'REMOTE_ADDRESS'}; my $sechash=md5_hex($userid . $passhash . $salt2 . $ipaddr);

        We then set cookies of $userid, $passhash, $sechash. When we go to the next page, we will validate the cookies presented in the HTTP request by recalculating $passhash and $sechash and comparing them with the cookies.

        I use a stored procedure to which I feed the values of $userid and $passhash. The procedure returns either true or false depending on whether the re-created hashes match. Note that this method means that neither the password nor a simple hash of the password are ever returned from the database.

        After doing this - validating that the password is correct for the user - we compare a calculated value of $sechash against the one in the cookie.

        To cut a long story short, if the values of $salt1 and $salt2 are not known (they only exist in the application,) it is highly unlikely that anyone would be able to hand-craft cookies that would allow them to be logged in. We can take this further and use dynamic salt values (possibly generated as per lostjimmy's example) which may be stored in the database against the user ID.

        Summary: preserving state in a logged in situation may be achieved safely using the user ID as the key, provided that appropriate security mechanisms - such as salted hashes - are used. In a non-logged in system where security is not an issue, lostjimmy's example is more than adequate.

        * - over-the-top in that it uses 2 different salt values.

      If you don't know the recipe you'll end up with sub-standard bricks, and your house will collapse in on you.

        Thank you for extending my metaphor ;-)

        Which is why it is important to fully understand why we are doing things rather than just how - and why I tend to advocate working from scratch in the first instance. You will then a) understand better what various modules are doing and b) appreciate all the hard work that has gone into writing them!

      Whilst you're no doubt right, from my reading and understanding, that can be frought with possible vunerabilities. I'd prefer to use a module for the low level stuff thats proven (thats why people go to the trouble of writing them).
      The question is, does it matter which one? I'm leaning towards CGI::Session, but I'm not sure.

        I can't give you a comparison between the Apache:: and CGI:: modules because I use neither of them but bear this in mind: anything that starts with Apache:: has probably been written with mod_perl in mind, whilst anything beginning with CGI:: has probably been written as an adjunct to the CGI that comes with the Perl distribution.

        I had a quick look at the documentation of the Apache:: version - it says that it should work with anything (even without Apache), but my inclination would be to work with the CGI:: version if you are going to use the standard CGI module in your application. Obviously, if you are going to be working with mod_perl, you should take a look at the Apache:: version.

Re: session management
by perrin (Chancellor) on Sep 04, 2008 at 16:56 UTC
    Use CGI::Session. It's better maintained at this point.
Re: session management
by stonecolddevin (Parson) on Sep 05, 2008 at 03:51 UTC

    Purely out of curiosity, what made you decide to start with developing session management for your message board?

    I wouldn't recommend rolling your own session management system, CGI::Session does a mighty fine job of this for you with a wealth of storage back ends at its disposal. It also integrates nicely with frameworks such as CGI::Application and Catalyst, either of which I would highly recommend looking into for this project.

    meh.