なぜPHPアプリにセキュリティホールが多いのか?

【スクリプトインジェクション対策05】文字エンコーディングは必ずHTTPヘッダで指定する

クロスサイトスクリプティングの危険性を解説した「CERT Advisory CA-2000-02 Malicious HTML Tags Embedded in Client Web Requests」⁠2000年2月)には、クロスサイトスクリプティングを防止する対策として文字エンコーディングを明示的に指定すべきある、と明確に記載されています。

In addition, web pages should explicitly set a character set to an appropriate value in all dynamically generated pages.
加えて、動的に生成されたすべてのWebページは適切な文字コードセットを明示的に設定されなければならない

としています。

<meta>タグを利用すると
<meta http-equiv="content-type" content="text/html; charset=iso-2022-jp">

などと文字エンコーディングをページ中に記載可能ですが、文字エンコーディングを指定するmetaタグ以前にユーザ入力を出力していると、UTF-7エンコーディングを利用したり、ブラウザの文字エンコーディング自動認識機能により、文字エンコーディング指定を解除される可能性があります。このような問題は以前から知られていた問題であり、RFCではこの問題を回避するための規定があります。

RFC2616のセクション3.4.1 Missing Charsetでは

HTTP/1.1 recipients MUST respect the charset label provided by the sender; and those user agents that have a provision to "guess" a charset MUST use the charset from the content-type field if they support that charset, rather than the recipient's preference, when initially displaying a document.

と、HTTP/1.1クライアントはContent-Typeヘッダのcharset設定に⁠必ず⁠従うこと、と記載されています。

また、セクション 3.7.1には

When no explicit charset parameter is provided by the sender, media subtypes of the "text" type are defined to have a default charset value of "ISO-8859-1" when received via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST be labeled with an appropriate charset value.

と、テキストの文字エンコーディングがcharsetで指定されない場合はISO-8859-1として取り扱い、ISO-8859-1以外の場合は⁠必ず⁠適切な文字エンコーディングを指定するようにと記載されています。

インターネットで利用されるプログラムがRFCに準拠していないことは珍しくありませんが、ブラウザも上記の仕様に準拠していません。RFCに規定されていない文字エンコーディングの自動認識機能のため、スクリプトインジェクションが可能になっています[1]⁠。

ブラウザがRFCに準拠していないとしても、リスクを減らすために必ずHTTPヘッダで文字エンコーディングを指定すべきです。文字エンコーディングを指定しないと、より安全にインターネットを利用するために文字エンコーディングの自動認識機能を無効にしているブラウザでは文字化けが発生してしまいます。PHPの場合、php.ini設定のdefault_charsetに文字エンコーディングを指定します。ここに文字エンコーディング名を次のように指定すると

ini_set('default_charset', 'Shift_JIS');

以下のようなContent-Typeヘッダが送信されます。

Content-Type: text/html;charset=Shift_JIS

しかし、PHPのデフォルト設定は空文字列で、文字エンコーディング指定なしとなり、以下のContent-Typeヘッダが送信されます。

Content-Type: text/html

default_charsetはini_set関数でPHPスクリプトからも変更できますが、上記のようにcharsetが設定されない状況を防ぐためにphp.iniでデフォルト文字エンコーディングを設定するほうが好ましいです。

対策のまとめ

  • プログラム中から必ずdefault_charset設定を利用してHTTPヘッダで文字エンコーディングを指定する
  • プログラムで設定漏れがあった場合にそなえ、php.iniなどの設定ファイルでもdefault_charsetを指定する
  • HTTPヘッダで指定した文字エンコーディングとコンテンツの文字エンコーディングには同じものを使用する
  • ユーザが送信したデータなどが原因で複数の文字エンコーディングの文字列が同一ページ上に表示されないように注意する

おすすめ記事

記事・ニュース一覧