Shift_JISを利用することの是非

 前回に引き続き、はじめてのPHPプログラミング 基本編5.3対応のゆるいところ第二段は、文字エンコーディングについてだ。
 本書は、文字エンコーディングとしてShift_JISを採用している。しかし、本家から配布されているWindows用バイナリのPHP処理系は、「--enable-zend-multibyte」というオプションが設定されていないため、2バイト目が0x5Cで終わる文字が正しく扱えない。このため、以下のPHPスクリプトはエラーになる。いわゆる5C問題である。著者には、ブログのコメントとして確認した。


<?php
$a = "表";
?>

 文字列リテラルを"表\"としてやればエラーは回避できるが、わずらわしいし、第一みっともない(前世紀のスクリプトみたいだ)。
 やはり、ソースコードEUC-JPかUTF-8で記述すべきだったと思う。前に取り上げたPHP×携帯サイト デベロッパーズバイブルの方は、携帯電話向けサイトで入出力にはShift_JISを用いるにも関わらず、内部コードとしてはEUC-JP(本の中では「EUC」と記載)を使うよう勧めている。そうするべき理由はあまり明確に書いていないが、実用的ではある。
 さて、ソースコード上の5C問題は、通常直接的には脆弱性とはならないが、Shift_JISを使うとなるとやはり脆弱性が気になる。本書の場合はどうだろうか。
 私がさくっと試した範囲ではXSSなどの「貫通」はできなかった。しかし、本の説明には危なっかしいところもある。以下のような箇所だ。


(htmlspecialcharsは)通常は第2引数まで指定すればほぼ問題ありませんが、念には念を入れるのであれば第3引数(文字エンコーディング)まで指定した方が良いでしょう。
「はじめてのPHPプログラミング基本編5.3対応」P203より引用

 「念には念を入れるのであれば」とあるが、どのような場合に「念を入れる」べきかの説明はない。正解は、「第3引数は常に指定すべき」である。一方、私が調べた範囲では貫通できなかったと書いたが、調べてみると、php.iniに以下の設定があるかららしい。


mbstring.encoding_translation = On

 この設定がOffの場合は、半端な0x81を用いて貫通が可能となった。mbstring.encoding_translationがOnの場合は、HTTPリクエストの文字列から、内部コードへの自動変換がされる。この場合は、Shift_JISからShift_JISへの変換だが、その過程でShift_JISとして無効な文字を除去するらしい。
 しかし、セキュリティの条件がmbstring.encoding_translationの設定に依存してしまうのは危険だと思う。この設定はOffにした方がよいという指摘もあるし、そもそも文字エンコーディングの妥当性をチェックする目的の設定ではないので、もっと明示的に文字エンコーディングのチェックを入れるべきだと思う(mb_check_encoding関数を用いるとよい)。

徳丸からのお勧め