文件上传的安全边界:别只检查后缀

一个名为 picture.jpg 的文件,不一定是有效图片;请求头声明为图片,也不代表内容真的符合声明。上传入口需要把文件当作待检查的输入,而不是已经可信的资源。

本篇目标: 列出一个通用上传流程的关键边界,并理解每层检查各自负责什么。

上传文件经过一条受控路径
上传文件经过一条受控路径放大图解 ↗

限制发生在接收过程,而不只在结束之后

验证当前身份是否允许上传,并限制文件大小、数量和请求频率。流式接收时也要累计已接收字节,超过上限及时结束,避免等整个文件进入内存后才发现太大。

压缩包、超大尺寸图片和复杂文档还可能在解码时消耗远超文件大小的资源。因此,接收上限与处理资源上限需要分别考虑。

用允许类型清单识别内容

先明确业务真正需要哪些类型。结合扩展名、媒体类型与实际格式识别,并通过受控解码器验证内容。对可重新编码的图片,可以在处理后输出新的标准文件,而不是直接沿用未经处理的原始字节。

上传策略应组合多层防护,不能把其中一种识别方式视为万能验证。OWASP:文件上传检查表

存储名由服务端决定

不要把用户提供的路径或文件名直接拼接到存储目录。可以生成独立对象标识,将展示名称作为元数据保存。存储位置不应允许上传文件被当作服务端程序执行。

展示名:spring-notes.png
存储键:服务端生成的不可预测对象标识
元数据:类型、尺寸、大小、归属、检查状态

文件路径越界、重名覆盖和特殊字符,应在命名规则中明确处理。展示文件名时也需要按所在上下文转义。

下载同样需要边界

文件上传成功不意味着任何人都能读取。私有文件应在每次访问或受控票据签发时验证归属与权限,公开文件也要使用正确的响应类型与下载策略。

验证时可以使用扩展名与内容不一致的文件、超过大小上限的流、异常尺寸图片、特殊文件名和未经授权的下载请求。记录错误类型即可,不必把文件原文或私密文件名直接写进普通日志。上传、处理、存储和访问四段都走通,才是一条完整的文件链路。

见字如晤

图解