PHP Unconference Europe 2015

グローバル変数の登録機能の使用法

警告

この機能は PHP 5.3.0 で 非推奨となり、 PHP 5.4.0 で削除されました。

PHPの変更点で最も議論の対象となったのは、おそらく、PHP » 4.2.0において PHPのディレクティブ register_globalsが デフォルトでONからOFFに変更された時でしょう。 当時、このディレクティブに依存することが一般的であり、多くの人は、 このパラメータの存在すら知らず、PHPの動作そのものであるというよう に考えていました。このページは、このディレクティブにより安全でな いコードを書く可能性があるということをこのページで説明しますが、 このディレクティブそのものが安全でないわけではなく、これを誤って使 用することが安全でないということを念頭においていてください。

register_globalsをonとした場合、この機能により、HTMLフォームから投 稿される変数と同時に、あらゆる種類の変数がスクリプトに注入される ことになります。これは、PHPにおいては変数の初期化が不 要であるということにも関係し、安全でないコードを書くことが極めて容 易になるということを意味します。困難な変更でしたが、PHPコミュニティ は、このディレクティブをデフォルトで無効とすることを決定しました。 onとした場合、どこから来たのかが不明であり、出処を仮定するしかない 変数を使用することになります。スクリプト自体で定義される内部変数は、 ユーザーにより送信されたリクエストデータと混ざってしまいますが、 register_globals を無効とすることでこれを回避することができます。 以下にregister_globalsの誤った使用例を示しましょう。

例1 register_globals = on の誤った使用例

<?php
// ユーザーが認証された場合のみ $authorized = true を定義する
if (authenticated_user()) {
    
$authorized true;
}

// 最初に$authorizedをfalseとして初期化していないため、
// 以下のコードにより、GET auth.php?authorized=1 のように
// register_globals機能により定義される可能性があります。
// このため、誰でも認証されたようにみせることができます!
if ($authorized) {
    include 
"/highly/sensitive/data.php";
}
?>

register_globals = onとした場合、上記のロジックは破綻する可能性が あります。offの場合、$authorizedはリクエストに より設定することはできず、正しく動作します。しかし、一般に良いプロ グラミング法は、変数を最初に初期化することです。例えば、上の例で $authorized = falseを先頭に置いておくことができ ます。これにより、ユーザーはデフォルトで認証されない状態となるため、 register_globalsのon/offによらず上記のコードは動作します。

別の例は、 セッションに関するも のです。register_globals = onとした場合、以下の例で $usernameを使用することもできますが、 (URLにより)GETのような他の手段によっても $username を設定することができることに留意する 必要があります。

例2 register_globals on またはoffの場合のセッションの使用例

<?php
// $usernameの出処は不明だが、$_SESSIONがセッションデータであることは
// 既知です。
if (isset($_SESSION['username'])) {

    echo 
"Hello <b>{$_SESSION['username']}</b>";

} else {

    echo 
"Hello <b>Guest</b><br />";
    echo 
"Would you like to login?";

}
?>

偽造が試みられた時に警告するために予防的な計測を行うことも可能です。 ある変数の出処を前もって正確に知っている場合、 投稿されたデータが適切な投稿元からのものであるかを調べることができ ます。これは、データが偽造されたものでないことを保証するわけではあ りませんが、攻撃者による偽造の成立を限定的なものにすることができま す。リクエストデータの出処を気にかけない場合、 $_REQUEST を使用することができます。 この変数には、GET、POST、COOKIEが合わさって含まれています。 本マニュアルの 外部から来る変数 のセクションを参照してください。

例3 簡単に変数汚染を検出する

<?php
if (isset($_COOKIE['MAGIC_COOKIE'])) {

    
// MAGIC_COOKIE comes from a cookie.
    // Be sure to validate the cookie data!

} elseif (isset($_GET['MAGIC_COOKIE']) || isset($_POST['MAGIC_COOKIE'])) {

   
mail("admin@example.com""Possible breakin attempt"$_SERVER['REMOTE_ADDR']);
   echo 
"Security violation, admin has been alerted.";
   exit;

} else {

   
// MAGIC_COOKIE isn't set through this REQUEST

}
?>

もちろん、register_globalsをoffにするだけでは、使用するコードが安 全であることを意味しません。投稿された全てのデータ毎に 他の手段で検証することも必要です。ユーザーデータを常に検証し、 使用する変数を初期化してください! 初期化されていない変数を調べるには、 error_reporting()E_NOTICEレベルのエラーを有効にするように してください。

register_globalsをOnまたはOffのエミュレートに関数情報に ついては、FAQを 参照してください。

注意: スーパーグローバル: 使用可能なバージョンに関する注意

PHP 4.1.0 以降、 $_GET, $_POST, $_SERVER 等のスーパーグローバル配列が使用可能となっています。 詳細な情報については、マニュアルの superglobals のセクションを参照してください。

add a note add a note

User Contributed Notes 7 notes

