なぜ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ヘッダで指定した文字エンコーディングとコンテンツの文字エンコーディングには同じものを使用する
- ユーザが送信したデータなどが原因で複数の文字エンコーディングの文字列が同一ページ上に表示されないように注意する
- ※1
- Firefoxの場合,文字エンコーディングの自動認識の有効/無効は「表示」-「文字エンコーディング」-「自動識別」で設定可能です。「自動識別」は無効にしてブラウズするほうが安全です。
補講
- 【スクリプトインジェクション対策19】ユーザを教育する
- 【スクリプトインジェクション対策18】ログイン処理を正しく実装する
- 【スクリプトインジェクション対策17】パスワードを正しく管理する
- 【スクリプトインジェクション対策16】関連するサイトが利用しているドメイン名の一覧を提供する
- 【スクリプトインジェクション対策15】JavaScriptが無効なクライアントでも利用可能なサイトにする
- 【スクリプトインジェクション対策14】HTML,CSS,JavaScriptの生成はホワイトリスト方式を利用する
- 【スクリプトインジェクション対策13】不正な文字データを保存できないようにする
- 【スクリプトインジェクション対策12】データベースなど,内部データを信用しない
- 【スクリプトインジェクション対策11】エスケープしてはならないデータ以外はすべてエスケープする
- 【スクリプトインジェクション対策10】すべての入力値を可能な限り厳しい条件で検証する
-
Webアプリセキュリティ対策入門〜あなたのサイトは大丈夫?
本書は,Webサイトのセキュリティ確保のために必要な基礎知識と,安全なコードを書くために必要な基礎知識を解説しています。Webアプリケーションは比較的簡単に作成で...
-
はじめてのPHP言語プログラミング入門
Webアプリケーション構築ツールとしてPHPを取り上げた書籍は数多くありますが,言語の解説・入門書としての書籍はあまりありません。 本書は,プログラミング言語として...

