Hi snippets watcher!
This is a simple structure of a login script and the very
first attempt to do so.
I'd appreciate proposals of improvements to the very basic
idea itself, and secondly of the code, too.
I know that CGI.pm is capable to substitute some of my
here-documents, but because there are not all function
implemented html offers, so I prefer them.
Also note, that this only the basic form of a standalone
script to serve further functions following the login-procedure
which don't show up here. On the other hand I am pretty shure,
that this is a good snippet to show the way of how to do it,
presumed, that I have not run into a dead-end.

enjoy reading the code!

UPDATE 20010913: I enclosed hijacking detection at the very
end of the script.
--
there are no silly questions
killerhippy
Pipe this to a file named "login"
#!/usr/bin/perl -wT ############################################################ # login - a perlscript to make a secure login to any site # # # # Requirements: # # - a server running a cgi-capable webserver # # with perl being installed # # and mysql (tested with apache & linux) # # - perlmodules DBI, DBD::mysql, CGI # # - any use for this :) # # # # This script is able to run at any place if you know how # # to configure your webserver. It runs from the cgi-bin- # # directory without any further configuration. # # If its name conflicts with your files, simply rename it # # and change the url at the "user config" a few lines down # # # # Prerequisite is to execute the sql-statements from the # # usertrack.sql file like this (as root): # # mysqladmin create usertrack # # mysql usertrack < usertrack.sql # # Note that you have to modify this file for your own # # needs (including dbuser und dbpassword). # # # # The special features of this script are: # # -no use of cookies # # -no hints about existing users from its reaction. # # -only one handshake of password transfer. # # -each request creates a new key in konjunction with the # # username. # # -automated logout by reloading after a specified timeout.# # # ############################################################ use strict; use DBI; DBI->trace(1); use CGI; use CGI::Carp qw(fatalsToBrowser); ###################### # user config starts # my $reloadpage = "http://localhost/cgi-bin/login"; # url of this scrip +t my $timeout = "20"; # seconds before lo +gout my $dbhost="localhost"; my $dbuser="gunny"; my $dbpass="RaZoRbLaCk"; my $dbname="usertrack"; # user config ends # ###################### my $q = new CGI; my $dbquery; my $sth; my $dbh; my @row; my $dbpassword; my $short; my $vname; my $nname; my $password; my $currentstate; my $state; my $time = time() . rand 65535; sub getrow { $dbquery = qq~ SELECT * FROM users WHERE short='$short' ~; $sth=$dbh->prepare($dbquery); $sth->execute; @row = $sth->fetchrow_array; $sth->finish; } sub putpassword { $dbquery = qq~ UPDATE users SET password='$password' WHERE vname='$vna +me' AND nname='$nname' ~; $sth=$dbh->prepare($dbquery); $sth->execute; $sth->finish; } sub putstate { $dbquery = qq~ UPDATE users SET state='$currentstate' WHERE vname='$vn +ame' AND nname='$nname' ~; $sth=$dbh->prepare($dbquery); $sth->execute; $sth->finish; } sub login { print <<EOF; <h4 align=center>Loginprompt:</h4> <table align=center border=1 cellpadding=8 bgcolor=#CCFFFF> <form method="post" enctype="application/x-www-form-urlencoded"> <input type=hidden name="currentstate" value=$time> <tr><td align=center>login-name:<br><input type=text name='short'></td +></tr> <tr><td align=center>password:<br><input type=password name='password' +></td></tr> <tr><td align=center><input type=submit name="login"> <input type=reset value="Reset"></td></tr> </form></table></center><hr> EOF } sub html_end { if ($vname) { print <<EOF; <h4 align=center></h4> <table align=center border=1 cellpadding=8 bgcolor=#CCFFFF> <form method="post" enctype="application/x-www-form-urlencoded"> </table> <input type=submit> <input type=reset> <input type=hidden name=vname value=$vname> <input type=hidden name=nname value=$nname> <input type=hidden name=currentstate value=$currentstate> <input type=hidden name=state value=$state> <input type=hidden name=short value=$short> </form></center><hr> <center>Hallo $vname $nname</center><br> EOF } print $q->end_html; $dbh->disconnect; exit; } ###################################################################### +########## # main + # ###################################################################### +########## # start database connectin and die if it fails $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass)|| d +ie "Couldn't connect to db $dbname at $dbhost $DBI::errstr"; # safe control data $short=$q->param('short'); # used to identify user all t +he time after login $password=$q->param('password'); # only used at loginprocedure $currentstate=$q->param('currentstate'); # new state if this script is + invoked, created from the script $state=$q->param('state'); # old state to verify connect +ion coming from the database $vname=$q->param('vname'); # data to present all the tim +e after login and to keep contact(!) $nname=$q->param('nname'); # data to present all the tim +e after login if (# user has given login-name ($short) and # was not logged in before (not $state) ) { # fetch user's data getrow; if (@row) { (undef,undef,$vname,$nname,$dbpassword,undef)=@row; if (# no password was given (not $password) or # password given does not match password at table (not ($dbpassword eq $password)) ) { # erase all data to disable control $q->delete_all(); undef @row; undef $vname; undef $nname; undef $state; undef $currentstate; } } putstate; getrow; (undef,undef,undef,undef,undef,$state)=@row; } # start the html-code # the java-script is not nescessary but focusses the cursor to the for +m print $q->header(); print <<EOF; <html> <head> <title>User-Tracking</title> <meta http-equiv="refresh" content="$timeout; URL=$reloadpage"> <SCRIPT LANGUAGE="JavaScript"><!-- function setFocus() { document.forms[0].elements[1].focus(); } //--></SCRIPT> </head> <body onLoad="setTimeout('setFocus()',1)"> </head> <h1>User-Tracking</h1> EOF if (not $vname) { login; html_end; } # this is, where the keycode is compared to enshure it is the right us +er requesting this getrow; (undef,undef,undef,undef,undef,$state)=@row; if ($currentstate eq $state) { $currentstate=$time; putstate; getrow; (undef,undef,undef,undef,undef,$state)=@row; } elsif ($vname) { print "Your connections has been hijacked!!!<br>"; print "...or the server has a current problem.<br>"; print "Anyway, report this to the webmaster.<br>"; print $q->end_html; $dbh->disconnect; exit; } # space to put in your own code # stop html-code and exit the script html_end;

