flask文件上传必须服务端校验后缀(白名单+secure_filename清洗)和大小(全局max_content_length+视图内手动检查),禁用前端accept/js校验,防范路径遍历、mime伪造、rce等风险。

Flask上传时如何正确校验文件后缀
靠前端 accept 属性或 JS 检查完全不可信,必须在服务端做二次校验。Flask 本身不自动解析 MIME 类型,request.files['file'].content_type 可被客户端随意伪造,真正可靠的是读取文件头(magic bytes)或扩展名白名单比对。
- 优先用文件扩展名 + 白名单控制,例如只允许
['.pdf', '.jpg', '.png'],注意统一转小写再比对 - 用
os.path.splitext(filename)[1].lower()提取后缀,别用filename.split('.')[-1]—— 遇到archive.tar.gz就出错 - 如果真要校验内容类型,装
python-magic库,但注意 Windows 下需额外配置libmagicDLL,Linux/macOS 更稳
保存上传文件前必须处理的路径与文件名风险
用户传来的 filename 是不可信输入,直接拼接 os.path.join(UPLOAD_FOLDER, filename) 会导致路径遍历(如 ../../etc/passwd)或覆盖关键文件。
- 用
werkzeug.utils.secure_filename(filename)清洗文件名,它会删掉所有非 ASCII 字母数字、点、下划线、短横线,并把开头/结尾的点和短横线去掉 - 清洗后仍要检查结果是否为空(比如全为非法字符),避免生成空文件名
- 存储路径不要依赖原始文件名做目录结构,更别用
filename生成子目录;建议用时间戳+UUID重命名,例如f"{uuid4().hex}_{int(time.time())}{ext}"
Flask中限制上传文件大小的两种生效位置
光设 request.max_content_length 不够,它只管整个请求体大小;而单个文件过大可能卡在读取阶段,得配合全局配置和视图内防御。
- 全局设
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024(16MB),超过直接返回 413 错误,不进视图函数 - 但该配置对 multipart/form-data 中多个小文件总和也生效,若业务允许多文件但单个不能超 5MB,就得在视图里手动检查
file.stream.seek(0, 2); size = file.stream.tell(); file.stream.seek(0) - 注意:Nginx 或其他反向代理可能有自己的上传限制(如
client_max_body_size),和 Flask 配置不一致时,请求会在代理层就被拒绝,根本到不了 Flask
为什么不能直接用 file.save() 存到静态目录
看似方便,实则埋下安全雷——如果攻击者上传一个带 BOM 的 .py 文件并命名为 shell.py,又恰好服务器配置不当(如 Apache 允许执行 .py),就可能触发远程代码执行。
- 上传目录必须与 Web 可访问路径隔离,比如存到
/var/uploads/,而不是static/uploads/ - 如果确实需要对外提供下载链接,用 Flask 视图动态读取并返回响应,不要暴露真实路径;或者通过 Nginx 的
X-Accel-Redirect头代理内部路径 - Linux 上给上传目录设好权限:
chown www-data:www-data /var/uploads且chmod 750,禁止 world-writable
事情说清了就结束。最常被跳过的其实是文件名清洗后的空值判断和上传目录的权限隔离,这两处一漏,前面所有校验都白搭。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











