アップロードファイルによるサーバ側スクリプト実行
発生箇所 | ファイルのアップロード機能を提供するページ |
---|---|
影響を受けるページ | すべてのページが脆弱性の影響を受ける |
影響の種類 | 秘密情報の漏洩、データの改ざん・削除、外部へのDoS攻撃、システムの停止など |
利用者の関与の度合い | 不要 |
概要
アップローダの中には利用者がアップロードしたファイルをWebサーバの公開ディレクトリに保存するものがある。加えてファイル名の拡張子として php, asp, aspx, jsp などスクリプトを表す拡張子が指定できると、アップロードしたファイルをスクリプトとしてWebサーバ上で実行できます。
外部から送り込まれたスクリプトが実行されると、OSコマンド・インジェクションと同様の影響があります。
攻撃手法
例として利用者が画像ファイルをアップロードし、それを公開する機能を考える。
下記はアップロードする画面である(抜粋)。form要素のenctype属性の値"multipart/form-data"によりフォームデータの送信方式を指定しており、ファイルのアップロードが可能となる。
<form action="upload.php" method="post" enctype="multipart/form-data"> ファイル:<input type="file" name="imgfile" size="20"><br> <input type="submit" value="アップロード"> </form>
下記はファイルを受け取って決まったディレクトリに保存した上で画面に表示するスクリプト(upload.php)である。
<?php $tmpfile = $_FILES['imgfile']['tmp_name']; // 一時ファイル名 $tofile = $_FILES['imgfile']['name']; // 元ファイル名 if (!is_uploaded_file($tmpfile)) { // ファイルがアップロードされているか die('ファイルがアップロードされていません'); } else if (!move_uploaded_file($tmpfile, 'img/' . $tofile)) { // 画像を移動 die('ファイルをアップロードできません'); } $imgurl = 'img/' . urlencode($tofile); ?> <body> <a href="<?php echo htmlspecialchars($imgurl); ?>"><?php echo htmlspecialchars($tofile, ENT_NOQUOTES, 'UTF-8'); ?></a> をアップロードしました。<br> <img src="<?php echo htmlspecialchars($imgurl); ?>"> </body>
この機能を使用して画像ファイルをアップロードすれば画像が表示される。
攻撃では画像ファイルの変わりにスクリプトをアップロードする。例として以下のPHPスクリプトをアップロードする。
<pre> <?php system('/bin/cat /etc/passwd'); ?> </pre>
すると、アップロードされたファイルは画像ではないがimg要素の部分にIneternetExplorerの場合、×マークが表示される。
リンクをクリックするとスクリプトが実行されて /etc/passwd が表示される。
アップロードファイルによるサーバ側スクリプト実行による影響は、OSコマンド・インジェクションと同じである。PHPスクリプトが動作するOSアカウントで実行可能な昨日すべて悪用可能である。
脆弱性の原因
脆弱性が生まれる条件は、以下の両方に該当することである。
- アップロードしたファイルを公開ディレクトリに保存する。
- アップロード後のファイル名として "php", "asp" などスクリプトであることを示す拡張子を指定できる。
対策
アップロードされたファイルを公開ディレクトリに保存しないようにする。その場合、ダウンロードスクリプトを使用する。
下記はダウンロードスクリプトを利用する形で upload.php を修正したものである。
<?php define ('UPLOADPATH', '/var/upload'); function get_upload_file_name($tofile) { // 拡張子のチェック $info = pathinfo($tofile); $ext = strtolower($info['extension']); // 拡張子(小文字に統一) if ($ext != 'gif' && $ext != 'jpg' && $ext != 'png') { die('拡張子gif、jpg、pngのいずれかを指定ください'); } // 以下、ユニークなファイル名の生成 $count = 0; // ファイル名再生試行の回数 do { // ファイル名の組み立て $file = sprintf('%s/%08x.%s', UPLOADPATH, mt_rand(), $ext); // ファイルを作成する。既存の場合はエラーになる $fp = @fopen($file, 'x'); } while ($fp === FALSE && ++$count < 10); if ($fp === FALSE) { die('ファイルが作成できません'); } fclose($fp); return $file; } $tmpfile = $_FILES["imgfile"]["tmp_name"]; $orgfile = $_FILES["imgfile"]["name"]; if (!is_uploaded_file($orgfile)) { die('ファイルがアップロードされていません'); } $tofile = get_upload_file_name($orgfile); if (!move_uploaded_file($tmpfile, $tofile)) { die('ファイルがアップロードできません'); } $imgurl = 'download.php?file=' . basename($tofile); ?> <body> <a href="<?php echo htmlspecialchars($imgurl); ?>"><?php echo htmlspecialchars($orgfile, ENT_NOQUOTES, 'UTF-8'); ?></a> をアップロードしました<BR> <img src="<?php echo htmlspecialchars($imgurl); ?>"> </body>
修正点は
・ファイルの格納先を公開ディレクトリから get_upload_file_name() が返すファイル名に変更したこと
・画像のURLをダウンロードスクリプト download.php 経由にしたこと
である。
get_upload_file_name() は拡張子をチェックし、次にユニークなファイル名を生成し、ファイルを作成してクローズし、削除せずにファイルのパスを返す。
その後、move_uploaded_file() によりアップロードされたファイルで返されたパスのファイルを上書きする。もし get_upload_file_name() で作成したファイルを消してしまうとファイル名の一意性が保障されなくなる。
次にダウンロードスクリプト download.php を以下に示す。
<?php // 注意:このダウンロードスクリプトにはクロスサイト・スクリプティング脆弱性があります。 define('UPLOADPATH', '/var/upload'); $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のいずれかを指定ください'); } header('Content-Type: ' . $content_type); readfile(UPLOADPATH . '/' . basename($file)); ?>
このスクリプトはクエリー文字列fileでファイル名を指定する。まず拡張子をチェックし、その後それぞれの拡張子に対応したContent-Typeを出力し、ファイル本体をreadfile()で出力する。
ファイル名をbasename()に通しているのはディレクトリ・トラバーサル脆弱性対策である。
脆弱性発生の原因にアップロードファイル名にスクリプトを示す拡張を指定できることがあったが、それに対する確実な対策は容易ではない。どの拡張子がスクリプトを示すのかは自明ではないためである。
よって、脆弱性の対策としては上記に示したアップロードファイルを公開ディレクトリに保存しない方法を用いる。
参考文献:体系的に学ぶWebアプリケーションの作り方 4.12.2 アップロードファイルによるサーバー側スクリプト実行
![]() | 体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践 徳丸 浩 ソフトバンククリエイティブ 2011-03-03 売り上げランキング : 4070 Amazonで詳しく見る by G-Tools |
« PL/pgSQL | Main | とんでもない地図の本 »
「PHP」カテゴリの記事
- PHPStorm 2018.1.7 に更新(2018.12.09)
- PHPメモ051:includeとrequireの使い分け(2015.06.19)
- CakePHPのインストール(2015.06.14)
- PHPからPDOでPostgreSQLに接続する(2015.06.09)
- PHPでメールを送信したら一部のOutlookで受信したメールでヘッダがおかしくなった(2015.05.31)
「セキュリティ」カテゴリの記事
- PHPメモ041:パスワードのSalt付きハッシュ値(2014.08.09)
- ファイルインクルード脆弱性(2014.05.18)
- ファイルダウンロードによるXSS その3(2014.03.27)
- ファイルダウンロードによるXSS その2(2014.03.23)
- ファイルダウンロードによるXSS その1(2014.02.24)
The comments to this entry are closed.
Comments