Hi
I'm just in the middle of developing an e-commerce site, and thought it might be an idea to post a topic on the security issues that came up while coding the site - as well as security issues in sites in general.
Most of the problems I came across were actually in the order that the script was run. One thing I've always been careful about is that my include scripts cannot be accessed by anyone from the internet.
For example, imagine you have a few scripts that are called up by almost every page in your site - initialise.php auth.php, and a few others.
Now, imagine that when I call initialise.php, it starts the session, and sets a few variables, such as $auth. The $auth variable should be set to '0' when the user is not logged in, and '1' when they are logged in. Auth.php looks at $auth, and stops the execution of the script if $auth equals '0'. Otherwise it saves $auth to the session, so that the user's still logged in when they refresh the page.
That all works wonderfully so long as initialise.php is called before auth.php - but what if a hacker were to call up the scripts him/herself. The hacker could call up auth.php without calling up initialise.php, and then the variables wouldn't have been set.
This would actually be okay, since $auth still evaluates to '0' if it's not set, but what if the hacker called auth.php?auth=1 ? If global variables were on (which they shouldn't be!), then the hacker would have just gained access to the site.
Normally it's not as simple as this. In fact, this example here wouldn't work, since auth.php wouldn't be able to write to the session variables if initialise hadn't started the session, but it's the best example I could think of.
So keep your include files out of the hackers reach, but what about the way PHP reads the code itself?
Okay, new example. We have an e-commerce site, and someone's just payed for something. Let's say they've paid for a hosting package. We want to set up the hosting package for them, and then charge them for the package.
Let's look at the pseudo code for this:
<?php
// $credits is the amount of credit (money)
// they have (they pay for credits, and then
// spend it - a bit like chips at a casino).
// $price is the amount of credits that the
// hosting package costs
if ( $credits >= $price ) {
// They have enough money in their account
// Set up their hosting account
set_up_hosting();
// Now charge them (subtract from their credits)
// (of course, we'd add something to the WHERE
// part of the query to limit to just that user).
mysql_query("UPDATE credits SET credits = credits-$price");
} else {
// They don't have enough credits
echo "Please purchase some more credits";
}
?>
So that should work nicely, huh?
Think about this - what if the hacker were able to run the first part of that code (create the hosting account), but avoid the second part of the code (the part where they are being charge).
How would they do that though? They have to somehow make the SQL query fail, while the first part succeeds. Everything that goes into an SQL query should have been made safe with mysql_escape_string(), so just adding a few quotes isn't going to work.
How about a good old buffer overflow script then?
Take a look at this script I ran on my system:
$ perl -e "print \"UPDATE credits SET credits = credits+'150\"; print '#'x999999999; print \"';\"" | mysql -u test -p test
Enter password: ****
ERROR 1153 at line 1: Got a packet bigger than 'max_allowed_packet'
Interesting. That command just ran an SQL query to update the credits table, but there's a huuuuge long string in the query. The query is perfectly valid, but it's too long - the result? It failed! If we implemented that into the above pseudo code, then we'd be able to make php run some of the code, but the MySQL query would fail - leaving our account uncharged.
This, of course, needs some tweaking. Firstly, the client shouldn't have access to the $price variable, but maybe there might be a trivial value that the client has access to that's put into the database. Possibly a $comments variable for the client to make any additional requests regarding their package.
As well as buffer overflows to stop the SQL from working, the script could exit prematurely. What if the system just happened to reboot at that exact point? Incredibly unlikely, but still possible.
Some of these things are impossible for us to allow for in our scripts, but we should try our best to make sure that premature exits, or failed SQL queries are dealt with. In this example, the easiest way to do this would simply be to change the order that the script works - charge first, then create the account. That way, if the SQL fails we can catch the error, and prevent the account from being created. It's still vulnerable to system reboots, etc, but it's much less likely to be hacked.
Other things to note in e-commerce sites:
Never expect anything. An example:
Say we have a currency conversion site that converts British currency to currency X, and back.
The smallest unit in British currency is the penny (p). The smallest unit in currency X is 'Y'. Y is worth 0.8 pennies.
Your site is expecting people to convert decent amounts of money - perhaps £100 or more. But what if someone were to convert 1Y? 1Y is 0.8p, so we'd have to round up and give the client 1p in return, otherwise the client would have just sent their money to /dev/null (that's a black hole in UNIX like systems â things go in, but nothing comes out). In that transaction, we'd have lost 0.2p, which is fairly trivial. But what if the client were to convert a few more Ys to pennies? They'd make 0.2p profit each time. Once the client has made a few pennies, they can convert them back to Ys (without losing anything), and start the process again.
If a bank made this mistake, then in theory a hacker could have drained the bank of its money in just a few hours.
That's about all I've got time for, I hope someone finds this remotely helpful :-)
--
Please contact me at:
Colin (dot) Horne (at) gmail (dot) com
My blog:
http://colinhorne.blogspot.com