register_globals

register_globals についてある人からメールで質問(正確には質問ではないのですが)されたましたので、調べたり考えたりしたことをまとめてみます。何か間違いや勘違い等がありましたら指摘していただけますと幸いです。

話題としては、以下のページにある、register_globals = Off でも動作する PHP スクリプトregister_globals = On の環境で動作させる必要がある場合でも、グローバル変数汚染を防ぎ、安全に PHP スクリプトを動作させることができるかという問題です。

register_globals が On の環境でも Off と同様の状態にする方法
http://www.asahi-net.or.jp/~wv7y-kmr/memo/php_security.html#PHP_register_globals_off

このページで、(IN)SECURE Digital Security Magazine ISSUE 1.1 を参考に、以下のようなスクリプトを提示しています。

if ( ini_get( 'register_globals' ) ) {
     foreach( array_keys( $_REQUEST ) as $key ) {
         unset( $GLOBALS[$key] );
     }
}

実際に register_globals を Off と同様にするには、上記のスクリプトは以下のような問題がありますので、少し修正した方が良さそうです。

  1. register_globals が On の場合は $_SERVER に含まれる変数も登録される

    $_SERVER の変数も登録されることを考えると register_globals が Off と同様の状態に近づけるためには、以下のように変更した方が良さそうです。

    if ( ini_get( 'register_globals' ) ) {
        foreach( array_keys( array_merge( $_REQUEST, $_SERVER ) ) as $key ) {
            unset( $GLOBALS[$key] );
        }
    }
    


  2. GET や POST 変数に、_GET や _POST、GLOBALS などのという文字列が与えられると該当の配列まで削除してしまう

    上記のスクリプトでは、例えば、http://localhost/index.php?_GET=1 のように _GET が含まれるリクエストを受けると $_GET を削除してしまう事になります。

    このような不正なリクエストを正しく処理する必要はないと考えられますし、PHP 4.4.0 以下、PHP 5.0.5 以下のバージョンで報告されている $GLOBLAS の上書きの問題にも対処できますので、以下のように処理を中止するようにすると安全かもしれません。

    foreach ( array( '_GET', '_POST', '_COOKIE', '_SERVER', '_SESSION', '_ENV', 'GLOBALS' ) as $key ) {
        if ( isset( $_REQUEST[$key] ) ) {
            // エラー処理
            exit;
        }
    }
    


まとめると、以下のコードを PHP の最初に実行しておくと少しは安全になるように思います。

if ( ini_get( 'register_globals' ) ) {
    // $_REQUEST にスーパーグローバル変数のキー名が含まれている場合は処理を終了
    foreach ( array( '_GET', '_POST', '_COOKIE', '_SERVER', '_SESSION', '_ENV', 'GLOBALS' ) as $key ) {
        if ( isset( $_REQUEST[$key] ) ) {
            exit();
        }
    }
    // register_globals によって登録されたグローバル変数を削除
    foreach( array_keys( array_merge( $_REQUEST, $_SERVER ) ) as $key ) {
        unset( $GLOBALS[$key] );
    }
}

あと、$HTTP_*_VARS のような使わないのに登録されているグローバル変数を削除したい場合には、以下のようなスクリプトを使用すると便利かもしれません。

foreach ( array_keys( $GLOBALS ) as $_key ) {
    if ( $_key[0] !== '_' && $_key !== 'GLOBALS' ) {
        unset( $GLOBALS[$_key] );
    }
}
unset( $_key );