> 
> 
> I working on a rather complex database cgi program and i've never done
much work with user logins. Ideally I woudl love to set up a secure
login which will verify passwords aggaints data stored in an SQL table.
The modle I wish to use ie below. The area I need help with is the
proper way to set and retrieve secure cookies.
> 
> retrieve pw:login from form.
> compare to stored password.
> if match encrypt a string which is stored in the database then set the
same string as a cookie with date and IP.
> use this cookie to authenticate each subsiquent page until logout.
>  can someone point me to a good how to ?
> 

Security is always relative.  Cookies are only "secure" (in one sense of
the word) when passed over an https (like) connection, as your password
should be too, otherwise anyone can sniff the password and be able to
retrieve their own authenticated cookie. 

It sounds like you have a decent start, step 1 is easily handled with
the CGI module.  Comparing the password is easily handled with DBI to
connect to your database. (Minor point, be sure to store the password in
an encrypted/hashed way rather than directly.)  Step 3 is a little odd,
why are you storing the string in the database? Generally I don't think
this is going to provide security, aka all I have to do is guess the
string you stored and set a cookie manually, then I have an
authenticated session.  Generally the idiom goes something like, create
a private key (aka a passphrase or something that no one else knows),
take the key and some reproducible non-private information (such as the
user name/id) and hash the two together. Provide the hash, and the
information used to create it (NOT the private key!) in the cookie. Then
during the requests check to see if the information provided back by the
user from the cookie can be used to reproduce the same hash using the
same private key as before.  If the hash is reproduced then the user is
authenticated. Assuming no one else has the private key, then the hash
should be unique for the user and the private key should be
non-guessable from the hash, generally if this holds an attacker can't
produce a string that will authenticate correctly. In general using the
IP address in this is not a good idea unless you know precisely what IP
a particular user is going to come from.  The complexity of your hash
inputs can vary what you can do, aka adding expirations, etc.

I don't take credit for this, the idiom is yanked from the Writing
Apache Modules with Perl and C book from ORA, it provides an excellent
discussion as well as code samples of how to implement this.

I imagine there are also a number of modules available from CPAN that
provide this functionality, Apache::Session rings a bell.

http://search.cpan.org/~jbaker/Apache-Session-1.6/Session.pm#Tracking_users_with_cookies

My recent implementation looks like this, the following is untested and
I am not a security professional,

Thoughts?

http://danconia.org


use constant CONFIG_AUTH_KEY => 'Secret passphrase goes here';
use Digest::SHA1;

#
# generate an authentication string that can be used to verify if
# a user is who they say they are without a database lookup
#
sub authentication_string {
    my $self = shift;

    my $uid = $self->id;
    my $time = time;
    my $expires = 2592000;

    my $data = join(':', CONFIG_AUTH_KEY, $time, $expires, $uid);
    my $hash = Digest::SHA1::sha1_hex($data);

    return "uid=$uid time=$time expires=$expires hash=$hash";
}

#
# class method to verify an authentication string, returns
# an exception on failure or the user id of the string on
# success
#
sub check_auth_string {
    my $class = shift;
    my ($auth) = @_;

    unless (defined $auth) {
        die "Missing arg: auth string";
    }

    # deparse auth string then check user cache
    my @auth = split(' ', $auth);
    my %auth;
    foreach my $element (@auth) {
        if ($element =~ /^(.+)=(.+)$/) {
            $auth{$1} = $2;
        }
    }
    foreach my $key ('uid', 'time', 'expires', 'hash') {
        unless ((defined $auth{$key}) && ($auth{$key} ne '')) {
            die "Cookie corrupt: $key";
        }
    }
    my $testdata = join(':', CONFIG_AUTH_KEY, $auth{'time'},
$auth{'expires'}, $auth{'uid'});
    my $testhash = Digest::SHA1::sha1_hex($testdata);

    unless ($testhash eq $auth{'hash'}) {
        die "Auth string corrupt";
    }

    return $auth{'uid'};
}


-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
<http://learn.perl.org/> <http://learn.perl.org/first-response>


Reply via email to