9.8.1 Problem
You want to
securely process form input variables and not allow someone to maliciously alter
variables in your code.
9.8.2 Solution
Disable the register_globals
configuration directive and access variables only from the $_REQUEST array. To be even more secure, use $_GET ,
$_POST, and $_COOKIE to make sure you know exactly where your
variables are coming from.
To do this, make sure this line appears in your php.ini file:
register_globals = Off
As of PHP 4.2, this is the default configuration.
9.8.3 Discussion
When register_globals is set on, external
variables, including those from forms and cookies, are imported directly into
the global namespace. This is a great convenience, but it can also open up some
security holes if you're not very diligent about checking your variables and
where they're defined. Why? Because there may be a variable you use internally
that isn't supposed to be accessible from the outside but has its value
rewritten without your knowledge.
Here is a simple example. You have a page in which a user
enters a username and password. If they are validated, you return her user
identification number and use that numerical identifier to look up and print out
her personal information:
// assume magic_quotes_gpc is set to Off
$username = $dbh->quote($_GET['username']);
$password = $dbh->quote($_GET['password']);
$sth = $dbh->query("SELECT id FROM users WHERE username = $username AND
password = $password");
if (1 == $sth->numRows( )) {
$row = $sth->fetchRow(DB_FETCHMODE_OBJECT);
$id = $row->id;
} else {
"Print bad username and password";
}
if (!empty($id)) {
$sth = $dbh->query("SELECT * FROM profile WHERE id = $id");
}
Normally, $id is set only by your program and is a
result of a verified database lookup. However, if someone alters the GET string,
and passes in a value for $id, with register_globals enabled,
even after a bad username and password lookup, your script still executes the
second database query and returns results. Without register_globals,
$id remains unset because only $_REQUEST['id'] (and
$_GET['id']) are set.
Of course, there are other ways to solve this problem, even
when using register_globals. You can restructure your code not to allow
such a loophole.
$sth = $dbh->query("SELECT id FROM users WHERE username = $username AND
password = $password");
if (1 == $sth->numRows( )) {
$row = $sth->fetchRow(DB_FETCHMODE_OBJECT);
$id = $row->id;
if (!empty($id)) {
$sth = $dbh->query("SELECT * FROM profile WHERE id = $id");
}
} else {
"Print bad username and password";
}
Now you use $id only when it's been explicitly set
from a database call. Sometimes, however, it is difficult to do this because of
how your program is laid out. Another solution is to manually unset(
) or initialize all variables at the top of your
script:
unset($id);
This removes the bad $id value before it gets a chance
to affect your code. However, because PHP doesn't require variable
initialization, it's possible to forget to do this in one place; a bug can then
slip in without a warning from PHP.