Page 1 of 2
Using the challenge/response Secure Login
Posted: Mon Nov 28, 2005 3:58 pm
by matthijs
First of all I want to thank Maugrim_The_Reaper for the tutorial, I read it with great interest. After reading Chris' book on security, I'm trying to take a step further and implement as most security as possible in my applications.
Secondly I hope it's ok that I post my question here, in some way I'm not allowed to post my question in the thread of the tutorial or the one on D2.
So my question:
how can I use the loginproces to secure pages? I understand the tutorial, and can follow most of the code. As I am authorized I get redirected to hello.php. But what if I browse directly to hello.php? There's nothing to stop me from doing that.
So how can you secure that file? Or how can I use the challenge/response system to secure other pages? Sorry if this should be obvious, but to me at the moment it isn't...
Thanks, Matthijs
Posted: Mon Nov 28, 2005 5:13 pm
by Burrito
you'll need to include your session information on each page you want "secured". You should check to see if a session var has been set and if not, redirect them to the log in screen.
Posted: Mon Nov 28, 2005 5:14 pm
by Ambush Commander
Once you've authenticated the user, you can bind the authorization to his session so he doesn't have to be reauthenticated every request.
Posted: Mon Nov 28, 2005 6:29 pm
by infolock
another method you could look into is having Apache authenticate and load information as part of it's enviorment, thus assigning to the superglobal $_SERVER variable.
While I will claim knowledge of this working, I will also claim no knowledge as to if this is unsecure or not. But still, it's another method you can look into at least.
Posted: Tue Nov 29, 2005 12:40 am
by matthijs
Thanks for the suggestions, really appreciated. I'll go and try to implement them and will come back with any questions - or better - a solution. Thanks.
Posted: Tue Nov 29, 2005 4:03 am
by matthijs
So far I have managed to get it working, but I'm sure something is not right.
After entering the correct login and password on the index.php page, I am succesfully redirected to hello.php.
After that I can visit my testpage (see below).
I have the following as a testpage:
test.php
Code: Select all
<?php
/*
Start the PHP Session
*/
session_start();
$conn = mysql_connect('localhost', 'dbuser', 'dbpw') or die('Could not connect to database');
mysql_select_db('dbname', $conn) or die ('Can\'t use mytestdatabase : ' . mysql_error());
/*
Get our server stored SessionID from the database
*/
$result = mysql_query("select sess_id from challenge_record where sess_id = '" . session_id() . "' and timestamp > " . time()) or die("Invalid query: " . mysql_error());
if(mysql_num_rows($result) == 0)
{
header('Location: login.php'); // return user to login page
}
else
{
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Untitled</title>
</head>
<body>
<p>This is secret content</p>
</body>
</html>
<?php
}
?>
However, I can also visit this page and get authorised when I:
- clear all cookies
- visit index.php (where the session data is created and stored in db)
- do NOT fill in login or password
- go to test.php
So obviously I do something wrong. Any suggestions are appreciated.
Posted: Tue Nov 29, 2005 4:06 am
by Jenk
change your if statement to:
see if that helps.
Posted: Tue Nov 29, 2005 4:20 am
by matthijs
jenk, thanks for the help. Unfortunately, that doesn't help.
The problem stays. I only have to visit the index.php page, the session_id is getting stored in db, and without filling in the login form I can now visit the testpage and get the "secret content".
There's something fundamentally wrong with how I tried to do this, but I'm not sure what else to do.
My gues was that the logic for the testpage would be:
- start session
- receive session information from the cookie
- see if the session_id in the cookie is in the db
However, I think this always is true, because after a visit to the first (index.php) page, there's always a session stored, even without doing the whole challenge-response code.
Posted: Tue Nov 29, 2005 4:40 am
by matthijs
Well, I might have made some progress.
I now changed the testpage to:
test2.php
Code: Select all
<?php
/*
Start the PHP Session
*/
session_start();
// is the one accessing this page logged in or not?
if (!isset($_SESSION['authenticated'])
|| $_SESSION['authenticated'] !== 1) {
// not logged in, move to login page
header('Location: login.php');
exit;
}
else {
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Untitled</title>
</head>
<body>
<p>This is secret content!!</p>
</body>
</html>
<?php
}
?>
This seems to work better.
- If I now clear all cookies and visit test2.php I'm redirected to login
- If I visit index.php (with the login form) but do not fill in the username/password, and then visit test2.php, I'm also redirected to login.php
- After I visit index.php and fill in the correct username/pw I can go to test2.php and get authorised for the "secretcontent".
However, the next question is: is this the right way? What are the security problems remaining?
Posted: Tue Nov 29, 2005 7:31 am
by Maugrim_The_Reaper
That's pretty much it