Pipe this to a file named "usertrack.sql"
DROP TABLE IF EXISTS users; CREATE TABLE users ( ID INT PRIMARY KEY AUTO_INCREMENT, short varchar(10) NOT NULL, vname VARCHAR(50) NOT NULL, nname VARCHAR(50) NOT NULL, password char(16) DEFAULT '' NOT NULL, state char(50) DEFAULT '' NOT NULL, UNIQUE short (short) ); INSERT INTO users VALUES ('','admin','Micky','Mouse','admin',''); INSERT INTO users VALUES ('','user1','Donald','Duck','daisy',''); INSERT INTO users VALUES ('','user2','Dagobert','Duck','gold',''); GRANT SELECT, INSERT, UPDATE, DELETE ON usertrack.* TO gunny@localhost + IDENTIFIED BY 'RaZoRbLaCk'

Replies are listed 'Best First'.
Re: usertracking
by $code or die (Deacon) on Sep 22, 2001 at 06:42 UTC
    Hello khippy

    This is a good effort. There are however a few things I would change.

    I would suggest using my to add scope to your variables in your subroutines. You are currently declaring all your vars at the top of the script and then using them all in your subs. This makes them all essentially global (at least within the code you present).

    I would also suggest adding most of the code in your "Main" section to a sub called isLoggedIn() or something. Put all of that code somewhere out of the way - maybe in a module if you can.

    I also highly recommend reading merlyn's column on cookie management. The code snippet he presents is quite short and the commentary is illuminating.

    I have recently written a module based on the idea in that column that makes it easy to drop in to different projects that authenticate using different methods.

    Of course, I'm slightly reinventing the wheel, there already exists Apache::Session and CGI::MxScreen which do similar things and probably more. So mine probably will not make its way to CPAN, but I'll post it here.

    Update: I would also suggest looking at Tricks with DBI especially the section on placeholders.

    Simon Flack ($code or die)
    $,=reverse'"ro_';s,$,\$,;s,$,lc ref sub{},e;$,
    =~y'_"' ';eval"die";print $_,lc substr$@,0,3;

      Thanks for looking at the code, Simon!
      Thanks also for the suggestions for code improvements.
      As this snippet is "currently being worked at" they are quite
      useful.
      One thing, I don't want to follow is the cookie thingy. I started
      this script especially to avoid the use of them because some people
      don't want to allow them, so why force them if there is a way out of
      it?
      I also know, that I am reinventing the wheel :) but on the other hand
      I wanted to create a secure login-procedure totally independent of
      other people's code - but maybe I should better look at the modules
      you suggested nevertheless.
      This has been a good way to study a usertracking, so far. I'd appreciate
      some security experienced people to say a word about the idea I am
      following.
      --

      there are no silly questions killerhippy