Register_Globals (a bad idea from day one)

Published:
Updated:
PHP Warning about Register GlobalsDeprecated and Headed for the Dustbin
By now, you have probably heard that some PHP features, while convenient, can also cause PHP security problems.  This article discusses one of those, called register_globals.  It is a thing you do not want.  You probably do not have it turned on (you can use <?php phpinfo(); to check).  But if you do have it turned on, you may have a lot of work ahead of you, just to keep your scripts running in the current PHP environment.

PHP is Both Easy and Powerful
At the confluence of easy, powerful, and complex, we find a lot of assumptions.  We assume certain things just happen automatically, like the contents of the URL GET arguments showing up in the $_GET array when we start our script.  $_GET is always there, always set, as dependable as gravity.  However the contents of $_GET may not be something that we want to use in our scripts, at least not until we have filtered or sanitized the external data.  Consider this interesting notion: the exact same PHP script, with the exact same URL arguments can produce two different outputs.  How could this confusion have happened?  We tried to make PHP too easy.

What Register_Globals Intended
The PHP directive was intended to make it easy for novice programmers to get access to external data.  It was thought that the syntax for assigning variables was "too much work" if you had to write something like this.
 
$x = $_GET["x"];

Open in new window

So PHP created the register_globals directive.  When register_globals is on, the variable $x is copied from the $_GET array and injected into the symbol table.  The reasoning was, "Why make the programmer take the extra step?"  And many PHP programmers thought this was just the way things worked.  But more knowledgeable developers saw the danger in this feature, and it quickly became a lightning rod for the attentions of the PHP Security Project.

What php.net Says About Register_Globals
"When on, register_globals will inject your scripts with all sorts of variables, like request variables from HTML forms. This coupled with the fact that PHP doesn't require variable initialization means writing insecure code is that much easier. It was a difficult decision, but the PHP community decided to disable this directive by default. When on, people use variables yet really don't know for sure where they come from and can only assume. Internal variables that are defined in the script itself get mixed up with request data sent by users and disabling register_globals changes this."

A Dangerous Example
Consider the PHP client authentication strategy here.  It uses a function called "authorized()" to test the client status.  Then it relies on the variable it sets, after testing "authorized()," in other parts of the script.  Simultaneously the script contentedly relies on the undefined variable $safe_client to have the same meaning as $safe_client == FALSE.  The PHP if() statement uses loose data typing, so the meanings of TRUE and FALSE are loosely interpreted, and undefined variables have the same meaning as zero or FALSE. Furthermore, PHP does not issue Notice level messages by default, so the programmer is never alerted to the risk of relying on an undefined variable.
 
<?php
                      if (authorized()) $safe_client = TRUE;
                      if ($safe_client) /* DO SOMETHING ONLY SAFE CLIENTS CAN DO */

Open in new window

Now consider what happens when an unauthorized client presents a URL like this: /path/to/script.php?safe_client=1.  With register_globals off, the script works properly, but with register_globals on, PHP injects a variable with the name $safe_client and the value of 1.  Now the test if ($safe_client) evaluates TRUE and the attack is underway.

Holy Cow! What Should I Do?
You should ensure that your code does not rely on register_globals.  One way to test is to deliberately unregister_globals() with this function.  Then you can see if your script will still run!  Another way to test is to raise the error reporting level to ALL.  That will cause PHP to issue Notice-level messages whenever the script relies on an undefined variable, and it may have the consequence of producing spurious output.  If your scripts do not initialize their variables, and most PHP scripts do not, this could be a real eye-opener!
<?php // RAY_unregister_globals.php?foo=bar
                      error_reporting(E_ALL);
                      
                      
                      // CALL THIS SCRIPT WITH A GET ARGUMENT LIKE THIS:
                      // ...php?foo=bar
                      
                      // THE DESIRED OUTPUT IS SOMETHING LIKE THIS...
                      // REGISTER GLOBALS IS OFF
                      // GET: foo => bar
                      // Notice: Undefined variable: foo in /home/websitet/public_html/RAY_unregister_globals.php on line 24
                      // NULL
                      // Notice: Undefined variable: foo in /home/websitet/public_html/RAY_unregister_globals.php on line 30
                      // NULL
                      
                      
                      // IS IT ON?
                      if (ini_get('register_globals'))
                      {
                          echo "REGISTER GLOBALS IS ON";
                      }
                      else
                      {
                          echo "REGISTER GLOBALS IS OFF";
                      }
                      
                      
                      // SHOW THE GET ARGUMENTS
                      foreach ($_GET as $key => $value)
                      {
                          echo "
                      GET: $key => $value" . PHP_EOL;
                          var_dump($$key);
                      }
                      
                      
                      // UNREGISTER THE GLOBALS AND TEST TO SEE IF THE GET ARGUMENT IS STILL INJECTED
                      unregister_globals();
                      var_dump($$key);
                      
                      
                      // A FUNCTION TO SIMULATE REGISTER-GLOBALS OFF
                      function unregister_globals()
                      {
                          if (ini_get('register_globals'))
                          {
                              $array = array('_REQUEST', '_FILES');
                              foreach ($array as $value)
                              {
                                  if(isset($GLOBALS[$value]))
                                  {
                                      foreach ($GLOBALS[$value] as $key => $var)
                                      {
                                          if (isset($GLOBALS[$key]) && $var === $GLOBALS[$key])
                                          {
                                              // ACTIVATE THIS FOR REAL-TIME OUTPUT
                                              // echo 'FOUND ' . $key . ' = ' . $var . ' in $' . $value; . PHP_EOL;
                                              unset($GLOBALS[$key]);
                                          }
                                      }
                                  }
                              }
                          }
                      }

