Page 4 of 5

Re: PHP & MySQL site login, security concepts

Posted: Sun Nov 01, 2009 4:36 am
by kaisellgren
Don't hash strong random. The hashing part
batfastad wrote:

Code: Select all

$handle = fopen(__FILE__, 'r');
echo hash('sha512', uniqid($rand.$_SERVER['REMOTE_ADDR'].implode('', fstat($handle)).memory_get_usage(), true));
was for those with weak $rand part. If you are using /dev/(u)random, OpenSSL or CSP, you don't and shouldn't be hashing.

Code: Select all

$salt = pack('H*',generateRandom(64));
$filesystemKey = pack('H*',generateRandom(64));
Where generateRandom() uses /dev/(u)random, CSP or OpenSSL. You don't actually need to pack or encode it if you use the MySQL's BLOB column type to store it directly as binary data.

Re: PHP & MySQL site login, security concepts

Posted: Mon Nov 02, 2009 6:07 am
by batfastad
Yeah I did think that was a bit weird as I was kind of double-hashing when I re-hash the password.

1) Should I be using the urandom rather than uniqid( mt_rand(), true) to regenerate my salt?

Code: Select all

$salt = hash('sha256', uniqid( mt_rand(), true));
$pass = hash('sha256', $_POST['pass'].$salt.$pepper);
2) Should the salt and file system pepper be generated the same way (urandom) and to the same length as each other?

3) Usernames. On some sites peoples login/usernames are their e-mail addresses and on others it's a separate string.
From a user point of view I always prefer it as my e-mail address (less stuff to remember). But I always feel its easier to recover your account (if your e-mail address changes) when you have to choose a separate username.
Are there any advantages/disadvantages to doing it either way?

4) I've been investigating various cookies stored on my system and it seems Paypal stores my username (e-mail address) in a cookie and a few others I've found.
I was under the impression that I shouldn't store username or password on the machine as a cookie, only the user's session cookie. Is that correct?
If I stored username and the session cookie then that would give me a double check

5) I'm storing my password_hash and salt in a VARCHAR field in my DB. And the pepper is just a PHP variable.
Should I be doing it differently... storing as binary/encoding somehow?

6) Session ID forgery/throttling. So I'm storing IP addresses, a counter of forgery attempts, the last session id they tried in a table.
And every time a user visits a secured page I'm looking up their IP address in that DB and comparing the session ID they've sent with the last one tried by that IP. But how do I detect forgery attempts from that?
Do I also need to store a timestamp of the last forgery attempt? Or have the counter represent the number of forgery attempts within a certain time range?

Thanks, Ben

Re: PHP & MySQL site login, security concepts

Posted: Mon Nov 02, 2009 9:00 am
by kaisellgren
batfastad wrote:1) Should I be using the urandom rather than uniqid( mt_rand(), true) to regenerate my salt?
Of course, for anything cryptographic you should always use as strong random generators as possible.
batfastad wrote:2) Should the salt and file system pepper be generated the same way (urandom) and to the same length as each other?
As always, the stronger the better. I wouldn't use less than 24-bytes (192-bits) of strength for them. There are no answers to this, because it depends on what you want (the server resource usage, the level of security, etc). However, the "pepper" , which I refer to as the file-system key, should be a lot more stronger since it's generated once and does not really consume any CPU or memory and it plays a big role here.
batfastad wrote:3) Usernames. On some sites peoples login/usernames are their e-mail addresses and on others it's a separate string.
From a user point of view I always prefer it as my e-mail address (less stuff to remember). But I always feel its easier to recover your account (if your e-mail address changes) when you have to choose a separate username.
Are there any advantages/disadvantages to doing it either way?
From the perspective of security, it would be better to not use anything that is known, guessable or visible to other users as the login identifier. Email is just that. If they can choose a username, it's likely to be harder to crack into the account by brute force. If someone has gotten inside your database, then it obviously does not matter what you use as the "username" unless you hashed it, too. So, using a custom string, which is unknown to others, will certainly yield in a solution that is just as secure as the email approach or stronger, but not weaker. It depends a lot on the user, she's ultimately the one who creates her login credentials.
batfastad wrote:4) I've been investigating various cookies stored on my system and it seems Paypal stores my username (e-mail address) in a cookie and a few others I've found.
I know. The password is all that prevents people from using my credit card :? . I have to make sure my password is strong, which I believe is what all people do since it's a bank.
batfastad wrote:I was under the impression that I shouldn't store username or password on the machine as a cookie, only the user's session cookie. Is that correct?
That's the best thing to do.
batfastad wrote:If I stored username and the session cookie then that would give me a double check
That double check is useless. You can as well increase the strength of your session identifier to achieve the same effect. It is a more sophisticated and controlled approach.
batfastad wrote:5) I'm storing my password_hash and salt in a VARCHAR field in my DB. And the pepper is just a PHP variable.
Should I be doing it differently... storing as binary/encoding somehow?
Personally, I like having a BLOB column where I store the salt and the password hash directly in binary format. I see (and have) no reasons to store them in a human readable format (except that the password hash is already in base 16). If I needed to, I can fetch the salt and pack it into hex or other format I want. This also saves some CPU and disc space. However, it's up to you to decide how you do it.
batfastad wrote:6) Session ID forgery/throttling. So I'm storing IP addresses, a counter of forgery attempts, the last session id they tried in a table.
And every time a user visits a secured page I'm looking up their IP address in that DB and comparing the session ID they've sent with the last one tried by that IP. But how do I detect forgery attempts from that?
Do I also need to store a timestamp of the last forgery attempt? Or have the counter represent the number of forgery attempts within a certain time range?
You also need to check the number in your counter and set some sort of timestamp to keep track of the last attempt. There are a few different ways to make it work. It doesn't really matter much how you implement as long as it works as expected. One way would be to update the timestamp whenever there's an attempt that failed and the counter was not too high yet. If the counter was too high, don't update the timestamp, but deny all. If the counter is too high, but the timestamp is more than x hours/days in the past, then clear the counter and set it to 0 or 1 depending on whether the last attempt succeeded or not.

