はじめに
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'
最後に
ドキュメントをちゃんと読みましょう。