文件扩展名验证Windows失效Mac正常,如何修复该问题?
问题原因及解决方案
核心问题
你的代码在Windows系统验证失效主要有两个原因:
- 大小写不兼容:Windows文件名不区分大小写,若用户上传
.EXE/.ExE这类大小写混合的文件,原代码的大小写敏感匹配会直接失效。 - 扩展名匹配逻辑错误:原代码中
forbiddenExt数组存储的是带点的扩展名(如.exe),但计算出的fileExtension是不带点的(如exe),本质上includes永远无法匹配——你提到Mac能拦截,大概率是Mac环境下上传的文件扩展名都是小写,且实际代码里forbiddenExt是不带点的,但Windows的大小写问题依然存在。
修正后的代码
方案一:匹配带点的扩展名(推荐)
async upload( @UploadedFile() file: Express.Multer.File, @Body() body: FileUploadDto, ) { const forbiddenExt = ['.exe', '.bat']; const fileName = file.filename || file.originalname; // 正确获取带点的扩展名并统一转为小写 const extIndex = fileName.lastIndexOf('.'); const fileExtension = extIndex === -1 ? '' : fileName.slice(extIndex).toLowerCase(); const hasForbiddenExtension = forbiddenExt.includes(fileExtension); if (hasForbiddenExtension) { throw new HttpException( 'File type is not supported. Upload only .apk or .ipa', 400, ); } const response = await this.uploadFile( `${body.filename}`, file.buffer, ); return { success: true, }; }
方案二:匹配不带点的扩展名
async upload( @UploadedFile() file: Express.Multer.File, @Body() body: FileUploadDto, ) { const forbiddenExt = ['exe', 'bat']; const fileName = file.filename || file.originalname; // 获取不带点的扩展名并统一转为小写 const extIndex = fileName.lastIndexOf('.'); const fileExtension = extIndex === -1 ? '' : fileName.slice(extIndex + 1).toLowerCase(); const hasForbiddenExtension = forbiddenExt.includes(fileExtension); if (hasForbiddenExtension) { throw new HttpException( 'File type is not supported. Upload only .apk or .ipa', 400, ); } const response = await this.uploadFile( `${body.filename}`, file.buffer, ); return { success: true, }; }
额外优化建议
仅靠扩展名验证不安全,用户可以手动修改扩展名绕过限制,建议同时验证文件MIME类型:
async upload( @UploadedFile() file: Express.Multer.File, @Body() body: FileUploadDto, ) { const forbiddenExt = ['.exe', '.bat']; // 常见的危险文件MIME类型 const forbiddenMimeTypes = ['application/x-msdownload', 'application/x-bat']; const fileName = file.filename || file.originalname; const extIndex = fileName.lastIndexOf('.'); const fileExtension = extIndex === -1 ? '' : fileName.slice(extIndex).toLowerCase(); // 同时验证扩展名和MIME类型 const hasForbiddenExtension = forbiddenExt.includes(fileExtension); const hasForbiddenMimeType = forbiddenMimeTypes.includes(file.mimetype); if (hasForbiddenExtension || hasForbiddenMimeType) { throw new HttpException( 'File type is not supported. Upload only .apk or .ipa', 400, ); } const response = await this.uploadFile( `${body.filename}`, file.buffer, ); return { success: true, }; }
内容的提问来源于stack exchange,提问作者saravanan mp
相关产品推荐
相关产品推荐