Re: PHP & MySQL site login, security concepts

Posted: Tue Nov 03, 2009 10:17 am
by timWebUK
This thread has got a lot of useful information in it, thanks.

I'm working on a log-in script from scratch and was wondering what is the best way to prevent brute-forcing? (How to throttle the log-in attempts?) Record the time of the last login, then compare with the current time (based on usernames)? IP addresses, etc?

Another quick question, I want to allow the user to display an image (avatar), by entering in the hyperlink location of their image and storing that in a database... how can I validate this to prevent CSRF/XSS (I'm a bit new to this) attacks?

Cheers.

Re: PHP & MySQL site login, security concepts

Posted: Mon Nov 09, 2009 6:42 am
by batfastad
kaisellgren wrote:Personally, I like having a BLOB column where I store the salt and the password hash directly in binary format. I see (and have) no reasons to store them in a human readable format (except that the password hash is already in base 16). If I needed to, I can fetch the salt and pack it into hex or other format I want. This also saves some CPU and disc space. However, it's up to you to decide how you do it.
Hi Kai

Storing the hashed password and new salt in 2 binary BLOB fields in my DB makes alot of sense.
However I'm having some trouble with this binary stuff. I've never used PHPs binary functions before (bin2hex and pack/unpack etc)

So this is the code I'm using to generate my dynamic salts, and my one-off file system key (pepper):

Code: Select all

if (($handle = @fopen('/dev/urandom', 'rb'))) {
    $rand = fread($handle, 64); // 8 is just an example that we might want to use, type 64 for to get 512 bits strong random
    fclose($handle);
}
Which outputs a binary value.

But in the case of my FS key (pepper), how can I store that in a PHP variable in my global config.php file?
Does that need to be stored in binary? Or should I store it as HEX/text and then convert to binary for the login when I need to re-hash the password?

Thanks, B

Re: PHP & MySQL site login, security concepts

Posted: Mon Nov 09, 2009 7:31 am
by kaisellgren
batfastad wrote:So this is the code I'm using to generate my dynamic salts, and my one-off file system key (pepper):

Code: Select all

if (($handle = @fopen('/dev/urandom', 'rb'))) {
    $rand = fread($handle, 64); // 8 is just an example that we might want to use, type 64 for to get 512 bits strong random
    fclose($handle);
}
Which outputs a binary value.

But in the case of my FS key (pepper), how can I store that in a PHP variable in my global config.php file?
Don't compress, don't hash, but encode it. Encoding is lossless and is reversible. It won't hurt your data. You can use base64 encoding, or just use (un)pack.
batfastad wrote:Does that need to be stored in binary?
Does not matter. It can be a separate binary file outside of the document root, or encoded inside a PHP source file.

This should shed some light.

Code: Select all

header('Content-type: text/plain; charset=ASCII');
 
// Generating poor random to demonstrate packing.
$random = '';
for ($a = 0;$a < 1024;$a++) // 1 kB of random data
{
 $random .= chr(rand(0,255));
}
 
// Let's have a look at our dirty binary data.
echo "$random\n\n";
 
// Let's unpack the binary data into hexadecimal base.
$hex = implode('',unpack('H*',$random)); // H = Hex, * = unpack all data.
 
// Let's echo the returned data.
echo "$hex\n\n";
 
// Did we lose anything during the process? Let's reverse this and output our results.
$binaryAgain = pack('H*',$hex);
echo $binaryAgain."\n\n";
 
// So do they match?
var_dump($random == $binaryAgain);

Re: PHP & MySQL site login, security concepts

Posted: Wed Nov 11, 2009 4:40 pm
by batfastad
Hi Kai... more questions I'm afraid! ;)

1) I've got it sort of working with binary but sometimes my password hash seems to get out of sync with the dynamic salt.
I get occasional SQL errors and I think this might be because of the way i'm inserting the binary into my DB... I'm not escaping for MySQL.
Previously I had this all set up and working with all the pass/salt hashes being entered as hex, so I didn't bother escaping those before they went to MySQL.
Should I now be running mysql_real_escape_string() on my binary hashes before they go into my query?
Or is there a different function I should use to escape binary?

