文件上传风险是什么
文件上传的风险不在上传动作本身,而在文件被谁以什么权限解析或执行:上传目录被当作脚本解析、文件名被用于拼接路径、上传内容被其他系统当作可信输入处理,三点任一成立都可能把一次上传变成执行入口。实践中的防护要按层次叠加:扩展名与内容双重校验负责挡掉非法文件,重命名与存储隔离负责让文件无法被直接访问,服务器配置负责切断解析路径。只做其中一层,往往会被组合手法绕过。
基本信息
| 风险类别 | OWASP Top 10 2021 A05 安全配置错误与 A03 注入类;CWE-434 |
|---|---|
| 常见后果 | 上传的脚本被解析执行、覆盖既有文件、作为跳板访问内网 |
| 校验层次 | 扩展名白名单、内容特征校验、存储重命名、目录不可解析 |
| 服务端原则 | 校验全部在服务端完成,前端校验只用于改善体验 |
| 存储原则 | 独立目录或对象存储,文件名与路径均不使用用户输入 |
| 配套控制 | 大小与数量限制、上传者鉴权、下载走鉴权代理 |
常见出问题的地方
- 头像、附件与工单图片上传,只校验扩展名不校验内容
- 编辑器与富文本上传接口,允许的扩展名列表过宽
- 导入功能支持压缩包,解压时不校验文件名与路径
- 上传目录放在站点根目录下且允许脚本解析
- 文件名直接拼接进存储路径,出现目录穿越
- 下载接口按文件 ID 直读存储路径,未做权限校验
服务端校验清单
服务端校验清单
1. 扩展名白名单(按业务需求列出,如 jpg/png/pdf)
2. 内容特征校验(图片解码验证、文件头魔数)
3. 重命名存储(UUID + 固定扩展名),不使用用户文件名
4. 存储目录禁止解析执行(服务器配置层)
5. 大小与数量限制,防止资源耗尽
6. 下载与预览走鉴权代理,不直接暴露存储地址
# 白名单比黑名单可靠:不能枚举的语言与扩展名组合太多
# 校验顺序:先鉴权,再限大小,然后校验类型,最后重命名落盘图片内容校验示意(Python Pillow)
图片内容校验示意(Python Pillow)
from PIL import Image
img = Image.open(path)
img.verify() # 校验是否为有效图片
if img.format not in ('JPEG', 'PNG', 'WEBP'):
raise ValueError('unsupported image format')
# 校验通过后统一重命名并按类型分目录存放
# 注意:解析库本身要定期更新,历史版本存在解析相关的漏洞校验层次与作用
| 层次 | 手段 | 能防住什么 |
|---|---|---|
| 文件名层 | 扩展名白名单、重命名 | 伪装扩展名与特殊字符路径 |
| 内容层 | 魔数校验、解码验证 | 内容与扩展名不符的文件 |
| 存储层 | 独立目录、不可解析、随机名 | 直接访问与非授权下载 |
| 服务层 | 鉴权、限大小与频率 | 滥用上传接口与资源耗尽 |
| 运行层 | 最小权限运行、容器隔离 | 解析漏洞被利用后的影响范围 |
MIME 类型(Content-Type)由客户端提供、可伪造,只能作为辅助信号,不能作为唯一依据。
上传目录的处理方式要与部署形态匹配:动态站点要确保该目录不解析脚本,静态站点要确保不会被当作源码或配置读取,对象存储则要检查桶的读权限与临时链接有效期。