How to make your php scripts secure

mconslau

New Member
Messages
6
Reaction score
0
Points
1
-An introduction into making your sites secure-
Written by Joshcamas

Hello all!
I've been learning quite a bit about php and security this past week, and wanted to share my knowledge. Keep in mind that this is not a tutorial on how to make a login system and such, and not how to code it, but just introduces how hackers do their shiz.

Security is a pain. This day in age you can download a 1.5 billion-word dictionary for free, simply letting you press a button to get into systems. But there are ways to make their job much harder, and here are a few of these ways.

1: Hash your passwords
When facebook saves their passwords to their database, they hash it before hand. A hash is when you get a password and turn it into gobbilygook:

'password123' --> '77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f'

The thing is, you can't turn the hash back into the password!

77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f' --X--> 'password123'

So how would you be able to check if the user's password they typed in the login-screen is right? You'd hash the input, then compare!

'entered password'--> '77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f'
'real password' --> '77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f'
'77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f' = 77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f'!!!! The password is correct!!


2: Salt your passwords
There is a problem though. If the hacker got the hash ('77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f' in this case), they could guess a password:

'guessed password (s3cr3t)'--> '13d653f6as2dhj2233230aa4b5046asdh28d20ae736bahejk237'
'real password' --> '77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f'
'77d656f673ff58e3f8b0adb904b5046ab8a120ae736ba3b60f' X= 13d653f6as2dhj2233230aa4b5046asdh28d20ae736bahejk237'
! WRONG PASSWORD

This is fine, unless the real password sucks, like 'password123'. The hacker knows a lot of people do this, and would guess this, and get in! (Note: He can try unlimited times because he has the hash on his own computer, so locking the login after a certain number of tries would not work!)
But there is a way to fix this! Using salts!

A salt is basically a random string. This lovely string is added to the password before it's hashed to make it more hard to guess:

salt = 'hk13298sdk1230f8s09df8'
'password123.salt --> ''asdhkj1237jk12kd789q343f8b0adb904b5046abdhk123yif'


That means that the hacker wouldn't know the salt, and would have to guess 'password123.hk13298sdk1230f8s09df8' which is much harder to guess than simply password123!!!

3: Stop MySql Injections
If you're using a Mysql database for saving anything, you'll need to know about Injections.
MySql works by sending something to the database. Example:

"SELECT * FROM users WHERE username = '$name'"

Now this is fine and dandy as long as $name is something like "joshcamas" or "justinbieber4life".
But what if it was... "'; DELETE FROM users WHERE 1 or username = '"!? What would happen?
Oh, nothing. Nothing except Delete the entire users table!!!!! Now thats a slight problem.
Dis bad! How fix? Its a quite easy fix, surprisingly enough. There are two ways to do it.
1). You simply replace the values "[\'\")(;|`,<>]" with "". That would turn that ";" into "" in this case.
2). You run $data = mysql_real_escape_string(trim($data), $connection). This would turn ";" into "\;", which would make MySql understand that its not part of the command.​
You can use whatever you please, but each one works a little differently.

4: Stop Cross-site scripting attacks
A XSS attack is kinda like a mysql injection - sending bad stuff for the server to run. If I remember correctly, this actually happened to MySpace once!
So lets take the example from myspace, shall we? Say a hacker has an account. This lovely account allows you to have a status. Yippy! Thats fine and dandy when the status is "Just killed myself" or "I hate grandpas". But what if... it was javascript?
This happened when a guy added some javascript to his status - basically he made the code automatically add the person looking at his status 1) friend him, and 2) turn their status into the malicious code. Oh no! In just a few hours, millions of people had this terrible status! (Note: I could be wrong about the setting - its possible that this never happened to MySpace, but this could happen to you)
So how to stop it? Right before you save the data, you can run it through strip_tags(), which removes all those yucky "<"'s and ">"'s and such. That would make the code stop working. You can then run it through htmlentities(), which turns special characters into what they should be. (Just like mysql_real_escape_string).

