Using the challenge/response Secure Login
Moderator: General Moderators
Using the challenge/response Secure Login
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
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
- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
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.
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.
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
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.
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
}
?>- 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.
change your if statement to:
see if that helps.
Code: Select all
if (mysql_num_rows($result) < 1)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.
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.
Well, I might have made some progress.
I now changed the testpage to:
test2.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?
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
}
?>- 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?
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
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.
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.
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:
which should have escaped data.
Something like:
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.
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());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());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.
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
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.
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.
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?:
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.
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
}
?>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.
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.
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.
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland