superzero10 superzero10 - 2 months ago 41
Perl Question

Perl Web Login Script With CGI::Session

i'm on the same problem since almost two week ago.
i'm a newbie with Perl and Web :/

i followed the CGI::Session tutorial and Cookbook, the code seems to be good but... not working.


index.cgi


#!/usr/bin/perl
use CGI;
use CGI::Cookie;
use HTML::Template;
use strict;
use warnings;
use CGI::Session;
use CGI::Carp qw(fatalsToBrowser);
require "cgi-bin/web_base.pl";
require "cgi-bin/login.pl";

my $cgi = new CGI;
my $session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'}) or die CGI::Session->errstr;
my $CGISESSID = $session->id();
print header();
print $cgi->header();
print my_topbar();
login_attempt($session, $cgi);

if ( $session->param("~login-trials") >= 3 ) {
print error("You failed 3 times in a row.\n" .
"Your session is blocked. Please contact us with ".
"the details of your action");
exit(0);

}

unless ( $session->param("~logged-in") ) {
print login_form($cgi, $session);
exit(0);

}
print footer();



login.cgi


#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use CGI::Cookie;
use HTML::Template;
use CGI::Session;
use CGI::Carp qw(fatalsToBrowser);
use Fcntl;

my $cgi = new CGI;
my $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});
sub login_attempt {
my ($session, $cgi) = @_;

if ( $session->param("~logged-in") ) {
return 1; # Verify if user is not logged.
}

my $username = $cgi->param("username") or return;
my $password=$cgi->param("password") or return;

# Form submited. Try to load profile.

if ( my $profile = load_profile($username, $password) ) {
$session->param("~profile", $profile);
$session->param("~logged-in", 1);
print "YOUPIIIII";
$session->clear(["~login-trials"]);
$session->redirect("dashboard.cgi");
return 1;
}

# Failed to login, wrong credentials.

my $trials = $session->param("~login-trials") || 0;
return $session->param("~login-trials", ++$trials);
}
return 1;

sub load_profile {
my ($username, $password) = @_;

local $/ = "\n";
unless (sysopen(PROFILE, "profile.txt", O_RDONLY) ) {
die ("Couldn't open profile.txt: $!");
}
while ( <PROFILE> ) {
/^(\n|#)/ and next;
chomp;
my ($n, $p) = split "\s+";
if ( ($n eq $username) && ($p eq $password) ) {
my $p_mask = "x" . length($p);
return {username=>$n, password=>$p_mask};

}
}
close(PROFILE);
return undef;
}


profile.txt

Formget 123


When i try to login, nothing happen, even when i try wrong crendentials it should block me after 3 attemps but it is not.

Can someone really help me on this ? i can't take it anymooooore.

feel free for any questions :)

EDIT :

-login_attempt() corrected

-load-profile wasn't working, made a new one, but still need improvement.

-Last Problem is session init

Answer

Are you sure that you don't get any errors? Have you checked the web server error log?

You call login_attempt() with two parameters ($session and $cgi) but in login.cgi, that subroutine is defined like this:

sub login_attempt() {
  ...
}

You're (probably accidentally) using a prototype on that subroutine, telling Perl that it takes no parameters. So I'd be surprised if you don't get an error saying:

Too many arguments for main::login_attempt

Remove the parentheses from that definition.

sub login_attempt {
  ...
}

Update: I think you're missing one very important step here. From the CGI::Session Tutorial:

There is one small, but very important thing your application needs to perform after creating CGI::Session object as above. It needs to drop Session ID as an HTTP cookie into the user's computer. CGI::Session will use this cookie to identify the user at his/her next request and will be able to load his/her previously stored session data.

To make sure CGI::Session will be able to read your cookie at next request you need to consult its name() method for cookie's suggested name:

$cookie = $query->cookie( -name   => $session->name,
                          -value  => $session->id );
print $query->header( -cookie=>$cookie );

name() returns CGISESSID by default. If you prefer a different cookie name, you can change it as easily too, but you have to do it before CGI::Session object is created:

CGI::Session->name("SID");
$session = CGI::Session->new();

Baking the cookie wasn't too difficult, was it? But there is an even easier way to send a cookie using CGI::Session:

print $session->header();

The above will create the cookie using CGI::Cookie and will return proper http headers using CGI.pm's CGI method. Any arguments to CGI::Session will be passed to CGI::header().

Without this, you'll be creating a brand new session for each request.