Help us understand the problem. What is going on with this article?

ファイルのPOST時にPHPで拡張子の偽装チェックを行う

こんにちは!

クライアントサイドよりPOSTされたファイルに対して、ディレクトリトラバーサル対策等の他にも着目すべき点はありますが、今回は「クライアントサイドのフォームでPOSTしたファイルのバリデーション」のうち、「ファイルの拡張子の偽装チェック」を行うことに着目します!

そもそも拡張子の偽装とは何なのか

mv sample.png sample.jpg

上記例のように内部バイナリのコンバート処理を行うことなく、拡張子を変更することであります。

しかし、Hexとして上記ファイルを検閲すると、

hex.png

pngファイルと確認することの出来るマジックナンバーがバイナリヘッダーに存在しますね!

では、このファイル名上の拡張子がjpgで内部的にpngであるファイルをクライアントサイドのフォームよりPOSTすると、PHPではどういった挙動になるのか確認してみましょう!

サーバーサイドで拡張子が偽装されたファイルのバリデーションをする

まず前提条件として、下記のフィールドよりPOSTされたファイルを扱うとします。

<input type="file" name="up-file">

その際に、sample.png を sample.jpg にリネームしたファイルをPOSTします。
すると、

echo $_FILES["up-file"]["type"];

では、MIME-Typeとして

image/jpeg

を確認できるはずです。

これは、MIME-Type一覧ページで確認できる通り、jpgファイルとして認識されていることがわかる証拠です。

しかし、

$f_info = finfo_open(FILEINFO_MIME_TYPE); 
$mime = finfo_file(
    $f_info,
    $_FILES["up-file"]['tmp_name'] 
);
finfo_close($f_info); //念の為メモリ空間に開いたストリームの破棄を行う

echo $mime;

として、POSTされたファイルのバイナリからMIME-Typeを取得すると、

image/png

のように本来はpngファイルであることを確認できます。

まとめ

$_FILES["識別子"]["type"]

で確認出来るMIME-Typeは、ファイル名上の拡張子から推測されるものであることがわかりました!

なので、

  • ファイル名上の拡張子から推測できるMIME-Type
  • ファイルのバイナリヘッダーから推測出来るMIME-Type

の合致チェックを行って、よりセキュアにファイルのバリデーションを行おこうと思いました!

以下は、サンプルコードです!

$file = $_FILES["識別子"];

$file_mime_type = $file["type"];

$f_info = finfo_open(FILEINFO_MIME_TYPE);
$binary_mime_type = finfo_file(
    $f_info,
    $file['tmp_name']
);
finfo_close($f_info);

$is_same_mime_type = $file_mime_type === $binary_mime_type;

ありがとうございました!!!

追記

POSTされたファイルのHexをPHP上で確認する方法。

$binary = file_get_contents(
    $_FILES["識別子"]["tmp_name"]
);

echo bin2hex($binary);

bin2hex(正確にはBinary to Hex)でBinaryデータをHexにコンバートします!
上記コード中の

$_FILES["識別子"]["tmp_name"]

には、POSTされたファイルの一時的(temporary)なパスが文字列(String型)として格納されているだけなので、バイナリデータとして扱うためにfile_get_contentsをしています。

あとは、

  1. 16進数にコンバートされたバイナリヘッダーより拡張子の推測を行い、(参照
  2. 拡張子からMIME-Typeの推測を行う(参照

事ができます!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした