PHP is a honking fat security hole, apparently. Of course so's a webserver. Imagine something that runs as the system administrator, and lets ANYONE read files from your hard drive, and run scripts without them ever needing to log in. That's a bruiser, and PHP doesn't make much of a difference after that, unless you write bad code that people can mess around with.
This is something to do especially for session variables. Say you've got a variable called $logged_in and you save it in a session. Every time the page loads and you load the session, PHP restores the value of $logged_in and then you check to see if it's 1 or 0.
A strength, and a potential issue of PHP is that you can type stuff in a URL that gets turned into variables when PHP loads. This is how PHP handles GET forms (and POST forms, though it's a tiny bit harder to spoof those) - someone could do http://www.yoursite.net/index.php?logged...name=admin
And $logged_in would be set to 1, and $username would be 'admin'
If $logged_in hasn't be registered in the session yet, someone could do that and pretend they've logged into your site. This supposes that they know your variable names, of course.. but nothing's stopping them from guessing. To get around this, declare your variables at the start of your code before you load the session, then malicious users can't "pre-load" those variables.
<?
$logged_in = 0;
session_start();
if (!$logged_in) {
echo "You aren't logged in, go away!";
} else {
include("secret.html");
}
?>
Let's say that you have a form on your page, and you use the contents of that form to build an SQL query. Someone could quite easily save the form to their PC, mess around with it and then submit it, sending data that runs SQL of their choosing on the end of your query. It's easy enough to do, so you need to check for this kind of thing.
The easiest way around this is never to use data from a web form inside database queries, or file-open calls, or system calls. Sometimes you have to. Fortunately by default, PHP has something called 'magic quotes' enabled by default - it'll add backslashes to unsafe characters from web forms.
But really, I can't rub it in enough -- CHECK USER INPUT. Whatever comes from outside can potentially be faked, sometimes by error, sometimes maliciously. Don't trust anything.
Ways around malicious input you can't check too much:
- If it's a file/folder-name, disallow the . character (ESPECIALLY at the start, and especially ../ ), and the / at the beginning of the string. This stops people going back one or more folders (i.e. ../../../../../../../etc/passwd :-)
- If it's filename, don't let the user choose the full name - add the file extension yourself. Or use a naming rule where you can and check it with a regular expression
- If your form allows file uploads, then use the is_uploaded_file() and/or move_uploaded_file() functions to handle them because they make sure that no one is spoofing the filename and stealing a file from your server.
If you think some of these are nasty/unlikely.. It can be done, it has been done, and it will be done again. Don't imagine for a second that you're immune!
Security changes in PHP 4.2.x
These methods have been around for ages, but weren't enforced. Up until PHP 4.2.x, the default behaviour of PHP on starting up was to turn all input from the GET, POST, COOKIE and SESSION environments into variables. So if you had a form input on a page called "myvar", then when PHP loads up the script for that request, it would set $myvar to the content of the input field. The problem with this is that sometimes you don't want people to be setting variables in your script - what if you forget to declare a variable like login-status and someone sets it for themselves, as described above? Many people who dislike PHP have this as their primary argument. Well in PHP 4.2.x the behaviour has changed.
Now, form, cookie and session variables are all stored in their own arrays, and not set as global variables when PHP starts up. In PHP 4.1.x and earlier you could access form inputs in the $HTTP_POST_VARS['var'], $HTTP_GET_VARS['var'], and $HTTP_SESSION_VARS['item'] arrays, it was pretty rare for people to do so. Now it's forced, but to help us beleagered programmers out, starting from PHP 4.1.x these variables also have short versions that are "superglobals" that you don't have to declare inside functions and you know exactly where your data is coming from : $_SESSION, $_FILES, $_COOKIES, $_SERVER, $_ENV, $_POST, and $_GET. Also, if you use the $_SESSION array, sessions are automatically started and new variables automatically registered, so you don't have to worry about session_register() and session_start().
If you're writing scripts that have to run on PHP 4.0.6 as well as 4.2.x, then you can still use the $HTTP_x_VARS variables, but performance enhancements, PEAR, and security fixes should combine to be a pretty good excuse to upgrade, and using the new superglobals ensures that you know where your data is coming from and that you can access it from anywhere in your scripts, even inside functions and objects. Don't let this give you a false sense of security though - always check and validate any data that comes from outside the script; files, form inputs, even environment variables. You can never be too safe.