Help us understand the problem. What is going on with this article?

PHP 5.4 以上でも register_globals を再現するライブラリ MercifulPolluter

More than 1 year has passed since last update.

php54_obsolute.png

あらすじ

PHP と呼ばれる言語では、かつて register_globals という機能が猛威を奮っていました。簡単に言うと、リクエストパラメータが自動的にグローバル変数にセットされるというものです。

// http://example.com/?foo=123&bar=baz

var_dump($_GET['foo'], $_GET['bar']);
// string(3) "123"
// string(3) "baz"

var_dump($foo, $bar);
// string(3) "123"
// string(3) "baz"

この機能が全盛期だった頃、残念ながら私は PHP を書いていませんでしが、おそらくこの機能が ON になっていることで「うわ、何も考えなくても勝手に変数として使える!便利だ!!」みたいな、 よしなにやってくれる という PHP 特有の機能でもダントツのものだったと思います。

しかし、上のコードを見ていただけるとわかるとおり、ある程度複雑化したアプリケーションがこの register_globals 前提の設計をしているとどうなるでしょう。「この変数 Where from?」としか思えないわけです。修正が困難になり、ソウルジェムが濁り、飯が食えなくなる。つらい。

もちろんセキュリティ的にもよろしくないということで有名です。

register_globalsをonとした場合、この機能により、HTMLフォームから投稿される変数と同時に、あらゆる種類の変数がスクリプトに注入されることになります。これは、PHPにおいては変数の初期化が不要であるということにも関係し、安全でないコードを書くことが極めて容易になるということを意味します。

PHP: グローバル変数の登録機能の使用法 - Manual

公式サイトにもはっきりと「安全でないコードを書くことが極めて容易になる」と書かれている機能として名を馳せていました。

ですがこの機能、PHP 5.4 で消え去りました 。悪魔は去ったのだ。平和は戻りました。

しかしみんな知っていました。PHP 5.4 から register_globals が消え去ったからといって、世の中に存在する PHP アプリケーションが全て 5.4 以上になったわけじゃない。現実は厳しいのだ。むしろ register_globals 前提で書かれたソースコードの改修に絶望し、PHP 5.3 以下から脱出できない方々も居るでしょう。5.3 の公式サポートは既に EOL 。どうすればいいんだ…

救済

  • 将来的には全て作り直す必要がある
  • でも俺達は人間だ。少しずつ少しずつ歩くしかないのだ
  • かといってサポート切れの PHP 5.3 を使い続けるのは…
  • PHP 5.4 にすぐにでも移行しつつ、且つ少しずつ修正していく時間を稼ぎたい

そんな方々の為に救済ライブラリとして MercifulPolluter をご紹介します。

直訳すると 慈悲深い汚染。あくまでも一時しのぎ。汚染していることに代わりはない。そういった覚悟を忘れない為のライブラリ名となっています。

Installation

Composer をお使いの方は、すぐにお使い頂けます。
それ以外の方はがんばってください。

{
    "require": {
        "gongo/merciful-polluter": "*"
    }
}

Usage

1. Basic

本来の register_globals の機能になるべく近づける為に、下記コードをアプリケーションのエントリーポイント1、その中でもなるべく早い段階にセットすることをお勧めします。

<?php
(new Gongo\MercifulPolluter\Request)->pollute();

// main routine

Gongo\MercifulPolluter\Request::pollute() を呼び出した時点で、$_GET['foo']$foo に、$_POST['bar']$bar に値を注入します。

2. Session

ご存知の方も居らっしゃると思いますが、register_globals = On の場合、実は $_SESSION もグローバル変数に注入されます。

もちろん MecifurlPolluter は Session にも対応していますが、先程の Gongo\MercifulPolluter\Request::pollute() だけでは $_SESSION が注入されるようにはなっていません。別途下記のコードが必要となります。

// session_start() の後に実行する
session_start();
(new Gongo\MercifulPolluter\Session)->pollute();

そしてもう一つ、$_GET$_POST などのリクエスト系と違って、$_SESSIONregister_globals の機能でグローバルに注入された時、グローバル変数と $_SESSION の間でリファレンスが貼られます

session_start();

var_dump($_SESSION['user_id'], $user_id);
// string(5) "gongo"
// string(5) "gongo"

$user_id = 'qiita';

var_dump($_SESSION['user_id'], $user_id);
// string(5) "qiita" <= !!!???
// string(5) "qiita"

MercifulPolluter はもちろんこの挙動も再現しております。

3. magic_quotes_gpc

register_globals と並んで、PHP が誇る よしなに機能 として magic_quotes_gpc のことも避けては通れません。

簡単に言うと $_GET$_POST および $_COOKIE のそれぞれの要素に対して addslashes() をかけていくものです。凄まじい威力だ。

この機能も PHP 5.4 で廃止となっていましたが、もしかしたらこの機能に依存しているアプリケーションもまだ数多くあることでしょう。

magic_quotes_gpc の機能は register_globals 同様、エントリーポイントに入る前に処理が行われているため、2つ併せて On にしている可能性も考え、MercifulPollutermagic_quotes_gpc の機能を持たせました。

// $_GET['body'] = "L'Arc-en-Ciel";

$request = new Gongo\MercifulPolluter\Request;
$request->enableMagicQuotesGpc();
$request->pollute();

var_dump($_GET['body'], $body);
// string(14) "L\'Arc-en-Ciel"
// string(14) "L\'Arc-en-Ciel"

pollute() を呼び出す前に enableMagicQuotesGpc() を実行しておくと、$_GET$_POST$_COOKIE およびそれらが注入されたグローバル変数に addslashes() が適用されます。

おまけ

MercifulPolluter というよりは、本家 register_globals がどういった仕様かについて軽く補足します。

:pizza: 同じキーが使われている場合どれが優先されるのか

例えば $_COOKIE['foo']$_GET['foo'] があったとして、どの値が $foo になるのか(どれが優先されるのか)ということについては、本家 register_globals と同じく variables_order に影響されます。

非推奨のディレクティブ register_globals が on になっていると、 variables_order の設定は、 ENV、 GET、POST、 COOKIE および SERVER の各変数がグローバルスコープに取り込まれる順番も左右します。

:pizza: $_FILES がグローバル変数に注入される時の注意

フォームからファイルアップロードした時に生成される $_FILES というスーパーグローバル変数も、もちろん register_globals の効果でグローバルに注入されます。ですが $_FILES だけは他のスーパーグローバル変数と少し違う名前付けが行われます。

ファイルアップロードされた時の $_FILES は、基本的にこんな形になっていると思います。

$_FILES['upload']['tmp_name'] = '/tmp/qwsedrftgyhuji';
$_FILES['upload']['name'] = 'memo.txt';
$_FILES['upload']['size'] = 123;
$_FILES['upload']['type'] = 'text/plain';

これをグローバル変数に注入する時、

  • $_FILES['upload']['tmp_name']$upload
  • $_FILES['upload'][xxx]$upload_xxx
    • 例えば $_FILES['upload']['size'] であれば $upload_size

というグローバス変数名になります。tmp_name だけ特別扱いです。注意しましょう。

まとめ

PHP 5.3.29 のソースコード を読んだりしてなるべく本来の機能と同等な効果が得られるようになっていると思いますが、やはり抜けはあるかもしれません。その際は issue にでもご報告頂ければと思います。

できればこんなライブラリが誰にも使われない方がいい。そんな未来にしていきましょう。

参考


  1. 例えば auto_prepend_file とか。各フレームワークにそういった類のものがあればそこでも大丈夫だと思います。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした