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

I'm learning perl, and I'm currently trying to write a user login system. I have the following code in login.pl:
# !perl use CGI qw(:standard); # use the CGI libraries print header; # start the html output $username = param('username'); # take <input name=username> $password = param('password'); # take <input name=password> + open(FILE, "./users/$username"); # open the user's file in $use +rs $passhash = <FILE>; # read the password hash from the f +ile close(FILE); # close the file if (crypt($password, $passhash) eq $passhash) { print "You are now logged in."; } else { print "Incorrect username or password, please try again."; }
$passhash was written to a file with the user's name earlier on (in newuser.pl):
$salt = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z') [rand 64, rand 6 +4]; # create random two-character salt $passhash = crypt($password, $salt); # hash the password with t +he salt
My question is: how can I set it so that the user can stay logged in between pages (eg. use a cookie) without storing the password somewhere in plaintext to check $passhash against? Sorry if I've gone about this whole thing completely the wrong way.

Replies are listed 'Best First'.
Re: setting a cookie on login
by atcroft (Abbot) on May 21, 2004 at 18:01 UTC

    A few thoughts/suggestions. First, use a single password file outside the web-accessible tree. This prevents someone from being able to read it, and also prevents someone from doing something to the username variable and getting into portions of the tree they should not be in (such as was mentioned in another recent posting). Secondly, the salt in a password is stored as part of the password, so you will need to retrieve the same salt for encoding the current entry to ensure it matches.

    As to your question about staying logged in, I am guessing here, but instead of printing "You are now logged in", send them to a page that sets a cookie with a reasonable timeout (say, 10min), that as soon as the cookie is set directs them to another page. Then, each time they hit a page, check to see if the cookie is set, directing them back to the login screen if it is not set or has expired, or updating the expire time if it is still valid (so it will be the reasonable timeout period you set from that time).

    Just a few thoughts. Hope they help.

Re: setting a cookie on login
by Joost (Canon) on May 21, 2004 at 21:58 UTC
    Make sure the script starts with
    #!/full/path/to/perl -wT use strict; use CGI qw(some imports); #... rest of program
    then...

    You defenitively do not want to store the user name in any form in the cookie. I'd use CGI::Session. General operation looks like this:

    use CGI::Session; my $query = CGI->new; my $session = CGI::Session->new(driver:File", $query, {Directory=>'/tm +p'}); # check if user logged in... if (validate($query->param('user'),$query->param('user'))) { $session->param('user') = $query->param('user'); } my $user_name = $session->param('user'); #get user name from the sessi +on (undef unless logged in). unless ($user_name) { # user is not logged in } else { # user is $user_name } # before you print out any content do print $session->header(); # like $query->header() but with session coo +kie
    Oh yeah:

    Don't forget to clean out '/tmp' once in a while, as this code uses files to store the sessions.

    Use the FreezeThaw serializer for CGI::Session if you want to store objects in the session, the default serializer doesn't work for objects.

    more info in the CGI::Session documentation

Re: setting a cookie on login
by waswas-fng (Curate) on May 21, 2004 at 18:25 UTC
    Try username = "../../../../../../../../dev/random" =)


    -Waswas

      Agreed. Worse still, the username can end with a "|". This shows that tainting mode is useful.

Re: setting a cookie on login
by Joost (Canon) on May 21, 2004 at 22:05 UTC
    Oh, I see I forgot to answer your question:
    My question is: how can I set it so that the user can stay logged in between pages (eg. use a cookie) without storing the password somewhere in plaintext to check $passhash against? Sorry if I've gone about this whole thing completely the wrong way.
    You don't need to store the password in the plain:
    if ($passhash eq crypt($incoming_password,$passhash)) { # $incoming_password is correct }
    You only need to store the encrypted passwords.
    I must be going blind, you're already doing this.