Recipe 9.15. Preventing Global Variable Injection


9.15.1. Problem

You want to access form input variables without allowing malicious users to set arbitrary global variables in your program.

9.15.2. Solution

Disable the register_globals configuration directive and access variables only from the $_GET, $_POST, and $_COOKIE arrays to make sure you know exactly where your variables are coming from.

To do this, make sure register_globals = Off appears in your php.ini file.

As of PHP 4.2, this is the default configuration.

9.15.3. Discussion

When register_globals is set to 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.

Example 9-26 contains a simple example: imagine 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.

Insecure register_globals code

<?php // 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 query string, and passes in a value for $id, you'll have problems. With register_globals enabled, your script could still execute the second database query and return results even after a bad username and password lookup. 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. One way to do this is in Example 9-27.

Avoiding register_globals problems

<?php $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"; }

In Example 9-27 $id has a value 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. 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.

9.15.4. See Also

Documentation on register_globals can be found at http://www.php.net/security.registerglobals.php.




PHP Cookbook, 2nd Edition
PHP Cookbook: Solutions and Examples for PHP Programmers
ISBN: 0596101015
EAN: 2147483647
Year: 2006
Pages: 445

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net