<input type="file" />
<input type="file" />
は手軽にファイルアップロード機能を提供できるタグですが、安易に設定してしまうことで脆弱なアプリケーション・システムを作成できてしまいます。安易にファイルアップロード機能を実装するのではなく、予め潜在するリスクについて知り、その対策を出来る限り取っておく必要があります。
以下に、<input type="file" />
を実装する上で気をつけるベきポイントを調査し列挙しました。
実装例はあくまで参考程度のものです。動作は保証しません。
また実装例はクライアントのみですが、以下に示すValidationチェックは原則クライアント・サーバ双方での実装を心がけてください。
アップロードファイルタイプを限定する
.exe
などの実行ファイルによって、悪意あるスクリプトが実行されてしまう可能性を防げます。
<!-- 拡張子を指定 --> <input type="file" accept="image/jpeg,image/png" onchange="onChange(this)"/>
また念のため、スクリプト内でもチェックしましょう。
function onChange(this) { let file = this.target.files[0]; if (file.type !== 'image/jpeg' && file.type !== 'image/png' ) { // assertion error } }
アップロードファイルサイズを限定する
システム・サービスによっては別の仕様として決まっている場合もあると思いますが、
DOS攻撃を防ぐためにも、一定サイズ以上のファイルは弾くようにしてしまいましょう。
また複数ファイルアップロードをサポートする場合、一度にアップロードするファイル数も同様の理由で限定したほうが良いと思われます。
クライアント・サーバサイドでチェックします。
const MAX_FILE_SIZE = 1024 * 1024 * 10; // 10MB function onChange(this) { let file = this.target.files[0]; if (file.size > MAX_FILE_SIZE) { // assertion error } }
ディレクトリ構造を受け付けない
foo/bar/piyo.jpeg
のようなディレクトリ構造を受け付けず、ファイルのbasenameのみ受け付けるようにしましょう。
指定したファイルパスに対するレスポンス結果からサーバ側のディレクトリ構造を推測する、ディレクトリトラバーサル攻撃の標的になります。
許されるのであれば、ファイル名をハッシュ値やタイムスタンプに置き換えるのも良いと思います。
function onChange(this) { let file = this.target.files[0]; let filePath = file.name.split('/'); let tmpFilePath = file.tmp_name.split('/'); let baseName = filePath[filepath.length - 1]; let tmpBaseName = tmpFilePath[filepath.length - 1]; }
エスケープ処理
例えばSQLのクエリ引数に設定するなど、アップロードしたファイルの情報を別の処理系のパラメータに渡すときはそれらをエスケープ処理しましょう。
SQLの場合、バインド機構などがあればそれも利用しましょう。
本当は、多分やらなきゃいけないことはもっとあるはず。。。