PDA

View Full Version : grabbing user params from htaccess?


bazz
08-18-2006, 03:57 PM
Maybe I'm not getting this at all but, I want to do this: -

Login using htaccess, htgroup and htpasswd files; when people log in, they will be shown a specific perl-generated web page.

I want the perl code to identify who logged in s that it will know whose details to supply to the page.

How would I best do this, please?


bazz

FishMonger
08-18-2006, 11:11 PM
It would be better to use a login page that authenticates to a db and once autenticated, create a session ID and load the proper data.

KevinADC
08-18-2006, 11:40 PM
look here too:


http://search.cpan.org/~gozer/mod_perl-1.29/Apache/Apache.pm

FishMonger
08-19-2006, 12:18 AM
Here's my login script that I use for our IMAP account administration. You should be able to adapt it to your needs. It stores/queries the user login info in a mysql database.

#!/usr/bin/perl

use strict;
use CGI qw(:all);
use CGI::Carp qw(fatalsToBrowser);
use CGI::Session;
use Crypt::PasswdMD5;
use DBI;
use globals; # To-Do -> change this to a common.pl script instead of a module

my $cgi = new CGI;
my $self = $cgi->url;
my %login = $cgi->Vars;
my $session = new CGI::Session($cgi) or die CGI::Session->errstr;

if ( $login{'logout'} || ! $session->param('logged_in') ) {
$session->clear;
$session->delete();
}

my ($authenticated, $roll, $name) = authenticate_user() if $cgi->param('Login');

($authenticated || $session->param('logged_in') ) ? admin_page() : login();


#####################################################################

sub login {
print $cgi->header,
$cgi->start_html(-title=>'Email Administration Login',
-style=>{-src=>'/emadmin.css'}
);

print "<img src='/logo.gif' />\n",
$cgi->h1("Email Administration").$/,
$cgi->start_form(-name=>'login'),
'<p>Username: ', $cgi->textfield('username'), "<br>\n",
'Password: ', $cgi->password_field('password', ''), "</p>\n",
'<p>', $cgi->submit('Login', 'Login'), "</p>\n",
$cgi->end_form;

print $login{'failed'} if defined $login{'failed'};
print $cgi->end_html;
}


sub authenticate_user {
if ( defined $login{'username'} && defined $login{'password'} ) {
my ($encrypted_pass, $roll, $name) = queryDB($login{'username'});
if ( $encrypted_pass ) {
my $salt = substr($encrypted_pass, 3,8);
my $password = unix_md5_crypt( $login{'password'}, $salt );
if ( $password eq $encrypted_pass ) {
$session->param('logged_in', 1);
$session->param('admin', $name);
return (1, $roll, $name);
}
}
}
$login{'failed'} = 'Invalid username, or password...Please try again';
return 0;
}


sub admin_page {

my $search = 'search.pl';
my $add = 'add.pl';
my $delete = 'delete.pl';
my $modify = "modify.pl?admin=$login{'username'}"; # change this to use a session param
my $chgIMAPpass = 'chgIMAPpass.pl';

print $session->header(),
$cgi->start_html( -title=>"Email Administration",
-style=>{-src=>'/emadmin.css'}
);
$session->param('user', 'username');

print "<img src='/logo.gif' />\n",
$cgi->h1("Email Administration").$/,
$cgi->start_form(-class=>'logout', -name=>'logout'),
$cgi->h3($name),
$cgi->submit('Logout'),$cgi->p,
$cgi->a({-href=>"chgpass.pl?user=$login{'username'}", -target=>"main"},
"Change My Password"),
$cgi->end_form;

print '<table border=0><tr><td></td>',
'<td>'.$cgi->a({-href=>"$search", -target=>"main"}, "Search").'</td>';

if ($roll ne 'helpdesk') {
print ' <td> '.$cgi->a({-href=>"$add", -target=>"main"}, "Add").'</td>',
' <td> '.$cgi->a({-href=>"$modify", -target=>"main"}, "Modify").'</td>',
' <td> '.$cgi->a({-href=>"$delete", -target=>"main"}, "Delete").'</td>',
' <td> '.$cgi->a({-href=>"$chgIMAPpass", -target=>"main"}, "Change Password").'</td>';
}
print '</tr></table>';
print "<iframe name='main' src='search.pl' width='800' height='700'></iframe>",
$cgi->end_html;
}


