My Photo

« 事故現場に遭遇 | Main | PHPメモ037:cURLでHTTPSなサイトにアクセスする »

March 27, 2014

ファイルダウンロードによるXSS その3

関連記事:
ファイルダウンロードによるXSS その1
ファイルダウンロードによるXSS その2

対策

アップロード時とダウンロード時に以下の対策を実施する。

アップロード時の対策
  • 拡張子が許可されたものかをチェックする。
  • 画像の場合はマジックバイトを確認する。

拡張子のチェックについてはアップロードファイルによるサーバ側スクリプト実行で説明した。
マジックバイトの確認にはPHPの場合 getimagesize() という関数が利用できる。

array getimagesize (string $filename [, array &$imageinfo ])
返り値は最大 7 つの要素からなる配列。
0番目および1番目の要素は、それぞれ画像の幅と高さ
2番目の要素は IMAGETYPE_XXX 定数のひとつで、画像の形式を表す。
3番目の要素はIMGタグで直接利用できる文字列 height="yyy" width="xxx"。

この関数は引数として画像のファイル名を受け取り、画像の縦横サイズと画像の形式などを配列として返す。
画像の形式の値の一部を下表に示す。

getimagesize()が返す画像形式の情報(一部)
定数
1 IAMGETYPE_GIF
2 IMAGETYPE_JPEG
3 IMAGETYPE_PNG

アップロードファイルによるサーバ側スクリプト実行で改良したアップロードスクリプトをさらに getimagesize() を使用して改良し、XSS脆弱性に対処する。対処後のスクリプトを upload3.php とする。
画像ファイルのチェック関数 check_image_type() を以下に示す。

// function check_image_type($imgfile, $tofile)
//   $imgfile : チェック対象画像ファイル名
//   $tofile : ファイル名(拡張子チェック用)
function check_image_type($imgfile, $tofile) {
  // 拡張子の取得とチェック
  $info = pathinfo($tofile);
  $ext = strtolower($info['extension']);
  if ($ext != 'png' && $ext != 'jpg' && $ext != 'gif') {
    die('拡張子はpng、gif、jpgのいずれかを指定ください');
  }
  // 画像タイプ取得
  $imginfo = getimagesize($imgfile);
  $type = $imginfo[2];
  // 以下、正常な組み合わせの場合はreturnしていく
  if ($ext == 'gif' && $type == IMAGETYPE_GIF)
    return;
  if ($ext == 'jpg' && $type == IMAGETYPE_JPEG)
    return;
  if ($ext == 'png' && $type == IMAGETYPE_PNG)
    return;
  // 最後までreturnしない組み合わせはエラー
  die('拡張子とイメージ形式が一致しません');
}

upload3.php でこの check_image_type() を呼び出している部分を以下に示す。

$tmpfile = $_FILES['imgfile']['tmp_name'];
$orgfile = $_FILES['imgfile']['name'];
if (! is_uploaded_file($tmpfile)) {
  die('ファイルがアップロードされていません');
}
// 画像のチェック
check_image_type($tmpfile, $orgfile);
$tofile = get_upload_file_name($orgfile);
ファイルダウンロード時の対策
  • Content-Typeを正しく設定する。
  • 画像の場合は、マジックバイトを確認する。
  • 必要に応じてContent-Dispositionヘッダを設定する。

・Content-Typeを正しく設定する
PDFファイルダウンロードによるXSS脆弱性のサンプルは、Content-Typeの間違いが原因だった。Content-Typeを正しく指定すれば脆弱性はなくなる。Content-Typeを正しく指定することはIEに限らずすべてのブラウザで必要である。
ダウンロードスクリプト経由ではなくファイルを公開領域に保存する場合は、Webサーバの設定の確認をする。Aapacheでは mime.types という設定ファイルにContent-Typeの設定が保存されている。あまり使われていないソフトウェアを利用する場合や mime.types を自分で設定した場合は、ブラウザ側で認識できるContent-Typeであることをチェックするすべきである。

・画像の場合はマジックバイトを確認する
ダウンロードスクリプトを用いてファイルをダウンロードする際には、ダウンロード時にもマジックバイトを確認するようにすれば、なんらかの原因で不正なファイルがWebサーバに紛れ込んでいても確実な対策が可能となる。
以下にアップロード時の対策で用いて check_image_type() を利用するダウンロードスクリプトの改良版を示す。

$mimes = array('jpg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif');

$file = $_GET['file'];
$info = pathinfo($file);       // ファイル情報の取得
$ext = strtolower($info['extension']);     // 拡張子
$content_type = $mimes[$ext]; // Content-Typeの取得
if (!$content_type) {
  die('拡張子はpng、gif、jpgのいずれかを指定ください');
}
$path = UPLOADPATH . '/' . basename($file);
check_image_type($path, $path);
header('Content-Type: ' . $content_type);
readfile($path);

・必要に応じてContent-Dispositionを設定する
ダウンロードしたファイルをアプリケーションで開くのではなくダウンロードできさえすればよい場合は、レスポンスヘッダに「Content-Disposition: attachment」を指定する方法がある。この場合は Content-Type も「application/octet-stream」にするとファイルタイプ上も「ダウンロードすべきファイル」とう意味になる。

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="sample.pdf"

・その他の対策
ここまでの対策は脆弱性を防止するための必要最小限のチェックである。例えば、マジックバイトのチェックのみでは利用者のブラウザで放蕩に表示できるかまでは確認できない。
このため、Webアプリケーションの仕様策定時に以下のようなチェックを行うかどうか検討するとよい。

  • ファイルサイズ以外の縦横サイズ、色数などのチェック
  • 画像として読み込めるかどうかのチェック
  • ウイルス・スキャン
  • コンテンツの内容チェック(自動まはた手動
    ・アダルトコンテンツ
    ・著作権を侵害するコンテンツ
    ・法令、公序良俗に反するコンテンツ
    など

参考:利用者のPCに対象アプリケーションがインストールされていない場合

Content-Typeに該当するアプリケーションが利用者のPCにインストールされていない場合、その Content-Type はブラウザにとって未知のものとなり、XSSの可能性がある。この問題の対応は容易ではない。確実な対処として以下の方法がある。

  • コンテンツを配信するサーバのドメインを別にする。
  • Content-Dispositionヘッダを付ける。

上記の方法には副作用があるため、確実性は劣るものの副作用のない対処としては以下の方法があります。

  • アプリケーションが想定したURLかどうかをチェックする。
  • コンテンツの閲覧に必要なアプリケーションの導入を利用者に注意喚起する。

参考文献:体系的に学ぶWebアプリケーションの作り方 4.12.3 ファイルダウンロードによるクロスサイト・スクリプティング

体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
徳丸 浩

ソフトバンククリエイティブ 2011-03-03
売り上げランキング : 4070

Amazonで詳しく見る
by G-Tools

参考サイト:
PHP: getimagesize - Manual

« 事故現場に遭遇 | Main | PHPメモ037:cURLでHTTPSなサイトにアクセスする »

PHP」カテゴリの記事

セキュリティ」カテゴリの記事

Comments

Post a comment

(Not displayed with comment.)

TrackBack

TrackBack URL for this entry:
http://app.cocolog-nifty.com/t/trackback/26461/59365879

Listed below are links to weblogs that reference ファイルダウンロードによるXSS その3:

« 事故現場に遭遇 | Main | PHPメモ037:cURLでHTTPSなサイトにアクセスする »

May 2017
Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof