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

For the life of me I cannot figure out why the below snippet fails. After the user submits the form it checks their username and password and checks in the in the database. But even with the correct login (username is "admin" password is "pass", it says the login failed.

The creation of that table is

my $sth = $dbh->prepare( "CREATE TABLE IF NOT EXISTS admin ( id int auto_increment not null, username VARCHAR(25) DEFAULT 'admin' NOT NULL, password VARCHAR(36) DEFAULT '1a1dc91c907325c69271ddf0c944bc72' + NOT NULL, primary key (id) )"); $sth->execute(); if ($sth->errstr) { print $sth->errstr; } else { print "Admin table was setup"; }
The login script itself is below
#!/usr/bin/perl use warnings; use strict; use CGI::Carp 'fatalsToBrowser'; use CGI qw/:standard/; use Digest::MD5 qw(md5_hex); use CGI::Cookie; use DBI; #################### # Configuration section #################### my $dbase = "spyders_test"; my $mysql_user = "spyders_admin"; my $mysql_pass = "pass"; #################### # Do NOT edit below this line #################### ###### # Connecting to our database ###### my $dbh = DBI->connect("DBI:mysql:$dbase", $mysql_user, $mysql_pass) o +r print DBI=>"errstr"; ####### # Check to see if they have a login cookie ####### my %cookies = fetch CGI::Cookie; if (defined($cookies{'user_id'})) { print header, start_html("Login"); print "You are already logged in. Please wait while you're redirec +ted..."; print "<script>window.location = 'index.cgi';</script>\n"; exit; } ####### # The form was submitted, let's process it ####### if (param()) { my $username = param("username"); my $password = param("password"); $password = md5_hex($password); my $data = qq(SELECT * FROM admin WHERE username = "$username" AND p +assword = "$password"); my $sth = $dbh->prepare($data); $sth->execute() or die $dbh->errstr; if ($sth->rows < 1) { print header, start_html("Authentication Failed"); print "User was $username: password was $password<p>"; print "<b>Your username or password were incorrect.</b> <p>Please + click Back to continue</p>"; exit; } else { while ($data = $sth->fetchrow_hashref) { my $auth_user = new CGI::Cookie(-name => 'user_id', -value + => $username); my $auth_pass = new CGI::Cookie(-name => 'user_pass', -value + => $password); print "Set-Cookie: $auth_user\n"; print "Set-Cookie: $auth_pass\n"; print header, start_html("Login"); print "Welcome " . $username . ", you have successfully logged in +.\n"; print "<script>window.location = 'admin.pl';</script>\n"; } } }
Anyone have any ideas why it may be failing? I know the form fields are ligned up because doing a test print I get
User was admin: password was 1a1dc91c907325c69271ddf0c944bc72 Your username or password were incorrect. Please click Back to continue
Thank you

Replies are listed 'Best First'.
Re: faulty small cgi/mysql login
by Animator (Hermit) on Feb 12, 2005 at 17:52 UTC

    You've shown the setup of the table, you have shown the select, but do you have an insert?

    Also, you should read about Placeholders in the DBI docs, then 'prepare' will make much more sense (and it will be lots safer).

      There is no insert. There's just one username and password in the admin table and that's setup via the DEFAULT statement.

      .... at least I thought that's how it worked.

        Nope,

        The default values are used whenever a value for that particular thingie isn't filled in.

        If there is no data in the table, then the select will always fail.

Re: faulty small cgi/mysql login
by CountZero (Bishop) on Feb 12, 2005 at 19:58 UTC
    Especially with user-supplied data you should not interpolate this data in the SQL but rather use placeholders:
    my $data = qq(SELECT * FROM admin WHERE username = ? AND password = ? +); my $sth = $dbh->prepare($data); $sth->execute($username, $password) or die $dbh->errstr;

    Also the use of the rows-method is not recommended in this case and may lead to subtle and difficult to trace errors. The DBI-docs say:

    Generally, you can only rely on a row count after a non-SELECT execute (for some specific operations like UPDATE and DELETE), or after fetching all the rows of a SELECT statement. For SELECT statements, it is generally not possible to know how many rows will be returned except by fetching them all. Some drivers will return the number of rows the application has fetched so far, but others may return -1 until all rows have been fetched. So use of the rows method or $DBI::rows with SELECT statements is not recommended. One alternative method to get a row count for a SELECT is to execute a ``SELECT COUNT(*) FROM ...'' SQL statement with the same ``...'' as your query and then fetch the row count from that.

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law