サーバーへのユーザーのアップロードを処理するスクリプトに取り組んでおり、セキュリティの追加レイヤーとして知りたいです:
ファイルの本当の拡張子/ファイルの種類を検出し、それが別の拡張子でマスクされた別のファイルの種類ではないことを確認する方法はありますか?
タイプ/拡張子ごとにバイトスタンプまたは一意の識別子はありますか?
誰かがアップロードしているファイルに別の拡張子を適用していないことを検出できるようにしたいと考えています。
サーバーへのユーザーのアップロードを処理するスクリプトに取り組んでおり、セキュリティの追加レイヤーとして知りたいです:
ファイルの本当の拡張子/ファイルの種類を検出し、それが別の拡張子でマスクされた別のファイルの種類ではないことを確認する方法はありますか?
タイプ/拡張子ごとにバイトスタンプまたは一意の識別子はありますか?
誰かがアップロードしているファイルに別の拡張子を適用していないことを検出できるようにしたいと考えています。
そうではありません。
各ファイルの最初の数バイトを読み取り、既知のファイルタイプの有限セットのヘッダーとして解釈する必要があります。ほとんどのファイルには個別のファイル ヘッダーがあり、MP3 の場合は最初の数バイトまたは最初の数キロバイトに何らかのメタデータがあります。
プログラムは、受け入れられたファイルタイプごとにファイルを解析するだけで済みます。
私のプログラムでは、アップロードされた画像を try-catch ブロックで imagemagick に送信します。失敗した場合、それは悪い画像だったと思います。これは、一般に攻撃ベクトルである外部プログラムに任意の (ユーザー提供の) バイナリ データをロードしているため、安全ではないと見なす必要があります。ここでは、imageMagick が自分のシステムに何もしないことを信頼しています。
攻撃ベクトルを回避するために、使用する予定の重要なファイル タイプに対して独自のハンドラを作成することをお勧めします。
編集:PHPには、これを行うためのツールがいくつかあります。
また、MIME タイプは、ユーザーのブラウザがファイルに要求するものです。これらを読み取ってコード内で操作するのは便利で便利ですが、不正なファイルを送信する人が MIME ヘッダーを簡単に偽造するため、安全な方法ではありません。JPEG を期待するコードが PNG を妨害しないようにすることは、一種の最前線の防御ですが、誰かがウイルスを .exe に埋め込んで JPEG という名前を付けた場合、MIME タイプを偽装しない理由はありません。
PHP には、使用している PHP のバージョンに応じて、ファイルの内容を読み取って MIME タイプを判別する方法がいくつかあります。
PHP 5.3 以降を実行している場合は、Fileinfo 関数を確認してください。
$finfo = finfo_open(FILEINFO_MIME);
$type = finfo_file($finfo, $filepath);
finfo_close($finfo);
または、古いバージョンのmime_content_typeを確認してください 。
$type = mime_content_type($filepath);
本当に安全にしたい場合は、ファイルの種類を検証するだけでは不十分であることに注意してください。たとえば、誰かが一般的なレンダラーの脆弱性を悪用する有効な JPEG ファイルをアップロードする可能性があります。これを防ぐには、適切に管理されたウイルス スキャナーが必要です。
PHP には、サイズやファイル タイプなどの情報を保持するスーパーグローバルな $_FILESがあります。タイプは拡張子ではなく、ある種のヘッダーから取得されているように見えますが、間違っている可能性があります。
w3schools サイトにその例があります。
機会があれば、だまされるかどうかをテストするつもりです。
アップデート:
他の誰もがおそらくこれを知っていましたが、$_FILES はだまされる可能性があります。私はこのようにそれを決定することができました:
$arg = escapeshellarg( $_FILES["file"]["tmp_name"] );
system( "file $arg", $type );
echo "Real type: " . $type;
基本的に Unix のfileコマンドを使用します。おそらくもっと良い方法がありますが、私はしばらく PHP を使用していません。私は通常、可能であればシステム コマンドの使用を避けます。
拡張子を変更した場合は、MIME タイプを取得する以下のコードを使用できます。
$finfo = finfo_open(FILEINFO_MIME_TYPE);
echo $mime = finfo_file($finfo, $_FILES['userfile']['tmp_name']);
finfo_close($finfo);
Windows ユーザー: php.ini を編集して、次の行のコメントを外します。
extension=php_fileinfo.dll
新しい php.ini を有効にするために、忘れずに Apache を再起動してください。
それはまだ偽造される可能性があります。サーバーに自動的にアップロードされたファイルを実行できない (または実行しない) ことを確認します。
また、ウイルス/スパイウェア スキャナーも用意しておりますので、そちらに任せてください。
MIME タイプのチェックだけで十分ですか? ファイルの拡張子を変更しても MIME タイプは変わらないと思いますか?
MIME タイプは、ここを通過するのに十分強力な指標ですか?
これまでのすべての応答に感謝します。
*nix では、ファイルの最初の 2 バイトでわかります (「マジック ナンバー」を参照)。Windows では、これが当てはまる場合もあります (「ヘッダー情報」)。最終的には、OS に依存します。
一般に、実行可能ファイルには最初のバイトに「署名」があります。ファイルの種類が実際に何であるかを実際に確認するのは難しいと思います。
どのようなファイルの種類を期待していますか? おそらく、それが期待どおりであることを確認し、それ以外はすべて拒否することができます。
他の人はすでに FileInfo について言及していますが、これは正しい解決策だと思いますが、何らかの理由でそれを使用できない場合に備えて、これを追加します。ほとんどの (すべて?) *nix ディストリビューションにはfile
、ファイルで実行するとそのタイプを出力するというコマンドが含まれています。人間が読める形式 (デフォルト) または MIME タイプで出力するためのスイッチがあります。アップロードされたファイルに対してスクリプトでこのプログラムを呼び出し、結果を読み取ることができます。繰り返しますが、これは推奨されるアプローチではありません。Windows を使用している場合、このユーティリティは Cygwin から利用できます。
MIME タイプのチェックだけで十分ですか? ファイルの拡張子を変更しても MIME タイプは変わらないと思いますか? MIME タイプは、ここを通過するのに十分強力な指標ですか?
それは本当に使い方次第です。