up
28
lester burlap
5 years ago
It would make this whole issue a lot less confusing for less-experienced PHP programmers if you just explained:

- $myVariable no longer works by default
- $_GET['myVariable'] works just fine

I'm embarrassed to say it's taken me six months since my ISP upgraded to PHP5 figure this out.  I've completely rewritten scripts to stop using GET variables altogether.

I'm dumb.
up
5
elitescripts2000 at yahoo dot com
1 year ago
<?php

/* Forces all GET and POST globals to register and be magically quoted.
* This forced register_globals and magic_quotes_gpc both act as if
* they were turned ON even if turned off in your php.ini file.
*
* Reason behind forcing register_globals and magic_quotes is for legacy
* PHP scripts that need to run with PHP 5.4 and higher.  PHP 5.4+ no longer
* support register_globals and magic_quotes, which breaks legacy PHP code.
*
* This is used as a workaround, while you upgrade your PHP code, yet still
* allows you to run in a PHP 5.4+ environment.
*
* Licenced under the GPLv2. Matt Kukowski Sept. 2013
*/

if (! isset($PXM_REG_GLOB)) {

 
$PXM_REG_GLOB = 1;

  if (!
ini_get('register_globals')) {
    foreach (
array_merge($_GET, $_POST) as $key => $val) {
      global $
$key;
      $
$key = (get_magic_quotes_gpc()) ? $val : addslashes($val);
    }
  }
  if (!
get_magic_quotes_gpc()) {
    foreach (
$_POST as $key => $val) $_POST[$key] = addslashes($val);
    foreach (
$_GET as $key => $val$_GET[$key]  = addslashes($val);
  }
}

?>
up
4
claude dot pache at gmail dot com
5 years ago
Beware that all the solutions given in the comments below for emulating register_global being off are bogus, because they can destroy predefined variables you should not unset. For example, suppose that you have

<?php $_GET['_COOKIE'] == 'foo'; ?>

Then the simplistic solutions of the previous comments let you lose all the cookies registered in the superglobal "$_COOKIE"! (Note that in this situation, even with register_global set to "on", PHP is smart enough to not mess predefined variables such as  $_COOKIE.)

A proper solution for emulating register_global being off is given in the FAQ, as stated in the documentation above.
up
2
Ruquay K Calloway
6 years ago
While we all appreciate the many helpful posts to get rid of register_globals, maybe you're one of those who just loves it.  More likely, your boss says you just have to live with it because he thinks it's a great feature.

No problem, just call (below defined):

<?php register_globals(); ?>

anywhere, as often as you want.  Or update your scripts!

<?php
/**
* function to emulate the register_globals setting in PHP
* for all of those diehard fans of possibly harmful PHP settings :-)
* @author Ruquay K Calloway
* @param string $order order in which to register the globals, e.g. 'egpcs' for default
*/
function register_globals($order = 'egpcs')
{
   
// define a subroutine
   
if(!function_exists('register_global_array'))
    {
        function
register_global_array(array $superglobal)
        {
            foreach(
$superglobal as $varname => $value)
            {
                global $
$varname;
                $
$varname = $value;
            }
        }
    }
   
   
$order = explode("\r\n", trim(chunk_split($order, 1)));
    foreach(
$order as $k)
    {
        switch(
strtolower($k))
        {
            case
'e':    register_global_array($_ENV);        break;
            case
'g':    register_global_array($_GET);        break;
            case
'p':    register_global_array($_POST);        break;
            case
'c':    register_global_array($_COOKIE);    break;
            case
's':    register_global_array($_SERVER);    break;
        }
    }
}
?>
up
1
arman_y_92 at yahoo dot com
2 months ago
To all those fans of this insecure functionality (which I'm glad is now turned off by default) , you can just use extract() to achieve a similar goal more securely (unless you overwrite local variables with $_GET or $_POST data).
up
0
moore at hs-furtwangen dot de
6 years ago
I had a look at the post from Dice, in which he suggested the function unregister_globals(). It didn't seem to work - only tested php 4.4.8 and 5.2.1 - so I made some tweaking to get it running. (I had to use $GLOBALS due to the fact that $$name won't work with superglobals).

<?php
//Undo register_globals
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]) {
                       
//echo 'found '.$key.' = '.$var.' in $'.$value."\n";                   
                       
unset($GLOBALS[$key]);
                    }
                }
            }
        }
    }
}
?>

The echo was for debuging, thought it might come in handy.
up
-5
Dice
6 years ago
To expand on the nice bit of code Mike Willbanks wrote and Alexander tidied up, I turned the whole thing in a function that removes all the globals added by register_globals so it can be implemented in an included functions.php and doesn't litter the main pages too much.

<?php
//Undo register_globals
function unregister_globals() {
    if (
ini_get(register_globals)) {
       
$array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
        foreach (
$array as $value) {
            foreach (
$GLOBALS[$value] as $key => $var) {
                if (
$var === $GLOBALS[$key]) {
                    unset(
$GLOBALS[$key]);
                }
            }
        }
    }
}
?>
To Top