T.Teradaの日記

2007-01-25

[]今更ながらmagic_quotes_gpcの欠点 19:57 今更ながらmagic_quotes_gpcの欠点を含むブックマーク

今更ながらですが、PHPのmagic_quotes_gpcをOnにすべきでない理由を整理してみます。

世の中には、magic_quotes_gpcはOffにすべき、と書いた文章は多数あるのですが、その理由を(私が見る限りで)十分に説明しているものは無いからです。

以下では、

1. 対象外の変数の存在

2. 弊害(副作用)

3. エスケープの不完全さ

という三つの観点から記述します。

対象外の変数の存在

magic_quotes_gpcの、「gpc」はGET/POST/COOKIEを表しています。つまり、この3種類のリソースからの入力はエスケープされますが、それ以外は原則エスケープされません。

エスケープされているもの、されていないものを、以下にまとめてみます。

エスケープ済み

・$REQUEST
  $_GET・$_POST・$_COOKIE

場合によってエスケープ済み

・$_SESSION(セッション)
  GPC由来の変数の場合

・$_FILE(アップロードファイル)
  ファイル名のみ

・DBから得たデータ
  magic_quotes_runtime=Onの場合

・データファイル
  GPC由来の変数の場合

・AP内で生成・加工されるデータ
  ケースバイケース

通常エスケープされていない

・$_ENV(環境変数)

・$_SERVER(ヘッダ等)

・設定ファイル等で定義された変数

・他システムからのデータ


これを見れば判るように、magic_quotes_gpcをOnにした場合、エスケープされている変数とされていない変数がAP内に混在します。これは、漏れや二重エスケープといった誤りを招きやすい危うい状態といえます。

特に漏れの危険性が高いのは、$_SERVERのUser-Agentや、DBから得たデータ、AP内で入力値内の全角文字を半角化した値などです。

弊害(副作用)

magic_quotes_gpcをOnにしたシステムで、生じうる副作用の例を挙げます(ほんの一例です)。

  1. Formで値を持ちまわる際に、「\」が増殖していく。
  2. メールアドレスなどの形式チェックができない。

2についてちょっと補足すると、メールアドレスのlocalpart(@より前)では、「'」はOK、「\」はNGです。しかし、それに従った正規表現で入力値のチェックを行なうと、「'」を含むアドレスははじかれてしまいます。あらかじめ「'」が「\'」に変換されているからです。

つまり、magic_quotes_gpcをOnにした場合、「'」や「\」などの文字を使う可能性があるデータ ―― メールアドレス、パスワード、URL、住所、掲示板の記事 etc. ―― を正確に扱うことが難しくなります。

これらは、HTML出力前や形式チェック前に、stripslashesを掛ければ避けられる問題ではあります。しかし、アドホックなコードを継ぎ足していけば、エスケープ漏れや保守性の低下につながります。

根本的な問題は、magic_quotes_gpcがエスケープを行なうタイミングです。magic_quotes_gpcは、APの前処理の段階でエスケープを行ないます。しかし、形式チェックやHTML出力時には、変数がSQLエスケープされている必要はありません。むしろ、エスケープは邪魔でしかありません。

つまり、エスケープされていない「プレイン」な状態で変数を保持する方が、APにとって都合が良い訳です。そして、SQLエスケープはSQL文を組立てる際に行ないます。それは、SQLエスケープのそもそもの目的を考えても自然なことです。

SQLの組立て時に統一することで、前述したようなエスケープ済みとそうでない変数の混在や、漏れや二重エスケープといった事態を招くことも少なくなります。

エスケープの不完全さ

magic_quotes_gpcは、addslashesというPHPの関数を内部的に呼び出して、エスケープを行ないます。しかし、この関数には2つほど問題があります。

  1. 文字エンコーディング
    (PHPマニュアルには記載されていませんが)latin1を前提としてエスケープ処理を行ないます。それ以外のエンコーディングには対応していません。
  2. 対応するDBMS
    (これもPHPマニュアルには明記されていませんが)MySQLのエスケープ方式をベースに作られているようです*1。しかし実際には、MySQLの行なうエスケープとは、細部で異なっています。

特に前者の問題は深刻で、Shift_JIS、EUC-JP、UTF-8といったエンコーディングでは、安全にエスケープされる保証はありません。実際にShift_JIS+addslashesでは、SQLインジェクションされてしまうことが知られています(Shift_JIS環境でaddslashesを使うことは稀だと思いますが)。

DBMSが提供するエスケープ関数や、バインド関数、もしくはそれらを使いやすいようにラップするPear::DBなどを使うようにするのが望ましいです。

まとめ

  • magic_quotes_gpcはOffにする
  • SQLを構成する際にエスケープする
  • エスケープ時にはRDBMS正規の関数を使用する

もし、既に稼動中のAPがmagic_quotes_gpc=Onを前提としているならば、前述したエスケープ対象外の変数に着目して、エスケープ漏れの有無を調べてみることをお勧めします。

ちなみにPHP6ではmagic_quotes_gpcは無くなるようです*2

*1:実際には、magic_quotes_sybaseという設定により、動作は変わります。

*2:それでも、magic_quotes_gpcをエミュレートするコードが出回りそうですが。

teraccteracc 2007/02/14 08:50 PHP5を前提に記述しています。
PHP4では、SERVER/ENVもmagic_quotes_gpcによってエスケープされるようです。

ぺ 2009/02/09 23:46 Onのときは何がエスケープされて、何がエスケープされないのか意識しないと危険ですね。

matsubobomatsubobo 2009/03/04 13:44 上記には明記されていませんが、magic_quotes_gpcがonの時、$_REQUESTの値がエスケープされます。