Flask - 我需要在上传到 S3/Google Cloud 时使用 secure_filename() 吗?

Flask - Do I need to use secure_filename() on uploads to S3/Google Cloud?

file uploads 的 Flask 文档中,他们建议使用 secure_filename() 在存储文件之前清理文件名。

这是他们的例子:

uploaded_file = request.files['file']
if uploaded_file:
    filename = secure_filename(uploaded_file.filename) # <<<< note the use of secure_filename() here
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return redirect(url_for('display_file',
                            filename=filename))

文档说:

Now the problem is that there is that principle called “never trust user input”. This is also true for the filename of an uploaded file. All submitted form data can be forged, and filenames can be dangerous. For the moment just remember: always use that function to secure a filename before storing it directly on the filesystem.

使用异地存储(S3 或 Google 云),我不会使用 Flask 将文件存储在网络服务器上。相反,我将重命名上传文件(使用我自己的 UUID),然后将其上传到其他地方。

示例:

blob = bucket.blob('prompts/{filename}'.format(filename=uuid.uui4()))
blob.upload_from_string(uploaded_file.read(), content_type=uploaded_file.content_type)

在这种情况下,我说得对吗不需要需要先调用secure_filename()

似乎是因为我 (a) 将文件内容读入字符串,然后 (b) 使用我自己的文件名,所以我的用例不容易受到目录遍历或流氓命令类型的攻击 (例如 "../../../../home/username/.bashrc") 但我不是 100% 确定。

你是对的。

如果您使用 request.files['file'].filename 的值来构建指定给您的文件系统的文件路径,则只需要使用 secure_filename 函数 - 例如作为 os.path.join 的参数。

当您使用 UUID 作为文件名时,用户输入的值无论如何都会被忽略。

即使没有 S3,如果您使用 UUID 作为本地文件系统上文件路径的文件名段,那么不使用 secure_filename 也是安全的。例如:

uploaded_file = request.files['file']
if uploaded_file:
    file_uuid = uuid.uuid4()
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], file_uuid))
    # Rest of code

在任何一种情况下,您都会将 UUID 存储在数据库中的某个位置。是否存储最初提供的 request.files['file'].filename 值是您的选择。

如果您希望用户在 他们 上传文件时看到文件的原始名称,这可能有意义。在这种情况下,通过 secure_filename 运行 值绝对是明智的,因此永远不会出现前端向用户显示包含名为 ../../../../ohdear.txt[=23= 的文件的列表的情况]


the secure_filename docstring 还指出了一些其他功能:

Pass it a filename and it will return a secure version of it. This filename can then safely be stored on a regular file system and passed to :func:os.path.join. The filename returned is an ASCII only string for maximum portability. On windows systems the function also makes sure that the file is not named after one of the special device files.

>>> secure_filename("My cool movie.mov")
'My_cool_movie.mov'
>>> secure_filename("../../../etc/passwd")
'etc_passwd'
>>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
'i_contain_cool_umlauts.txt'