Open in new window

The desired output is something like this:
REGISTER GLOBALS IS OFF
GET: foo => bar
Notice: Undefined variable: foo in /home/websitet/public_html/RAY_unregister_globals.php on line 23
NULL
Notice: Undefined variable: foo in /home/websitet/public_html/RAY_unregister_globals.php on line 29
NULL

Remediation of Register Globals
This is a multi-step process that requires changes to functional software.  Before you do anything else make backups of every script involved! If you're using a version control system you can say "Whew" because you can always roll back from SVN or GIT.

Take the following steps, on all PHP scripts, one at a time.

1. Add error_reporting(E_ALL) to the top of the script.  Make sure that error_reporting() is not reset in any include() scripts.
2. Run the script and look carefully in the browser output or error log for PHP "Notice" messages about undefined variables.
3. Using a text editor with a "find" function, locate every instance of the undefined variables.
4. Examine the PHP script to determine the expected origin of the variables, probably $_GET or $_POST in most instances, but $_COOKIE and other superglobal arrays could be involved, too.
5. Make an explicit assignment statement to set the values of the undefined variables.
6. Lather, rinse, repeat for each undefined variable.
7. Lather, rinse, repeat for each PHP script.

You code coverage during testing is an important part of this process.  Make sure your test suite exercises all of the locations that you found in step 3.

You may want to read about the very useful ternary operator.

An example of a PHP script that depends on Register Globals and the remediation process...
 
<?php
                      // ASSUME THAT PHP WILL INJECT $q INTO THE SYMBOL TABLE
                      if ($q == NULL) echo "MISSING $q= URL PARAMETER";

Open in new window


<?php
                      /**
                      * RAISE ERROR REPORTING LEVEL
                      */
                      error_reporting(E_ALL);
                      
                      /**
                      * THIS IS AN EXAMPLE OF AN UNDEFINED VARIABLE
                      * THE ORIGINAL PROGRAMMER ASSUMED THAT REGISTER GLOBALS
                      * WOULD INJECT A URL PARAMETER INTO THE SYMBOL TABLE
                      * AT $q BUT WITHOUT REGISTER GLOBALS THE INJECTION
                      * DOES NOT OCCUR AND MUST BE DONE MANUALLY
                      */
                      if ($q == NULL) echo "MISSING $q= URL PARAMETER";

Open in new window


<?php
                      /**
                      * RAISE ERROR REPORTING LEVEL
                      */
                      error_reporting(E_ALL);
                      
                      /**
                      * THIS IS AN EXAMPLE OF REMEDIATED CODE USING THE TERNARY OPERATOR
                      * THE TERNARY OPERATOR ENSURES THAT $q IS CORRECTLY ASSIGNED
                      * EITHER FROM THE REQUEST VARIABLE IN THE URL PARAMETER OR
                      * TO THE PHP NULL VALUE.
                      */
                      $q
                      = (!empty($_GET['q']))
                      ? $_GET['q']
                      : NULL
                      ;
                      
                      /**
                      * NOW THE if() STATEMENT WILL WORK WITHOUT THROWING A "NOTICE"
                      */
                      if ($q == NULL) echo "MISSING $q= URL PARAMETER";

Open in new window


References Worth Noting
The PHP security warning
http://php.net/manual/en/security.globals.php

It will be removed in PHP5.4, which is nearing release (this is written in September 2011).  Note the list of legacy stuff removed here:
http://www.php.net/releases/NEWS_5_4_0_alpha1.txt

Another longstanding warning
http://phpsec.org/projects/guide/1.html#1.3

Also Deprecated and Headed for the Dustbin
You may also be interested in this article:
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_6630-Magic-Quotes-a-bad-idea-from-day-one.html

And, of course, MySQL support is going away.  Here's the explanation and solution:
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/PHP_Databases/A_11177-PHP-MySQL-Deprecated-as-of-PHP-5-5-0.html

Please give us your feedback!
If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!
 
5
8,955 Views

Comments (2)

Commented:
Ray I have a somewhat silly question for you.

Is it a bad practice to initialize all of your variables with PHP?
I always initialize all of my variables to NULL or some preset value so I don't have any issues.  I think it comes from VB coding.
Most Valuable Expert 2011
Author of the Year 2014

Author

Commented:
I recommend that you initialize variables.  Not all of my code does this but it is a really great idea.  PHP does not require it.  Quel Fromage.

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.