フィルター/デコード時のセキュリティ対策の鉄則

11月 29, 2013 PHP Security, Security
(Last Updated On: 2018年8月4日)

ブラックリスト型(サニタイズ型)のセキュリティ対策クイズ 解答編 ではバリデーション時の注意点をまとめに書きました。

ホワイトリスト型のバリデーションを行う場合でも以下の項目に注意しなければなりません。

  • 特殊な意味を持つ文字を許可する場合、細心の注意を払う
  • そもそも特殊な意味を持つ文字は許可しない方が良い
  • 絶対の自信が無いのであれば、特殊な意味を持つ文字を許可してはならない

ブラックリスト型(サニタイズ型)のセキュリティ対策クイズ 解答編 でパストラバーサルを禁止するようなフィルター処理(ブラックリスト型サニタイズ処理)は基本的には行うべきではありません。しかし、どうしてのフィルター/デコード処理が必要になる場合があります。

正当な理由でフィルター/デコード処理が必要となる代表例はUnicode文字列の正規化です。Unicode文字列のカタカナや記号を統一する為には正規化処理が必要になります。

PHPでは意識する必要はありませんが、URLリクエストの文字列はURLエンコードされています。普通はURLエンコードされた文字列はデコードして利用します。

Webアプリではフィルター/デコード処理は必ず必要になる処理です。フィルター/デコード処理を行う場合に必ず行うべき鉄則があります。

ブラックリスト型(サニタイズ型)のセキュリティ対策クイズ 解答編 の脆弱なサンプルスクリプトをもう一度書きます。

<?php
if ($_GET['filename']{0} === '/') {
   // 絶対パスは無効
   die('無効なファイル名が送信されました。');
}
// トラバーサルに利用される"../", ".."を削除
$safe_path = str_replace(array('../', '..'), '', $_GET['filename']);
// カレントディレクトリ以下のファイルだけ読み込む?
readfile($safe_path);

このサンプルスクリプトには脆弱なサニタイズ処理である問題の他に、フィルター/デコード処理時の鉄則を守っていません。この為、ファイルシステムに自由にアクセスできてしまう脆弱性を作っています。

以下のコードで “/” で始まる絶対パスを禁止しているのですが、

if ($_GET['filename']{0} === '/') {
   // 絶対パスは無効
   die('無効なファイル名が送信されました。');
}

その後のサニタイズ処理(フィルター処理)で “/” で始まるセキュリティ処理を台無しにしています。

// トラバーサルに利用される"../", ".."を削除
$safe_path = str_replace(array('../', '..'), '', $_GET['filename']);

(なぜ台無しにしているのか分からなかった方は ブラックリスト型(サニタイズ型)のセキュリティ対策クイズ 解答編 を参照してください)

セキュリティ処理はフィルター/デコード処理の後に行う」という鉄則を守っていない事が脆弱性を作ってしまった原因です。

絶対パスに変換できる脆弱なサンプルスクリプトも、この鉄則を守っていればより安全になっていました。つまり、フィルター処理(サニタイズ処理)の後に、セキュリティ処理である文字列先頭の”/”を禁止する処理を行っていれば”/”から始まる絶対パスによるアクセスは行えなくなります。

<?php
// トラバーサルに利用される"../", ".."を削除
$safe_path = str_replace(array('../', '..'), '', $_GET['filename']);
if ($_GET['filename']{0} === '/') {
   // 絶対パスは無効
   die('無効なファイル名が送信されました。');
}
// カレントディレクトリ以下のファイルだけ読み込む?
readfile($safe_path);

修正前のスクリプトよりは安全になりましたが、このスクリプトもまだ脆弱です。このようなサニタイズ処理は基本的には行ってはなりません。(繰り返しになりますが、なぜ脆弱なのか分からなかった方は ブラックリスト型(サニタイズ型)のセキュリティ対策クイズ 解答編 を参照してください)

まとめ

セキュリティ処理はフィルター/デコード処理の後に行う」はフィルター/デコード処理を行う場合の鉄則です。必ずセキュリティ処理はフィルター/デコード処理の後に行わなければなりません。

この鉄則を守っていない事が原因で、大手ソフトウェア会社のセキュリティ対策にも利用されるプロキシーサーバーが脆弱になってしまっていた、という例もあります。

私も同じですが、プログラマはセキュリティ処理を先に書きたがる傾向があります。しかし、フィルター/デコード処理がある場合は後に行わないと様々な脆弱性の原因になります。

この他にも二重(多重)デコード処理もセキュリティ問題の原因になります。デコード/フィルター済みのデータか分からないし、既にデコード/フィルターされた文字列なら再度デコード/フィルター処理しても大丈夫なハズ!と複数回デコードしてはなりません。

  • セキュリティ処理はフィルター/デコード処理の後に行う
  • デコード/フィルター処理は一度だけ行う(絶対に複数回処理しない)
  • 2つ目の鉄則には稀に例外がある。デコード/フィルター処理を行ったらセキュリティ処理を必ず実行する。

この鉄則を守るだけでよりセキュアなコーディングが可能になります。

楽しく、セキュアなコーディングをしましょう!

お知らせ

昨日FacebookにPHP Securityのページを作成しました。私がこのブログのPHPセキュリティ関係のエントリを投稿します。他のPHPセキュリティ、Webセキュリティ関係の情報・製品・サービスなどの投稿も大歓迎です。よろしければ「いいね!」&投稿をお願いします。全ての質問に答えられるか分かりませんが、質問も歓迎します。

作ってまだ24時間経っていないのにもう直ぐ100いいね!です。

いいね!してくださった方、ありがとうございます!

投稿者: yohgaki