5: Never trust the client: Do everything on the server
Say you're building a register page, and want some fancy javascript that says "You need at least one symbol" when the user's password doesn't have a symbol. This is fine, and I would say you're on the right path. But you can't stop there. Javascript is run on the client's side, and that means the user can edit said javascript to do his bidding. If he simply made it so it didn't give the warning, *boom*. He just made a password without a symbol.
To fix this you need to remember to do everything on the server, which would mean in PHP. In other words, do the checks server-side as well! If you're checking to see if someone did the captcha correctly in javascript, do it in php as well! Remember this, and remember this well: The client is in the hands of the enemy.


I hope this post at least introduced the crazyness of making sure your code doesn't let nasty hackers into it :)
 
Last edited:

bdistler

Well-Known Member
Prime Account
Messages
3,534
Reaction score
196
Points
63
I've been learning quite a bit about php and security this past week...
IMO...
the amount you know about "How to make your php scripts secure" after just one week of study vs. the the amount you need - you can put in your eye and still see out of it
nobody should write their own "Security" scripts
you should use "Open Source" scripts that are keep up to date with current updates and have "peer reviews"

...But you can be free of this fear as long as you follow these tips.
here is a link to just one open source PHP script [contact form program ] that has "peer reviews"
[ http://www.fastsecurecontactform.com/download-php-script ]
down load size is 780.6 kB (780,613 bytes)

down load it - then review its security with what you know - from your one week study of "How to make your php scripts secure"
after your review be sure to tell Mike Challis how he can make the security tighter in his program
 

mconslau

New Member
Messages
6
Reaction score
0
Points
1
IMO...
nobody should write their own "Security" scripts
When I said this I meant for simply saving a password to a database. Does this mean you do not agree with what I said above? That you should not hash and salt passwords? Instead of simply copy-pasting scripts, how about learning how hackers get into your stuff? If someone wants to program their own ANYTHING in php, they'll need to know how to do security, not just grab some secure script! I personally enjoy how a system works, not simply being ignorant.

I am truly sorry about posting something here. I didn't realise you had to be an expert to teach someone some simple information.
 
Last edited:

leafypiggy

Manager of Pens and Office Supplies
Staff member
Messages
3,819
Reaction score
163
Points
63
Hashing and salting is great, but the password_hash() functions are definitely the way to go for hashing in PHP right now:)

It's a good tutorial! :)

bdislter is right in some respects. Don't reinvent the wheel if you don't have to, especially when it's something you can use that you know is secure.
 

mconslau

New Member
Messages
6
Reaction score
0
Points
1
Updated post.
IMO the issue here is - PHP security is not "simple"
Yes but the few tips I showed was simple. People need to start somewhere, yes?

...But you can be free of this fear as long as you follow these tips.
Yea I agree that was a mistake on my part.
Hashing and salting is great, but the password_hash() functions are definitely the way to go for hashing in PHP right now
Now thats something that's actually useful to say! Thanks, I'll look into it!

EDIT: After looking at the manual, I'm confused. It says password_hash() creates a salt, but how would password_verify() be able to grab said salt?
 
Last edited:

leafypiggy

Manager of Pens and Office Supplies
Staff member
Messages
3,819
Reaction score
163
Points
63
password_hash() creates a password. When you use password_verify(), you psas it two things. Firstly, the plaintext password. Then, you pass it the stored hash you created with password_hash(). Internally, it deconstructs the second paramater to see how it was hashed, and then hashes the first parameter in the same way, then compares the two.

From the PHP docs:

Note that password_hash() returns the algorithm, cost and salt as part of the returned hash. Therefore, all information that's needed to verify the hash is included in it. This allows the verify function to verify the hash without needing separate storage for the salt or algorithm information.
 

mconslau

New Member
Messages
6
Reaction score
0
Points
1
password_hash() creates a password. When you use password_verify(), you psas it two things. Firstly, the plaintext password. Then, you pass it the stored hash you created with password_hash(). Internally, it deconstructs the second paramater to see how it was hashed, and then hashes the first parameter in the same way, then compares the two.
What do you mean "deconstructs"? If you can deconstruct a hash, isn't that kinda bad?
 

leafypiggy

Manager of Pens and Office Supplies
Staff member
Messages
3,819
Reaction score
163
Points
63
Note that password_hash() returns the algorithm, cost and salt as part of the returned hash. Therefore, all information that's needed to verify the hash is included in it. This allows the verify function to verify the hash without needing separate storage for the salt or algorithm information.