sub queryDB {
my $user = shift;
my $dbh = DBI->connect("DBI:mysql:$isodb:$isosrv", $isouser, $isopass,
{'RaiseError' => 1, 'PrintError' => 0 })
or die "Connection Failed: $isodb DB on $isosrv\n\t$DBI::errstr\n";

my $sth = $dbh->prepare("SELECT password, roll, name
FROM users
WHERE id = '$user' and status = 'active' limit 1")
or die "prepare statemnet failed: $DBI::errstr\n";
$sth->execute;
my ($password, $roll, $name) = $sth->fetchrow_array;

$sth->finish;
$dbh->disconnect;
return ($password, $roll, $name);
}
The application is still under development, but the login and the other scripts that this script calls are all working. I'm thinking about reworking the script to use CGI::Application.
http://search.cpan.org/~markstos/CGI-Application-4.06/lib/CGI/Application.pm.

bazz
08-20-2006, 05:05 PM
Thanks to you both.

@FishMonger, Thanks for the code as it helps me to understand what I am really trying to do. :)

bazz

bazz
05-28-2007, 01:35 AM
ugh... finally getting around to doing this but have discovered an issue that I can't get my head around.




sub queryDB {
my $isodb = 'MainUserLogin';
my $isosrv = 'localhost';
my $isouser= 'myName';
my $isopass= 'password';
my $port = '3306';
#my $user = shift;


my $dbh = DBI->connect("DBI:mysql:$isodb:$isosrv", $isouser, $isopass,
{'RaiseError' => 1, 'PrintError' => 0 })
or die "Connection Failed: $isodb DB on $isosrv\n\t$DBI::errstr\n";

my $sth = $dbh->prepare("SELECT Customer_ID, status, User_Name, PassWord
FROM securityTable
WHERE User_Name = '1' and status = 'active' ")
or die "prepare statement failed: $DBI::errstr\n";
$sth->execute;
my ($Customer_ID, $status, $User_Name, $PassWord) = $sth->fetchrow_array;

$sth->finish;
$dbh->disconnect;
return ($Customer_ID, $status, $User_Name, $PassWord);
}



I think only the vars have been changed from the script posted by FishMonger (above) but, I have the following problems with it now.

a. In the WHERE line, User_Name never equals 1, it is always somones name. How can I check User_Name against the login_name. That said, why if User_Name is set to 1 deos the script appear to work. does 1 simply mean true? Also status makes no difference. only active accounts should get through but inactive does also.

b. I don't seem to understand where the $user Var is coming from and, therefore, why it might need to be shifted.

The columns in my MySQL Db are as set out in the return line.
Please help me understand this REALLY annoying thing as I have worked at it on and off for the past few days.


bazz

FishMonger
05-28-2007, 09:14 PM
a. In the WHERE line, User_Name never equals 1, it is always somones name. How can I check User_Name against the login_name. That said, why if User_Name is set to 1 deos the script appear to work. does 1 simply mean true? Also status makes no difference. only active accounts should get through but inactive does also.In boolean context 1 means true, but in this context, it's being used in string comparison i.e., where the User_Name equals the character '1'.

Since the db query will only return active users, if inactive users are getting through, it's not due to this portion of the script.

The value of the "status" field in my db is either 'active' or 'inactive'. In hindsight, since it's always used in boolean context, it would have been better to name the field 'active' and the value would either be 0 or 1 (meaning false or true). In fact, I see several things in my original example that could/should be improved and I may fix them at some point in the future.


b. I don't seem to understand where the $user Var is coming from and, therefore, why it might need to be shifted.The $user var is the login/user name supplied by the user when attempting to login, and is passed to the queryDB subroutine, which is why it is being "shifted" off of the subroutine's @_ array.

Here's the adjusted sub.
sub queryDB {
my $db = 'MainUserLogin';
my $srv = 'localhost';
my $user= 'myName';
my $pass= 'password';
my $port = '3306';
my $user = shift;

my $dbh = DBI->connect("DBI:mysql:$db:$srv", $user, $pass,
{'RaiseError' => 1, 'PrintError' => 0 })
or die "Connection Failed: $db DB on $srv\n\t$DBI::errstr\n";

my $sth = $dbh->prepare("SELECT Customer_ID, status, User_Name, PassWord
FROM securityTable
WHERE User_Name = $user AND status = 'active'")
or die "prepare statement failed: $DBI::errstr\n";
$sth->execute;
my ($Customer_ID, $status, $User_Name, $PassWord) = $sth->fetchrow_array;
$sth->finish;
$dbh->disconnect;
return ($Customer_ID, $status, $User_Name, $PassWord);
}

bazz
05-29-2007, 02:45 PM
Thank you FishMonger.

That sub seems to be working now and the script only falls down (seemingly), on the crypt password part. I am having the necessary module installed on my server and I shall see how I get on after then.

As for the line


if ($password) {
my $salt = substr($StoredPassword, 3,8);
my $password = unix_md5_crypt( $login{'password'} );


Can you point me to a reliable tutorial (or, better, explain here) what the 3 and 8 mean. I am unsure as to why they would be added to the $StoredPassword and not to the $login{'password'}, when they are subsequently to be compared.

bazz

FishMonger
05-29-2007, 08:48 PM
Can you point me to a reliable tutorial (or, better, explain here) what the 3 and 8 mean.perldoc -f substr

----------
The salt is the "key" that the unix_md5_crypt sub uses to encrypt the password. If you don't supply a salt value, a random salt value will be generated, which is fine when creating new passwords, but not when you need to compare the supplied plain text password to the encrypted one.

Here's an example of an MD5 encrypted password.

$1$.H3lfPPz$PP.2q/IX.b/NxvJAexvjC1

Here's its breakdown

$1$ = magic string (used for MD5-crypt, it can also be $apr1$ which is for Apache crypt and is compatible with .htpasswd files)

.H3lfPPz = salt value (maximum of 8 characters)

$ = separator between the salt and encrypted password

PP.2q/IX.b/NxvJAexvjC1 = encrypted (encoded) password

So, the substr command is extracting the salt value of the stored MD5 password and uses it to encrypt the supplied plain text password. Then the 2 encrypted passwords are compared. If they match, the user supplied the proper password. If a different salt value had been used, then the encrypted passwords would not match even though the correct password was supplied.

Here's the correction you need to make.if ($password) {
my $salt = substr($StoredPassword, 3,8);
my $password = unix_md5_crypt( $login{'password'}, $salt );
if ($StoredPassword eq $password) {
# they matched
}

bazz
05-30-2007, 12:58 AM
FishMonger, you're a star (as we say here). What you have just explained, makes it clealry understandable, especially when compared with the tute link you gave in the other thread. I shall try it later and see how I get on.

I has not realised that the $1$salt$ was to be entered like this. I has just added a salt to the start of the encrypted password so my $StoredPassword was wrong. Hopefully that's all it will take to get this going and I can then move on to building a session_ID to bring $Clients_Full_Details into the script which I redirect the successful login towards.

Thank you once again.
bazz

bazz
05-30-2007, 01:32 AM
Just to check:

my $salt = substr($StoredPassword, 3,8);

say the $storedPassword = $1$909867$gdgqwtyiqjhdQEfdsWERfsdfsflkw

does the 3, 8 mean that it is pulling out the salt from the 4th character along for another 8 characters? So I ought to make sure I use 8 charcters then? $1$ are numbers 0, 1 and 2?

Therefore, if I had six characters in the salt the line of code would be:

my $salt = substr($StoredPassword, 3,6); ?

I'm not sure actually because salt is encrypted too isn't it?

bazz

ps. sorry to be so-o-o slow with this.

FishMonger
05-30-2007, 02:48 AM
Just to check:

my $salt = substr($StoredPassword, 3,8);

say the $storedPassword = $1$909867$gdgqwtyiqjhdQEfdsWERfsdfsflkw

does the 3, 8 mean that it is pulling out the salt from the 4th character along for another 8 characters? So I ought to make sure I use 8 charcters then? $1$ are numbers 0, 1 and 2?

Therefore, if I had six characters in the salt the line of code would be:

my $salt = substr($StoredPassword, 3,6); ?


"By jove, I think he's got it" :)

I'm not sure actually because salt is encrypted too isn't it?No, the salt is just a string of randomly selected characters with a maximum length of 8, but could be set to as little as 2. In fact, I've seen people hard code the salt with only use 2 characters instead of a random selection.

This is taken from the Crypt::PasswdMD5 module source code that shows 1 method how the salt can be generated.
$itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

$salt = '';
$salt .= substr($itoa64,int(rand(64)+1),1) while length($salt) < 8;
Only the portion after the salt is encrypted (or more correctly, it's encoded)

bazz
05-30-2007, 12:13 PM
"By jove, I think he's got it" :)



Music to my ears!! :)

I think I have identified part of why I wasn't getting it. I was encoding the password using an md5 encoder and then adding $1$12345678$ in front of it. Then when I used the login script, I was (and still am not), getting a match.

Has just struck me that md5 is a different encoding than the unix_md5_crypt?

So I shall now build my script to input the clients data to the MySQL Db using unix_md5_crypt and see how the match works after then.

I have been away too long from this coding melarky. I should have recalled having used the substr($var 0,200) before, when presenting the first 200 characters of my clients text fields.

now playing catch up to make my not-so-young brain recall better.

bazz

bazz
05-30-2007, 02:49 PM
Ignore me for a bit: I'm an eejit.

I am off to read how to encode the salt and pwd. The way had had done it was absolutely stupid.

my $salt = '12345678';
$StoredPassword = unix_md5_crypt( $StoredPassword );
$StoredPassword = $salt.$StoredPassword;

numptie/bazz :o

bazz
05-31-2007, 11:56 AM
OK I have sorted the salt issue, I think but this is the error the server gives me and I don't understand what it means.



login_test.pl: param(): attempt to read/write deleted session at login_test.pl line 25,


here is the script


my ($authenticated, $StoredUserName, $Client_Full_Details) = authenticate_user() if $cgi->param('Login');

($authenticated || $session->param('logged_in')) ? admin_page (): login();

#####################################################################

sub login {

print $cgi->header,
$cgi->start_html(-title=>'Email Administration Login',
-style=>{-src=>'/emadmin.css'}
); # replace this with my own header here.

print "<img src='/logo.gif' />\n",
$cgi->h1("Email Administration").$/,
$cgi->start_form(-name=>'login'),
'<p>Username: ', $cgi->textfield('username'), "<br>\n",
'Password: ', $cgi->password_field('password', ''), "</p>\n",
'<p>', $cgi->submit('Login', 'Login'), "</p>\n",
$cgi->end_form;


print $login{'failed'} if defined $login{'failed'};
print $cgi->end_html;
}


sub authenticate_user {
if ( defined $login{'username'} && defined $login{'password'} ) {
my ($Customer_ID, $Status, $StoredUserName, $StoredPassword, $Client_Full_Details) = queryDB($login{'username'});


if ($password) {
$salt = substr($StoredPassword, 3,8);
my $password = unix_md5_crypt( $login{'password'}, $salt );

if ( $StoredPassword eq $password ) {
#$session->param('logged_in', 1);
#$session->param('admin', $StoredUserName);

return (1, $StoredUserName, $Client_Full_Details);
}
}
}
$login{'failed'} = 'Invalid username, or password...Please try again';

return 0;
} # end of sub


sub queryDB {
my $isodb = 'MainUserLogin';
my $isosrv = 'localhost';
my $isouser= 'myDbAccessUsername';
my $isopass= 'myDbAccessPassword';
my $port = '3306';
my $user = shift;


my $dbh = DBI->connect("DBI:mysql:$isodb:$isosrv", $isouser, $isopass,
{'RaiseError' => 1, 'PrintError' => 0 })
or die "Connection Failed: $isodb DB on $isosrv\n\t$DBI::errstr\n";

my $sth = $dbh->prepare("SELECT Customer_ID, StoredUserName, StoredPassword, Client_Full_Details
FROM securityTable
WHERE StoredUserName = '$user' AND Status = 'active' ")
or die "prepare statement failed: $DBI::errstr\n";


$sth->execute;
my ($Customer_ID, $StoredUserName, $StoredPassword, $Client_Full_Details ) = $sth->fetchrow_array;
$sth->finish;
$dbh->disconnect;
return ($Customer_ID, $StoredUserName, $StoredPassword, $Client_Full_Details);
}




It seems to me that the issue may be at the very top - above the line of ######### - but, I really need your help on it, please.

bazz

FishMonger
05-31-2007, 06:26 PM
There are a couple problems with the modifications you made to my script.

In the queryDB sub, you commented out the assignment of the $user var. In my version of the script, that is the only place where it was being assigned and used. Did you make it a global var and declare it somewhere else?

In the authenticate_user sub you commented out the assignment of the logged_in session var that are used to to determine if the session should be deleted, and is the cause of your error message. Did you make any changes to this part of the script, which should be just prior to the portion of the script that you posted?
if ( $login{'logout'} || ! $session->param('logged_in') ) {
$session->clear;
$session->delete();
}

The if ($password) { in the authenticate_user sub should be if ( $encrypted_pass ) {

You probably should cleanup some of the var names to be inline with your needs. All of the var names starting with $iso are specific to my dept (Information Systems and Operations).

bazz
05-31-2007, 07:19 PM
YAY!!!!!!!!!!!!!!!! :thumbsup: :thumbsup: :thumbsup: :thumbsup:

At last it works. wish I could say I did it but, I can't.

I don't think I can thank you enough FishMonger.

bazz

FishMonger
05-31-2007, 07:50 PM
You're Welcome!

I'm glad that I was able to help, but the most important question is, do you understand why it's working? Once you you know how and why it works, it should be easy for you to write your own more complicated version(s).

bazz
05-31-2007, 08:08 PM
I can see why the login works and was confused between $StoredPassword, $encrypted_pass and $password. The commented out parts you referred to, were in fact running againand chnaging the vars to their correct names made it work.

Starting to read about sessions now and, I think I see also that your code check for / creates a Session. i just need to make my other perl scripts read in the session details and so be able to run with $Client_Full_Details.

I have about 6 pages to read, including some which you have posted in other threads. After reading them, I hope I will get the hang of them.

So far, I think this line looks for an existing session and if 'true' it loads it and if not, it creates a new one.


$session = new CGI::Session() or die CGI::Session->errstr;


Then I think it places the session data in a cookie on the server with this code


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



and then I can call in the data as required like this


my $name = $cgi->param('username');
$session->param('username', $name);



Close?? And isn't it always better to send the session via the url so that it will work for those who have cookies disabled?

bazz