2) This is more about general script and page structure.
On every page that I want to be protected, I'm planning to have the following at the very top:

Code: Select all

<?php
// MY PROTECTED PAGE
require_once('config.php');
require_once('auth.php');
 
// rest of stuff here for logged-in users only
The auth.php script does this:

Code: Select all

if( $_SESSION['auth'] == 1) {
    // logged in, get user data from MySQL by looking up session id in the DB
    // do some other checks (subscription expire date etc)
} else {
    // not logged in, redirect to login form
    //possibly store the requested page in session to redirect after login
    header('Location: login.php');
}
So if the authorisation checks in auth.php fail, then the user is redirected to login.php before the rest of the "parent" protected script runs.
Is this the best way of doing things?
Or should I be adopting a different script structure that's more secure?

Cheers, B

Re: PHP & MySQL site login, security concepts

Posted: Thu Nov 12, 2009 2:13 am
by VladSun
You should always use exit() after a header() call.

Re: PHP & MySQL site login, security concepts

Posted: Thu Nov 12, 2009 2:28 am
by batfastad
Gotcha. So adding exit() my script structure would be good enough?
And I assume you mean only after a header('Location: ...'); call

What about this binary thing? How should I sanitise binary data before it goes to MySQL?
Just mysql_real_escape_string()?

Cheers, B

Re: PHP & MySQL site login, security concepts

Posted: Thu Nov 12, 2009 2:30 am
by VladSun
batfastad wrote:Gotcha. So adding exit() my script structure would be good enough?
And I assume you mean only after a header('Location: ...'); call
Yes, your assumption is right ;)
batfastad wrote:What about this binary thing? How should I sanitise binary data before it goes to MySQL?
Just mysql_real_escape_string()?
Using mysql_real_escape_string() should be fine.

Re: PHP & MySQL site login, security concepts

Posted: Thu Nov 12, 2009 5:21 am
by kaisellgren
batfastad wrote:I'm not escaping for MySQL.
Whenever you pass data into your database, escape it before doing so. It does not matter what type it is, a string, an integer, or some binary data. No data should leave your PHP application and enter MySQL without escaping it first. That's likely the problem you are facing. The binary data gets corrupted, and causes SQL errors.

And like Vladsun already said, you should stop the execution after the header() -call.

Re: PHP & MySQL site login, security concepts

Posted: Thu Nov 12, 2009 5:52 am
by batfastad
Yeah absolutely. I wasn't sure if there was a different function I should be using for binary data though.
Ok looks like I'm set for the time being! I think I've got a question brewing about session management though at some point ;)
Thanks for all the help/advice (so far)

EDIT: Is there any advantage to changing the session.hash_bits_per_character in php.ini from 4 (default) to 5?
Does that make the session ID any stronger?

Cheers, B

Re: PHP & MySQL site login, security concepts

Posted: Fri Nov 13, 2009 12:46 am
by kaisellgren
batfastad wrote:EDIT: Is there any advantage to changing the session.hash_bits_per_character in php.ini from 4 (default) to 5?
Does that make the session ID any stronger?
It does not affect the strength.

Re: PHP & MySQL site login, security concepts

Posted: Fri Nov 13, 2009 5:14 am
by timWebUK
timWebUK wrote:This thread has got a lot of useful information in it, thanks.

I'm working on a log-in script from scratch and was wondering what is the best way to prevent brute-forcing? (How to throttle the log-in attempts?) Record the time of the last login, then compare with the current time (based on usernames)? IP addresses, etc?

Another quick question, I want to allow the user to display an image (avatar), by entering in the hyperlink location of their image and storing that in a database... how can I validate this to prevent CSRF/XSS (I'm a bit new to this) attacks?

Cheers.
Hey, hate to bump or anything, but any suggestions please would be great. Quite important.

Re: PHP & MySQL site login, security concepts

Posted: Fri Nov 13, 2009 12:09 pm
by kaisellgren
timWebUK wrote:
timWebUK wrote:This thread has got a lot of useful information in it, thanks.

I'm working on a log-in script from scratch and was wondering what is the best way to prevent brute-forcing? (How to throttle the log-in attempts?) Record the time of the last login, then compare with the current time (based on usernames)? IP addresses, etc?

Another quick question, I want to allow the user to display an image (avatar), by entering in the hyperlink location of their image and storing that in a database... how can I validate this to prevent CSRF/XSS (I'm a bit new to this) attacks?

Cheers.
Hey, hate to bump or anything, but any suggestions please would be great. Quite important.
Keep count of the number of attempts, the IP address and the date of the last attempt.

I'm not sure what you want in your second question. Does the displayed image come from your database? Or directly from another website?