From the docs.
 

essellar

Community Advocate
Community Support
Messages
3,295
Reaction score
227
Points
63
Not a problem. Security is one of the hard parts of programming/computer science, and lots of very, very smart people with impressive credentials and resumes/CVs have gotten things very wrong over the years. The trick is to never trust your own judgement (always get community verification of everything), never try to "roll your own" (which you haven't done here) — even if you're a real expert hip-deep in the math, you need to go back to that "community verification" thing to make sure you haven't inadvertently left gaping holes all over the place — and keep up with best practices (the part you missed). Ten years ago you would have been praised for this tutorial; today it's horribly out of date.

The returned value of password_hash($password_in, PASSWORD_DEFAULT) looks something like this: $2y$10$AeWih9PGlBNhv6pBrE51GO5mgFLC1EonogYyNoiJ0uOKYtM9veBJC

The "$2y" at the start means "I used the BCRYPT algorithm".

The "$10" is the cost factor.

The required salt for the algorithm is a known length, which varies according to the key-stretching function you're using¹ (and it is generated automatically using mcrypt_create_iv if you don't supply a salt value to the password_hash function) and it takes up the first X number of bytes in the value that follows the third dollar sign. So all of the information that you need to hash the user-supplied password exactly the same way as the stored password is available in the stored value.

The default algorithm and cost (right now) for password_hash($password, PASSWORD_DEFAULT) is BCRYPT with a cost factor of 10. If you leave it at PASSWORD_DEFAULT and use the password_needs_rehash() function, you can re-hash the user password/passphrase (at verification, since that's the only time you'll have the password in plain text) when the defaults change (which will happen in future versions of PHP).

PHP:
if (strlen($password_in) > 64) {
   $password_in = hash("sha256", $password_in);
}
if (password_verify($password_in, $stored_password)) {
   // code to mark the user as logged in goes here
   if (password_needs_rehash($stored_password, PASSWORD_DEFAULT)) {
      // the defaults may have changed since the last time the password was hashed
      $new_hash = password_hash($password_in, PASSWORD_DEFAULT);
      // code to store new hash in place of the old in the database/whatever goes here
   }
}

Do note, though, that BCRYPT can only handle a 72-byte password/passphrase string, and you may not want to put an upper limit on the length of the password/passphrase the user can use. In that case, use ONE RUN of SHA-256 the plain text password before feeding it to the password functions. (You don't have to, or want to, salt that hash. Not only will it be salted later anyway, but using a salt here will increase the likelihood of hash collisions — two inputs resulting in the same output. Collisions are unavoidable when the input space — the number of possible input values — is larger than the output space, and you're using this hash to safely shorten the apparent length of a string to 64 characters. Since there are many more possible strings longer than 64 characters than there are strings that are exactly 64 characters long, there will be collisions. Salting just makes it worse.)

I know this is a lot to go through, but I hope it's been some help.
______
¹ A key-stretching, or key-derivation, function is just like a crypto hash function, except that it's deliberately slower and much more "expensive" to run. It's "the same" in the sense that it always returns a value of the same length, that the same value n always results in the same value out, that small changes to the input make large changes to the output, and there's no way to reverse it. But crypto hashes are designed to be really, really fast so that you can use them for things like signing large documents. You can literally run millions (and sometimes billions) of hashes per second on password-length strings. You can run tens or hundreds per second, at best, through a good KDF. They're fast enough for checking a single user-supplied password, but slow enough that brute-force cracking, even with a stolen database using local, custom-designed hardware, takes a long, long time. It's the difference between a couple of hours and a thousand years or more. Of course, that "thousand years" is bound to change at some point in the future, but security is about making things impractical, not impossible. If you fool yourself into believing in "perfect security", you've already lost the game. (Even one-time pad, which is the closest there is, is only as secure as the physical security of the pad.)
 
Last edited:

caftpx10

Well-Known Member
Messages
1,534
Reaction score
114
Points
63
Just wanted to point out something here.
You're still vulnerable to XSS injections if you don't use the second parameter ENT_QUOTES in htmlentities().

Oops, I've just noticed that I've posted on a thread that is a few months old.
 
Top