标准答案
- 扩展名只能作为用户体验和初步分类信息,不能决定可信类型;攻击者可以轻易改名。
- 请求头中的 MIME 由客户端声明,也不能完全信任;服务端应结合文件签名、解析器和允许类型列表识别实际内容。
- 对允许的文件类型使用安全解析器,限制解压层数、图片尺寸、文档页数和嵌套内容,并在需要时接入恶意文件扫描和隔离流程。
- 存储路径应使用服务端生成的对象 ID 或随机文件名,不能把原始文件名直接拼进本地路径,防止目录穿越和覆盖。
- 上传、预览、下载、分享和删除都要按资源归属和租户边界授权;私有文件不应仅凭可猜测 URL 或对象键公开访问。
题目解析
文件安全是生命周期问题。上传时安全不代表转换、预览、索引、下载和归档时仍安全,每个后续处理器都可能解析不可信内容。
把用户上传的 HTML、SVG 或文档直接以内联方式在同源域名展示,可能把文件内容转化为脚本执行或钓鱼入口。
代码示例
校验应在可信服务端完成,并将原始文件名与实际存储对象键分离。
TypeScript
const detectedType = await detectFileType(stream)
if (!allowedTypes.has(detectedType.mime)) throw new Error("unsupported file")
const objectKey = "uploads/" + randomUuid()
await objectStore.put(objectKey, stream)
await fileRepository.save({ objectKey, detectedType: detectedType.mime, ownerId })常见误区
- 只检查 .jpg、.pdf 等文件扩展名。
- 用用户文件名拼接磁盘路径或对象存储路径。
- 上传成功后任何登录用户都能通过 URL 下载私有文件。