はじめに
Laravelでアップロードされたファイルの拡張子をバリデーションするmimes
ルールに
ついて、誤った認識をしていたため忘れないよう記事にしました。
結論
mimes
ルールは拡張子をみているわけではなくファイルの内容を読み取って取得したMIMEタイプをみているため注意が必要。
(ドキュメントをしっかり確認するべきでした。。)
ファイルは、リストする拡張子のいずれかに対応するMIMEタイプを持っていることをバリデートします。
実験
.txtファイルを許可するバリデーションに、SQLが書かれたhoge.sql
をアップロードして$validator->errors()->first('file')
にエラーメッセージが入っているか確認します。
class TestRequest extends FormRequest
{
public function rules()
{
return [
'file' => [
'mimes:txt',
],
];
}
public function after(): array
{
return [
function (Validator $validator) {
dd($validator->errors()->first('file'));
}
];
}
}
SELECT * FROM users;
結果
バリデーションエラーにならず通った。
$validator->errors()->first('file')
は空でした。
どうしてバリデーションエラーにならずに通ったのか
ドキュメントに理由が記載されていました。
拡張子を指定するだけでもよいのですが、このルールは実際には、ファイルの内容を読み取ってそのMIMEタイプを推測することにより、ファイルのMIMEタイプをバリデーションします。
このバリデーションルールは、MIMEタイプとユーザーがファイルに割り当てた拡張子との一致をバリデートするものではありません。例えば、mimes:pngバリデーションルールは、ファイル名がphoto.txtであっても、有効なPNGコンテンツを含むファイルを有効なPNG画像とみなします。
このことから.txtファイルをアップロードしたとしても、中身が空ならMIMEタイプはnull
となり、'mimes:txt'
ルールに弾かれてバリデーションエラーとなります。
ファイルの拡張子でバリデーションするには?
extensions
ルールを使用します。
ユーザーがファイルへ割り当てた拡張子をバリデートしたい場合は、extensionsルールを使用してください。
今回の場合はこのようにすると、ファイルの内容を読み取って取得したMIMEタイプと
拡張子が.txtのファイルのみを許可することになり、hoge.sql
を通さなくなります。
'file' => [
'mimes:txt',
'extensions:txt',
],
注意点としてextensions
だけだとファイルの中身はチェックされないため、mimes
またはmimetypes
と組み合わせて使う必要があります。
※バージョン9以前のドキュメントにはextensions
ルールの記載がなかったので使用できないかもしれません。
ちなみに
ファイルの拡張子を取得するgetClientOriginalExtension()
がありますが、こちらはextensions
ルールと同じくファイルの内容はチェックしていません。
ファイルの内容を見て拡張子を取得するにはextension()
を使用する必要があります。
ドキュメントに以下の記載がありました。
悪意のあるユーザーによりファイル名や拡張子が改竄される可能性があるため,getClientOriginalNameとgetClientOriginalExtensionメソッドは安全であると考えられないことに注意してください。そのため,アップロードされたファイルの名前と拡張子を取得するには,通常,hashNameメソッドとextensionメソッドを使用するべきです。
dd($this->file('file')->getClientOriginalExtension());
// 'sql'
dd($this->file('file')->extension());
// 'txt'
最後に
ドキュメントをちゃんと読みましょう。