I’ve given myself a quest, a quest to find the most secure method to get a user to register, and login to a site, and make sure it’s still them as they explore. This is both surprisingly simple, just complicated because of the amount of steps involved, but depending on who you host with, could also get quite expensive. More on that later. Now? Onto the technical jargon.
Setup
The first thing that you need to realize is that having a secure site, is not just a one stop shop. You can’t just put up a login form on your site, without any amenities and expect it to be suddenly the most secure thing in the world ever. There’s certain things you have to do, and certain things you have to worry about, and most importantly: Certain things you have to realize.
The first thing on that list is this: Everything is hackable. There is nothing so secure that it is non-hackable.
The second thing on this list: There’s a multitude of things you can do to make a website more secure than just having a plain jane login form.
Now that said, everything is hackable, yes, but here’s the key: It costs money and time to hack someone. If you have a plain jane login form, that cost is going to be minimal. If you want to put a value on it, it’ll be less than US$0.01.
So the question is, what can we do, to make that cost go up? Well, let us start from the beginning.
First step? Don’t use flat files. What I mean by this, is when a user registers for your site, don’t store those sensitive details into a file that’s easily readable to the entire freaking internet. Move to a database system. The most common one for websites is MySQL, and there are plenty other options available to you, all of which work with PHP, and the MS version also works with ASP.NET as well, if that’s your style.
After that, the most simplest and easy thing to do, is correctly protect your database. It is the dumbest thing ever, to use the root user as the main user on your database. For MySQL this user is called “root”, for Oracle it’s “hr”, and for MSSQL it’s “dbo”. Create a user that has a name that you’ll remember, with a strong enough password and you’ve already taken another step to improved security.
Note: If you’re making a website, and your host refuses to grant you more than one user to your websites database under the pretensions of that you won’t need more than one, (which I have seen before with this company), leave them. Up and leave. It is one thing to have a user that is not THEIR root user (which is secure for them), it is another completely to have a user that is not YOUR root user (which is secure for you).
User Registration
The next step is to do something with the password after you get it server side. I’m not versed in the ways of ASP.NET, Python, or Perl so any code that I show from here on out will be strictly PHP, or a conglomeration of HTML and Javascript. What I mean by “doing something with the password” is that the biggest mistake that people make when originally designing these things is that they think “OH! It’s in a database, there’s nothing they can do about it!” well sadly there is. If you don’t have a secure enough password to your database, and don’t fiddle with that password that the user has, then they just got access to everyones’ accounts, and instead of having to dawdle around with SQL code, they can just go “Ok, this guy’s an admin. I’ll log in as him!” and then use the regular web portal that you set up to wreak havoc. Remember, it’s all about costs and the less they have to deal with to get the job done as quickly as possible the better for them, the worse for you. Or maybe they were simply looking to just have some fun, or (and this has happened to me) log in as an admin to your site, and read messages they were not originally supposed to read in the first place, if you don’t do something with that password, it’ll become all the much easier for them to get to their ends, and in a format that they can read.
Now I told you that really really long paragraph to get to this point: When I say “mess with the password” I don’t mean change it to something that you decided while high and eating kitty litter every single time. I mean to hash it. For those of you who don’t want to go to wikipedia, the short short version is change it in a predictable pattern to resemble something it was never close to resembling in it’s original form. The most common way right now, is to use something called the MD5 hash. Now sadly, in PHP this is also one of the least secure options you can use. In general what you would do (using some pseudo-code here) is this:
< ?php $password = $_GET['password']; $password = md5( $password ); ?> |
And then you’d store $password in the database (upon registration) or check it against the database (upon login). The fact that a hash is used in the first place, is a testament to why you cannot recover a password at any worthwhile site that takes it’s security seriously. You always always have to ask to get a one time password to get back in (which there’s another post completely in this line), and from there change it back to another secure password that you can think of through the user control panel or whatever. Now when I mentioned that MD5 was one of the least secure hashing algorithms in use by PHP today, I meant it. The only one that I can figure as being any less secure is it’s predecessor, MD4. To fix this, you can do a few things. PHP actually has a hash function that can take a string and then digest it into a more secure hash. My personal favourite, the ISO standard: whirlpool.
$password = $_GET['password']; $password = hash( "whirlpool", $password ); |
Logging In
If there are any of you out there who are at least slightly experienced with PHP, or connecting a backend to a webpage at all, you’d have realized that in the previous code I’m passing information in the least secure way possible. The good news? There’s ways to fix this.
The first step in THIS part, is to change from using the $_GET variable to using the $_POST variable. Now for those who don’t know, anything in the $_GET variable is visible in the actual url bar of your browser. To see an example to a quick google search. Now look at the URL. The part of it after “search” (specifically the “?q=blah+blah+blah” part) that is called a “request string”, and is put there when you set up your login form to use the “GET” method:
<form method="get"> ... </form> |
To secure this just a tad bit more send it over post, and viola, no more accidental findings of what your password is, if someone looks through your history (which is becoming harder and harder to avoid with browsers remembering where you’ve been and at what addresses).
<form method="post"> ... </form> |
So let’s review. You’re using a database, you’re hashing the password, you’re not using GET anymore. SEXY! You’re secure! Well not really. If your website is worth it, there will be programs called “port sniffers” that will lock onto the http port of your server (normally “80″) and analyze everything that goes in and out of it. Without any further modification to your form, everything will be sent in what is called “plain text format”. This means that those sniffers on your server can see everything and anything you send from that form to the server. To not get it sent in plain text format can actually become quite costly. I know for my host it’s $45 a year, plus an extra $30 one time charge to get a digital signature added to my site that says “You can connect to this site securely and nothing will be sent over plain text.” And that’s pretty much the cheapest you can get, with any amount of quality, and trust for the user.
So, to battle this, we do something called “client side encryption”. Is it still sent over as plain text? Yeah, but at least people won’t be able to get the plain vanilla password that you used to login with. And again, we use my favourite hashing algorithm: whirlpool in a javascript implementation.
The way this portion will work, is that the form, instead of just having two text boxes and a button, will have an additional hidden element, which you’ll actually use to send the encrypted password.
(The following code is using the javascript library jquery to do it’s job).
$("#login_form").submit( function() { pass = HexWhirlpool( $("#pass").val() ); $("#pass").val( "" ); //We clear it out, because we don't actually want to send it. $("#hidden").val( pass ); //We want to send it in it's encrypted form instead. }); |
Excellent! So now we’re sending things all encrypted like, sure it’s over plain text but now they at least don’t know what the password is! Feeling like a few little black ops hackers working for the CIA or something right?
Well I can’t vouch for how much they know about this subject, but for us personally we have at least one or two more steps to go before we can call us “commercially secure”.
Of these the first one we want to look at, is implementing a salt. Again, for those who don’t like wikipedia, the short short version: A salt randomizes the hash of the password by stapling to the front, end, or somewhere in the middle, a secret (known only to you) phrase or series of bits and bytes to the password, adding essentially another layer of complexity that any hackers who want to go through your hash and try and return it to it’s original value, will have to deal with. Corresponding to the name, it adds a bit of sourness to the hacker’s attempts at getting in.
But, you may ask, how will this work? Upon install of your login controller, or your CMS, or whatever web software you’re using, you will (or should) be asked to create a salt. This salt, is known only to you, and is completely secret. This salt, is then used in conjunction with hashing the password. For this, there have been conflicting reports of using it more than once is more secure than just using it once, and putting it on the front isn’t less secure than putting it in the back or in the middle. However, this is more of a general way of doing it.
Staying Logged In
The easiest way to do this, is to create a cookie that expires after x amount of days, and that cookie holds data, such as the user id, and a random hash of some kind. However there’s some extra steps we can take, to make sure that the user’s session doesn’t get spoofed easily using a Cross Site Scripting attack. First things first, however.
Creating the data that the user’s cookie holds is a relatively simple matter, and to do this the proper way can actually help prevent spoofing attacks. In general, you’ll need to use a hashing algorithm first, and then get something random to hash. The easiest thing to hash that’s random is, well, a random number. However, there is only a set amount of random numbers you can generate and eventually you run the risk of a collision. So, you’ll need something to make it unique. If you’re database is set up correctly, you can use one of two things: a user name, or a user id, and use that to salt the random number. That way, if two people log in at the same time, and get the same random number (which is surprisingly possible for very high traffic sites), the uniqueness of the name, or the id, will help keep the hash different.
< ?php mt_srand(); //Self seeds if one isn't specified. $random = mt_rand(); //Get's a random number from the better random number generator in PHP. $salt = $user->getUsername( ); $hash = hash( "whirlpool", $salt . $random ); ?> |
Then we do two things: One, we store the resulting hash into the database (to keep it on record for the next time the user comes a-clicking around your site) , and two set it as your hash for the user in his cookies.
< ?php define( "SITE_DOMAIN", "localhost" ); //This should be done in a separate file. $expire = time() + (7 * 24 * 60 * 60); //Expires in one week. set_cookie( "hash", $hash, $expire, "/", SITE_DOMAIN, false, true ); ?> |
The set_cookie function in PHP has a feature that was added in 5.2.0, and that is the httponly parameter (the last one on the list), which in simple terms helps prevent the XSS attacks that have been mentioned, be not allowing the cookie to be accessed through javascript. The sad thing is, not all browsers support this option, and I don’t know which ones do, so you’ll have to hope that some time in the near future it’ll be supported in all major browsers. Along with the hash cookie, it’s also a smart idea to set the user name as a cookie data peice as well, just so you can confirm that the hash belongs to the user who set it.
Now that we have the hash confirmed to the user, another security feature would be to change the hash with each confirmation of who the user is. And that basically means every time a page is loaded, if that user stays logged in, get a new random number, salt it with the name or id of the user, hash it, and then set it as the new hash for the cookie. This way one hash doesn’t stay set for long until the user leaves, so while the user is active on the site, they keep themselves secure, and the person trying to hack you, needs to start taking guesses and stalking users. The important thing would be to keep in mind is that the people with the power stay constantly active and keep the hash constantly being regenerated. At least, one would hope it’s like that.
Review
I realize the post was long, as there was a lot of explanation for everything in it. So if your brain melted while reading that, here’s a short list of the things to cover to keep yourself secure (in order as mentioned in the post):
- Use a real database, not a flatfile
- Protect your database with a user that isn’t root, and a password that’s actually complicated
- Use POST and not GET over your login form
- If you can afford to, get SSL enabled on your site, and send all login information through that.
- Hash the user’s password client side, before sending it server side.
- Salt and re-hash the password before storing it into, or checking it from, the database
- Create a unique hash for the user every time they load up a page, and store it into their cookies.
Finale
With this information in hand, you’re well on your way to keeping yourself secure from most web attacks. There are probably more ways out there that I didn’t cover here, and there’s also probably ways to get around even the rules I laid down here today that I didn’t research. All in all, if you care about not letting people in who aren’t supposed to, then make sure you take the appropriate level of security seriously.
Way cool! Some very valid points! I appreciate you writing this
article and the rest of the website is very good.
I have been exploring for a bit for any high quality articles or
weblog posts on this sort of space . Exploring in Yahoo I ultimately stumbled upon
this web site. Reading this information So i’m glad to show that I’ve an incredibly excellent
uncanny feeling I found out exactly what I needed.
I such a lot indubitably will make certain to don?
t omit this web site and provides it a look on a continuing basis.
Somebody essentially lend a hand to make severely posts I would
state. This is the first time I frequented your web page and
up to now? I amazed with the research you made to create this
particular publish amazing. Wonderful process!
Please let me know if you’re looking for a writer for your weblog. You have some really great articles and I think I would be a good asset. If you ever want to take some of the load off, I’d really like to
write some content for your blog in exchange for a link back to mine.
Please blast me an email if interested. Thanks!
My partner and I stumbled over here different web page
and thought I might check things out. I like what
I see so now i’m following you. Look forward to looking over your web page yet again.