あらすじ
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 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
をご紹介します。
- Packagist https://packagist.org/packages/gongo/merciful-polluter
- GitHub https://github.com/gongo/merciful-polluter
直訳すると 慈悲深い汚染。あくまでも一時しのぎ。汚染していることに代わりはない。そういった覚悟を忘れない為のライブラリ名となっています。
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
などのリクエスト系と違って、$_SESSION
が register_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 にしている可能性も考え、MercifulPolluter
に magic_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
がどういった仕様かについて軽く補足します。
同じキーが使われている場合どれが優先されるのか
例えば $_COOKIE['foo']
と $_GET['foo']
があったとして、どの値が $foo
になるのか(どれが優先されるのか)ということについては、本家 register_globals
と同じく variables_order に影響されます。
非推奨のディレクティブ register_globals が on になっていると、 variables_order の設定は、 ENV、 GET、POST、 COOKIE および SERVER の各変数がグローバルスコープに取り込まれる順番も左右します。
$_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 にでもご報告頂ければと思います。
できればこんなライブラリが誰にも使われない方がいい。そんな未来にしていきましょう。
参考
- http://php.net/manual/ja/ini.core.php#ini.register-globals
- PHP における register_globals と $_FILES の関係について - Thanks Driven Life
- PHP 5.4 以上でも register_globals と生きていくために $_SESSION のことも忘れてはいけない - Thanks Driven Life
-
php-5.3.29/main/php_variables.c
-
register_globals
のメインというべき場所
-
-
例えば auto_prepend_file とか。各フレームワークにそういった類のものがあればそこでも大丈夫だと思います。 ↩