secure_filename()是Werkzeug工具包中的一个实用函数(Flask内置了Werkzeug),专门用于处理用户上传的文件名,确保文件名是安全且符合文件系统要求的。
基本功能 #
secure_filename()会对传入的文件名进行处理:
- 去除路径信息(防止路径遍历攻击)
- 只保留ASCII字母、数字、下划线和点
- 其他字符会被替换为下划线
- 确保文件名不以点开头或结尾(某些系统隐藏文件)
基本用法 #
from werkzeug.utils import secure_filename
filename = secure_filename(user_input_filename)示例 #
print(secure_filename("../../etc/passwd")) # 输出: etc_passwd
print(secure_filename("My cool file 123!.jpg")) # 输出: My_cool_file_123_.jpg
print(secure_filename("中文文件名.txt")) # 输出: _.txt
print(secure_filename(".hidden_file")) # 输出: hidden_file为什么需要使用secure_filename #
- 安全考虑:防止路径遍历攻击(如
../../etc/passwd) - 兼容性:确保文件名在不同操作系统上都能正常工作
- 规范化:避免文件名包含特殊字符导致的问题
实际应用场景 #
最常见的应用场景是处理用户上传的文件:
from flask import Flask, request
from werkzeug.utils import secure_filename
import os
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return 'No file uploaded', 400
file = request.files['file']
if file.filename == '':
return 'No selected file', 400
if file:
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return 'File uploaded successfully'局限性 #
非ASCII字符:会完全删除非ASCII字符(如中文、日文等)
- 解决方案:可以结合
uuid或自定义逻辑保留有意义的部分
- 解决方案:可以结合
扩展名保留:虽然会保留最后一个点后面的内容,但可能不适合所有情况
处理非ASCII字符的增强方案 #
如果需要保留非ASCII字符,可以考虑以下方法:
from werkzeug.utils import secure_filename
import unicodedata
import re
def enhanced_secure_filename(filename):
# 转换为NFKD标准化形式
filename = unicodedata.normalize('NFKD', filename)
# 移除非字母数字字符(保留点、下划线和连字符)
filename = re.sub(r'[^\w\-\.]', '_', filename).strip('._')
return filename
print(enhanced_secure_filename("中文文件-测试123.txt")) # 输出: 中文文件_测试123.txt安全建议 #
即使使用了
secure_filename,也应该:- 限制允许上传的文件类型(通过文件扩展名或内容检查)
- 设置合理的文件大小限制
- 考虑将上传的文件存储在非Web可访问的目录
- 对上传的文件进行病毒扫描
对于生产环境,考虑使用专门的存储服务(如AWS S3、阿里云OSS等)
secure_filename是Flask文件上传处理中不可或缺的安全工具,能有效防止许多与文件名相关的安全问题。