Sorry if the tutorial didn't make that completely clear - the C/R process is required only or the initial login... After that you depend on the authentication flag set in $_SESSION.
As for other security problems...
Bear in mind the original DB querys used no data escaping (i.e. escaping variables in SQL queries with mysql_real_escape_string()). Without that, XSS/SQL Injection attacks are no doubt possible. Maybe I should have explicitly used proper security practice - but it seemed a bit of information overload...
In the specific example you posted above - the main security concern would probably be the session itself. Sessions can be stolen or hijacked, so its generally a good idea to call
session_regenerate_id() just after you confirm the user and just before you set the authenticate flag (this ensures anyone else following your session suddenly gets dropped).
After that - there are other things you can try. A good start for security is
http://shiflett.org/articles which has some excellent (and surprisingly brief and readable) Session Security articles.
Posted: Tue Nov 29, 2005 8:23 am
by matthijs
Hi M,
Thanks for your answer. Good to hear the code I posted is correct and safe (as far as that can be). You give some good suggestions.
1) DB escaping. I understand. You are probably pointing at this query in login.php:
Code: Select all
/*
Execute a query to select User data based on the submitted username
Normally we would use some escaping here - its omitted for clarity (is magic_quotes dependent)
*/
$result = mysql_query("select userid, username, password from user_accounts where username = '" . $_POST['username'] . "'") or die("Invalid query: " . mysql_error());
which should have escaped data.
Something like:
Code: Select all
$result = mysql_query("select userid, username, password from user_accounts where username = '" . mysql_real_escape_string($_POST['username']) . "'") or die("Invalid query: " . mysql_error());
2) Session hijacking: I will dive into that.
Those articles by Shifflet are indeed very good. I have read them, as well as his book. Will have to reread it a couple of times, that's obvious
His book and articles, as well as a book like the php architect's guide to PHP security, make the potential problems very clear. My main problem is translating all those potential security problems to solutions for those problems in applications. My guess is that that is a problem for many who do not have a programming background. Certainly with a C/R authorisation system like this. However, slowly things are getting clearer.
More in general, I think there are a bit too many tutorials "how to build your own ... in 5 steps" and too little tutorials "how to have your.... hacked in 5 ways." That's something you only realise after reading a bit about security or having something exploited.
Well, you guys here do a great job, thanks a lot for all your help.
Posted: Tue Nov 29, 2005 9:32 am
by Maugrim_The_Reaper
You got the escaping down good...
And yes, there a lot of 5 step tutorials that won't even mention security. That's why so first time login scripts are likely flawed and vulnerable. I think I'll take that on board and use security next time in a tutorial. Better to make the reader aware...than leave them completely ignorant.
Posted: Tue Nov 29, 2005 10:24 am
by matthijs
And what if I want to allow the user to be logged in for a certain amount of time?
Would something like the following be a good solution?:
Code: Select all
....
/*
Compare the actual client Response hash against our expected Response hash
1. If they match, we will authenticate the user
2. If they don't, we will check if a plain text password exists (might be a client with javascript disabled), hash it, and compare to the database stored password hash
3. All other cases - we fail the authentication test, and boot the user (maybe direct to "Try Again" page)
*/
if($_POST['response'] == $expected_response)
{
$_SESSION['authenticated'] = 1;
$_SESSION['userid'] = $user['userid'];
$_SESSION['timeloggedin'] = time(); /* here is were i set the time */
header('Location: hello.php');
}
...
Code: Select all
<?php
/*
Start the PHP Session
*/
session_start();
// is the one accessing this page logged in or not?
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== 1
|| time() > ($_SESSION['timeloggedin']+60) ) {
// not logged in or time expired, move to login page
header('Location: login.php');
exit;
}
else {
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Untitled</title>
</head>
<body>
<p>This is ecret content!!</p>
</body>
</html>
<?php
}
?>
60 seconds is a bit short, but ok for testing purposes. The above seemed to work well.
Are there better solutions? I know the timestamp is also in the database, but my guess is it's faster/better to get the timestamp from the session instead of doing an extra db query just for that.
Posted: Tue Nov 29, 2005 11:15 am
by matthijs
To all reading this:
I found another thorough explanation of a login system, which uses HMAC.
http://pixelated-dreams.com/pages/secur ... _hmac.html
Looks very solid (as well) to me. So might be of use for any other poor soul like me trying to master this stuff
If anyone has an answer to my question about the time-out (see above), please let me know.
Posted: Wed Nov 30, 2005 3:04 am
by Maugrim_The_Reaper
More reading material
Just to note MD5/SHA1 are considered a lot less secure these days. I would suggest SHA256 - and you can grab a PHP implementation from this forum (or the download - if its still up, my server is in maintenance).
You probably already know this of course...