Laravel/VueJS Serverless项目AWS S3文件扩展名限制失效求助
策略不生效根因
你之前配置的S3限制策略失效,通常是两类原因:
- 策略仅限制了客户端匿名/普通身份的上传操作,没有覆盖你后端转存时使用的IAM角色的
s3:PutObject权限,导致服务端写入时可以绕开后缀限制 - 策略匹配规则错误,比如只校验了请求的
Content-Type字段没有校验文件最终存储的objectKey后缀,或者匹配路径没有包含Vapor生成的临时文件目录和最终存储目录
多层拦截解决方案
建议从前端、后端、S3策略三层设防,避免单一层被绕开:
1. 前端前置校验(第一道过滤,减少无效请求)
在调用Vapor上传接口前,先做本地扩展名校验,优先用白名单规则而非黑名单:
// 仅允许的扩展名白名单,全部转小写避免大小写绕过 const allowedExtensions = ['.pdf', '.jpg', '.jpeg', '.png'] const selectFile = e.target.files[0] const fileExtension = selectFile.name.slice(selectFile.name.lastIndexOf('.')).toLowerCase() if (!allowedExtensions.includes(fileExtension)) { alert('不支持的文件格式') return } // 校验通过再执行Vapor上传逻辑
注意:前端校验只能拦截普通用户操作,必须有后端和S3层面的校验兜底
2. 后端转存前强校验(核心拦截层)
你当前的流程中后端需要拉取临时文件转存,这一步是最佳校验时机,除了校验后缀还要校验文件真实类型,避免用户改扩展名绕过:
// 先提取临时文件扩展名校验 $tempExtension = strtolower(pathinfo($tempKey, PATHINFO_EXTENSION)); $allowedExt = ['pdf', 'jpg', 'jpeg', 'png']; if (!in_array($tempExtension, $allowedExt)) { Storage::disk('s3')->delete($tempKey); return response()->json(['error' => '文件格式不合法'], 400); } // 再校验文件真实魔数(文件头),确认不是改后缀的违规文件 // 仅拉取文件前256字节即可完成校验,避免大文件占用内存 $fileHeader = Storage::disk('s3')->get($tempKey, [], 0, 256); $finfo = new \finfo(FILEINFO_MIME_TYPE); $realMime = $finfo->buffer($fileHeader); $allowedMimes = ['image/jpeg', 'image/png', 'application/pdf']; if (!in_array($realMime, $allowedMimes)) { Storage::disk('s3')->delete($tempKey); return response()->json(['error' => '文件格式不合法'], 400); } // 所有校验通过后再转存到最终路径
如果用的是Laravel Vapor的内置上传能力,还可以直接在Vapor签名生成阶段就加扩展名限制,不符合要求的文件根本拿不到上传权限,从根源上避免违规文件写入临时目录。
3. S3策略兜底(最后一道防线)
调整S3桶策略,对所有写入操作都做后缀限制,不管是前端临时上传还是后端转存都要命中规则:
{ "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::你的存储桶名称/*", "Condition": { "NotStringLike": { "s3:objectKey": [ "*.pdf", "*.jpg", "*.jpeg", "*.png" ] } } }
如果有更高安全要求,还可以配置S3上传事件触发Lambda自动扫描文件,违规文件自动删除。
内容的提问来源于stack exchange,提问作者Baptiste Briois
相关产品推荐
相关产品推荐

