経緯
- 2011/1/1 徳丸が「PHPのescapeshellcmdの危険性」を書いて、クォート文字がペアになっている場合にエスケープしないという仕様が余計なお世話であり、危険性が生じていることを指摘
- 2011/1/7 大垣さんがブログエントリ「phpのescapeshellcmdの余計なお世話を無くすパッチ」にて修正案を提示
- 2011/10/23 廣川さんが、大垣さんのパッチ案を少し修正してbugs.php.netに提案。修正案は却下され、マニュアルを修正することに
- 2012/3/1 PHP5.4.0リリース。実はescapeshellcmdの仕様が変わっていた
MLでの議論
大垣さんと廣川さんのパッチ提案に対して、lbarnaudさんが、「いやいや、escapeshellcmdはコマンド全体をエスケープするもので、提案のような使い方は想定してないよ、マニュアルの例も間違いだ」と指摘します。歴史的に見て、この指摘は正しいように思いました。そうでないと、escapeshellcmdとescapeshellargという別の関数が存在する理由が説明できません。
すなわち、元々PHPには、コマンド全体をエスケープするescapeshellcmdが存在したが、それだとコマンドの引数を追加する攻撃が出来てしまうので、escapeshellargという関数が後から追加されたのでしょう。
ということで、escapeshellcmdは修正せず、マニュアルの方を修正するということで決着がつきました。詳細は、bugs.php.netの方を参照下さい。マニュアルは既に修正されています。
ところが・・・
PHP5.4.0でescapeshellcmdの仕様が変わっていた
このエントリは、上記の顛末を報告するつもりで書き始めたのですが、念のためPHPの様々なバージョンでescapeshellcmdの挙動を確認したところ、PHP5.4.0でこの関数の仕様が変わっていたことが分かりました。サンプルスクリプトは以下です。
<?php
echo phpversion(), "\n";
$a = 'echo "foo bar" baz"';
echo $a, "\n";
echo escapeshellcmd($a), "\n";
system(escapeshellcmd($a));
PHP5.3.10での実行結果
5.3.10
echo "foo bar" baz"
echo "foo bar" baz\"
foo bar baz"
PHP5.4.0での実行結果
5.4.0
echo "foo bar" baz"
echo foo bar baz\"
foo bar baz"
「' および " は、対になっていない場合にのみエスケープされます」ではなく、「対になっている' および " は削除されます」という動作に変わっています。その結果、echoの表示も、複数の空白が一つにまとめられています。マニュアルには、この仕様は記載されていないようですし、変更履歴にも載っていません。
この修正も余計なお世話としか思えませんが、私の結論は変わりません。
ということで、先のエントリの結論を再掲します。
まったく余計なお世話としか言いようがありません。この仕様では、恐ろしくてescapeshellcmdは使えませんし、マニュアルにここまではっきり書いてある仕様を今さら変えられないでしょう。escapeshellcmdはお蔵入りするしかないと思います。幸い、escapeshellargの方はまともな仕様と思われますので、escapeshellargで代替してください。
ただし、そもそもOSコマンドを呼ぶのがよいかとか、もっと良い方法はないのかという疑問が出てきます。もっと良い方法はあります。それは、本が出てからのお楽しみ、ということで。
追記
廣川さんに確認したところ、上記はPHP5.4.0のバグだそうで、PHP5.4.1で元の仕様に戻るだろうということです。PHP5.4.0でescapeshellcmdを使用する際はご注意ください。廣川さん、確認ありがとうございました。[PR]
Webサイトのセキュリティ強化策についての相談は、HASHコンサルティング株式会社まで。
「安全なWebアプリケーションの作り方」DRMフリーのPDFによる電子版もあります。
0 件のコメント:
コメントを投稿