Making secure Log-In

What things should I do to make my Log-In script secure?

There must be an established list of DO’s and DO NOT’s out there since this is such a common thing on websites.

I’m always worried that something I do programatically could put people’s information at risk?! :-/

Thanks,

Debbie

Test every single input as being within your accepted range, reject anything outside your accepted range.
Reject any character that could be used in an sql injection attack. Things like = or ; or ’ or " can be used to do bad things.

Okay, but what about things like how I store the Log-In Credentials in the database?

Or how I keep track of who is logged in? (This on worries me the most.)

Debbie

Debbie,

Log-in credentials:
Store most of it in the database in readable format (name, user-name, email address, etc.).
Store the password encrypted with a salt added. The code I use to store the password is:

$query = "UPDATE users_tbl SET pass=SHA(CONCAT('$p','salt')) WHERE user_id='$u'"

(use your own word in place of ‘salt’.

Keeping track of who is on your site is beyond my knowledge.

Sanitizing the form inputs is important since that is how SQL injection compromises your database. Cups has been a great help to me with making sure my inputs are sanitized.

Store the password in the database in an encrypted format preferably using a randomly-generated password salt. Something along the lines of: When the user registers, a four character (or longer) random salt is created using one of PHP’s randomization functions. This salt is then concatenated to the user’s password then run through one of PHP’s encryption functions such as hash() or crypt() then stored in the database. When the user logs in, the password supplied has the salt concatenated to it then encrypted using the same function then compared to the value in the database. If it matches, the password is correct.

Do not store any more information in the user’s cookie than you have to. Usually, the session ID alone is sufficient. On the server, the session information will be stored in the session file or in the database, again with as little information as required, such as the session ID and the user’s ID if logged in. If the user is logged in, a logged in flag can be set to true or a field with the user’s ID could be supplied with the user’s ID number. If the user is not logged in, the default will be zero.

If you want an idea, you could always look at the way some popular CMSes do it. I looked at how phpBB does it and made mine (currently unfinished) similar, though not with as ridiculous of a password hashing mechanism as phpBB uses.

Thanks CheeseDude!

What value does the Salt provide?

This salt is then concatenated to the user’s password then run through one of PHP’s encryption functions such as hash() or crypt() then stored in the database.

Which PHP encryption algorithm is the best? (I hear some have been hacked…)

When the user logs in, the password supplied has the salt concatenated to it then encrypted using the same function then compared to the value in the database. If it matches, the password is correct.

So everyone has the same salt?

Or can you “randomize” the salt?

Do not store any more information in the user’s cookie than you have to. Usually, the session ID alone is sufficient.

Why is that?

BTW, how secure is using a session? (I have this unexplainable fear that it can be hacked like a cookie or file…)

On the server, the session information will be stored in the session file or in the database, again with as little information as required, such as the session ID and the user’s ID if logged in.

Realistically, how much extra security is added by storing the session in your database?

And don’t all of those calls to the database really slow down your website?

If the user is logged in, a logged in flag can be set to true or a field with the user’s ID could be supplied with the user’s ID number. If the user is not logged in, the default will be zero.

Isn’t that pretty easy to “hack”?

If you want an idea, you could always look at the way some popular CMSes do it. I looked at how phpBB does it and made mine (currently unfinished) similar, though not with as ridiculous of a password hashing mechanism as phpBB uses.

Is there code available to view?

And lastly (for now), how hard is it to come up with an Authentication Module that is pretty much impenetrable?! (For instance, let’s say I want to be able to store Customer Profiles, Preferences, Order Information - just NOT Credit Cards… Is that something that I could reasonably do myself with some help from the SitePoint gurus?!) :-/

Thanks,

Debbie

Hi Debbie,

You know such a list would be endless, and it would need to be updated and reviewed each time a new hack/hole/secure issue is found with PHP and/or MySQL (this can be DAILY!).

Its not just about passwords and hashing them (or encrypting, as they should be) there are lots of other important issues such as:

  • preventing brute force and dictionary attacks (http://en.wikipedia.org/wiki/Dictionary_attack)
  • prevent members from sharing passwords in forums
    (both the above will/can cause major problems for your host: due to high bandwidth/server usage)
  • correct and thorough input data validation
  • etc…

For these reason many designers will often successfully use a third party application like WMM (http://www.websitemembermanager.com/) or one of the many other website member management applications (http://uk.search.yahoo.com/search?p=php+website+members+management&).

If you do still decide to go the self-development route then ask your host what they would recommend

All the best, and good luck

Dave

It makes it a little more difficult to brute-force the password. You can see this for a clearer idea:

The benefit provided by using a salted password is making a lookup table assisted dictionary attack against the stored values impractical, provided the salt is large enough. That is, an attacker would not be able to create a precomputed lookup table (i.e. a rainbow table) of hashed values (password + salt), because it would take too much space. A simple dictionary attack is still very possible, although much slower since it cannot be precomputed.

You will get differing opinions on that. I’m sure you will see some differing opinions in answers provided to you about ever part of this. Some are still using md5(). PHP recommends:

http://us3.php.net/manual/en/faq.passwords.php#faq.passwords.fasthash

Why are common hashing functions such as md5() and sha1() unsuitable for passwords?

Hashing algorithms such as MD5, SHA1 and SHA256 are designed to be very fast and efficient. With modern techniques and computer equipment, it has become trivial to “brute force” the output of these algorithms, in order to determine the original input.

Because of how quickly a modern computer can “reverse” these hashing algorithms, many security professionals strongly suggest against their use for password hashing.

How should I hash my passwords, if the common hash functions are not suitable?

When hashing passwords, the two most important considerations are the computational expense, and the salt. The more computationally expensive the hashing algorithm, the longer it will take to brute force its output.

There are two functions that are bundled with PHP that can perform hashing using a specified algorithm.

The first hashing function is crypt(), which natively supports several hashing algorithms. When using this function, you are guaranteed that the algorithm you select is available, as PHP contains native implementations of each supported algorithm, in case one or more are not supported by your system.

The second hashing function is hash(), which supports many more algorithms and variants than crypt(), but does not support some algorithms that crypt() does. The Hash extension is bundled with PHP, but can be disabled during compile-time, so it is not guaranteed to be available, while crypt() is, being in the PHP core.

The suggested algorithm to use when hashing passwords is Blowfish, as it is significantly more computationally expensive than MD5 or SHA1, while still being scalable.

No, users do not have the same password salt. The salt is randomly generated when the user registers. There are a number of ways of generating a random string of alphanumeric characters of a length you specify. I don’t recall what I did. You can search the internet for more information and probably get a function you can use.

I believe I saw some examples where a new salt is generated periodically, such as on every successful login.

A session can be compromised by someone using a computer previously used by someone logged in to your site. Browser cookies can be stolen, from what I’ve read. And on many servers session data is stored in files in the server’s temporary folder meaning that it is possible–although highly unlikely–for someone to steal session information.

You can on many web hosts specify a folder in your own account where session files will be stored. Or you can stash it in a database. Storing session data in the database is potentially more secure because an attacker would need to know your database password to access it whereas gaining access to the server’s temporary folder may not be as difficult. But if someone compromises your hosting account and accesses your PHP files where your database password is located, that goes out the window.

Storing session data in the database also enables you to scale your system if you ever needed to deploy a database cluster. Sure, there is a little overhead every time a visitor needs to have session information stored or retrieved, which could be on every pageview. But there is also overhead in storing the session in flat files on the server because the server has to seek and read the session file on every pageview. On a good webhost a query can be executed in a few thousandths or hundredths of a second. So it isn’t going to slow your website much, probably not even noticeably.

Storing the session in the database will also enable you to do some analytic things like determine the number of registered users or guests viewing your site currently, the pages they have viewed, their total number of pageviews, etc.

Assigning a new session ID on every pageview can help make things more secure because session IDs will only be valid for one pageview.

If you want to view phpBB’s session and user managment, download the script and look in the /includes/session.php file.

Yes, I think you could reasonably do something like this yourself.

Something to consider: make your login form be accessible only when using an SSL connection.
If you don’t, bad people will be able to sniff the connection and read the password (which is sent plain-text).

CheeseDude,

At work and gave your response a quick read for now.

Thanks for the help so far!!

I don’t follow you.

If my password=‘Debbie’ and I use something like MD5() to hash it, then it may be stored as ‘3nRT75’

So when I log in and type ‘Debbie’ the log-in script runs it through MD5 and converts it to ‘3nRT75’.

And ‘3nRT75’ = ‘3nRT75’ so I get in.

By contrast…

If a random salt is used, say, ‘1234’ and it is added to my ‘Debbie’ before running it through MD5, then on the tail end how would my log-in script know that the salt was ‘1234’ when it appends that to ‘Debbie’ when I go to log in?! :-/

If you kept changing the Salt but didn’t keep track of which Salt was used for which hash, you’d never be able to make a comparison work…


Also, if the Salt is made public, then that sorta seems to defeat the purpose of using a Salt, right?

Thanks,

Debbie

The salt is stored in the database, in its own column, and each user will have a different value.
You can compare them directly in the SQL SELECT query.


$supplied_pw = mysql_real_escape_string( trim('Debbie') );

$query = "
  SELECT * FROM `users` 
  WHERE username='my-username' 
  AND `password` = SHA1( concat('$supplied_password', salt) )
";

In this example salt is the name of the DB field, hence no quotes around it.
MySQL will join the typed password from the user to the salt stored in the DB, run it through SHA1 (better than MD5) and return rows where they match.

The salt doesn’t change for a given user.
So if a user changes their password, you’ll need to use that same salt to generate the new hash (again, could be done directly by MySQL).

Storing the salt is not the same as making it public. And the salt is no where near as private as the password. Even if an attacker knows the salt they’d have to generate a whole new rainbow table for every user in your DB (because ever user has a different salt).

Good answer!

I get it now.

Thanks,

Debbie

Hope you guys hang around, because maybe over the next several days I can actually try to set up a database and some php to make your examples a working reality…

Debbie

cranial-bore - that solution uses the OLD MySQL extension which will eventually be deprocated in the newer versions of PHP. If you do not switch to using either MySQLi or PHP PDO then you are creating a WHOLE lot of work for yourself in the near future.

I would stongly suggest anyone to NOT use the old MySQL extension, and instead save yourself the headache later by learning and using the newer extensions: MySQLi or PHP PDO (more here with examples: http://www.seiretto.com/news/PHP-MySQLi-or-PDO-the-old-MySQL-extension-will-become-obsolete.php)

This is exactly the situation I attempted to highlight in my first post - old code (only a few years old) will fail to work with newer PHP versions and you can find yourself endlessly patching your code, and belive me after over a decade of coding in PHP becomes tiresome and time consuming.

I use the OO MySQLi myself, but for some reason still use the old procedural functions for examples. I guess I think of it as a pseudo code way of saying “escape this value, run a query that looks like this here”.

I should stop doing that.
PDO is probably great, but may not be the best for a lot of forum questions. If the asker’s question isn’t centered around the DB it could just add a layer of complexity to the answer.