Eugene Sotirescu <[EMAIL PROTECTED]> wrote:
> I'd like to authenticate users via a login form (username, password
> text fields) instead of using the standard dialog box a browser pops
> up in response to a 401 response code.

Here's what I do in an application I'm currently working on...

Application has a table of users stored locally, and along with the
usual /etc/passwd-ish sort of info stored, each user row also has a
random per-user secret.  The application also has its own secret
stored in the source code (and changed from time to time).

When a browser session comes in without appropriate authentication
cookies, they get a login screen.  When they post username and
password, check that against the locally stored user table, and if
they match, issue a set of authentication cookies.  These hold three
pieces of information:
 - the username
 - the date-time (seconds since epoch) these cookies were issued
 - an MD5 hash

The hash is of: username, per-user secret, application secret,
 application's version number, IP address of browser session, and
 time cookies were issued.

If a session comes in with these cookies already set, they are
checked.  Application reads the username and time from the cookies,
the IP address from the Apache request object, then looks up the
per-user secret in the local users table, and using the secret and
version number already known to it, recomputes the MD5 hash.  If this
matches the hash read from the cookie, the cookie set is valid.

If the cookie set is invalid, treat the user as if they had no cookies
and send them to the login screen.  If the cookie set is valid,
compare the cookie time to current time, and see if the difference is
greater than some configurable "expire" (say, 30 minutes).  If the
difference is lesser, then compute a new set of cookies with a new
time, and issue them to the browser with this page.  If the difference
is greater than the expire, send them to a screen that says the login
is expired and asking the user to resubmit username/password.

Finally, use SSL to protect the whole mess from sniffing.  SSL is a
reasonable protection against the initial username/password submission
from being sniffed.  It's also a protection against the cookie set
being copied and used by another user, along with the IP address.  In
this particular situation, the application is not for the general
public, and we can safely assume that all legitimate users will have
consistent IP addresses for the life of a browser session,a nd will
have cookies enabled on their browser.

The real key to using cookies this way, I think, and the one that
transfers well to a more public environment, is the expiration, which
is *not* under browser control.  Any authentication cookie set handed
out by this application is good only for a limited time, so if anyone
can "break" it it'll only do them good if they can do it within that
time period (hence attacks against SSL & MD5 are impractical).  Since
both the time encoded in the cookie hash, and the time it is compared
against, are both from the server's clock, the client can't affect
this by futzing with its own clock.  The tradeoff between convenience
and security can be controlled by changing the expire period.

Note that since a new set of cookies is issued for *every* access, a
valid login will not actually "expire" until the user fails to load
any new pages within the application for the duration of an expire
period.  Even if you set the expire to something really short, say a
minute, a user can continue using it without resubmitting
login/password as long as they do at least one thing that causes a new
HTTP access every minute.  As soon as they step away for a minute,
when they come back they'll be expired and have to log in again at the
next access.

I didn't use any modules for this, so I don't know if modules are
available for it, but the basic cookie generation and checking code
was rather short and simple.  The login & expired login page and
dealing with those form submissions took some more time to write, but
that's the sort of thing you probably want to design yourself anyway.

Thorough testing of every possible path through the login code is
essential here!  Remember also that users can "submit forms" that you
never gave them.  Test every possible path through the code to make
sure you're making the right decisions about a user's authentication
status, and do not limit the testing to only those form submissions
that make sense given what forms you present the user with.  This is
easy to overlook but you don't have security without it, if anyone
ever sees your code (and it's good to assume that someone will see it).

  -- Cos (Ofer Inbar)  --  [EMAIL PROTECTED] http://www.leftbank.com/CosWeb/
  -- Exodus Professional Services -- [EMAIL PROTECTED] http://www.exodus.net/
 Now, for the Quote out of Context(TM), from Mike Haertel <[EMAIL PROTECTED]>
 "I think it's pretty clear that Unix is dead as a research system."

Reply via email to