Looking for feedback on the security model that I have for a particular site. We've been asked to encrypt only the login process (SSL). This site is for an administration console and will only have a few users. They are required to have cookies enabled. Here's basically how it works:

The user enters a username/password combination. This is sent over an ssl connection to the authentication script. The password is hashed using Digest::MD5 and a salt that is read from a non-web accessible file. This is compared to the hashed password in the database. If they don't match, they are redirected to the login screen up to seven times, after which they are locked out until an administrator unlocks them. If successful, a digest is created with the following algorithm:

sub _create_session_digest { # Please note that we will compare the digest against what's in th +e database rather than # recompute. It's quite possible for someone's ip address to chan +ge with every request. my $self = shift; my $md5 = new Digest::MD5; my $remote = $ENV{ REMOTE_ADDR } . $ENV{ REMOTE_PORT } . $self->{ +_salt }; my $id = $md5->md5_base64( time, $$, $remote ); $id =~ tr|+/=|-_.|; # Make non-word characters URL-friendly $id; }

This digest is returned as a cookie. Subsequent accesses to the admin console will return the digest and compare it to the database. If this takes too long, their session times out (controlled by the script, not the cookie) and they must relogin. If the digest matches, a new digest is created, stored in the database, and sent back in a cookie. Except for the initial SSL connection when the username and password are submitted, they will never again be sent. Here's the main code that controls this:

# Everything in ALL CAPS is a constant sub validate_and_get_new_cookie { my ( $self, $cgi, $user, $pass ) = @_; my $cookie = $cgi->cookie( SESSION_COOKIE_NAME ); # delete sessions older than that session ID's allowed timeout $self->_clear_old_session( $cookie ); if ( defined $user and defined $pass ) { # they're submitting a username and password, so let's try to +log them in my $attempts = $self->_count_login_attempts( $user ); $self->_lockout if $attempts >= MAX_LOCKOUT_ATTEMPTS; my $db_pass = $self->_get_password( $user ); my $user_pass = $self->_create_digest_from_password( $pass ); if ( $db_pass eq $user_pass ) { return $self->_create_digest_cookie( $user ); } else { my $attempts = $self->_update_attempts( $user ); $self->_log_bad_attempt( $user, $pass ); $self->_lockout if $attempts >= MAX_LOCKOUT_ATTEMPTS; print $q->redirect( LOGIN_PAGE ); } } else { # no user or password, so we'll try to validate with the cooki +e my ( $user, $active ) = $self->_get_digest_info( $cookie ); if ( ! defined $user or ! $active ) { # didn't get a user name or they've been inactive too long print $q->redirect( LOGIN_PAGE ); } else { return $self->_create_digest_cookie( $user ); } } }

In the future, I plan to add a 'bogusLogin' table to the database. The intent is to even lockout non-existent user IDs after MAX_LOCKOUT_ATTEMPTS so that crackers can't use the lockout feature to determine if they have a valid user id. Have I overlooked anything?

Cheers,
Ovid

Vote for paco!

Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.


In reply to CGI Security Advice Sought by Ovid

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.