我正在尝试决定如何在我的烧瓶应用程序上实现图像上传功能。 我目前正在使用 Flask-Uploads 来完成工作,它似乎工作得很好。 但是,我不知道它是否非常安全,这可能是一个大问题,因为涉及文件上传。 Flask-Uploads 并没有真正提供有关其服务实现的详细信息,因此我没有通过查看文档获得任何见解。 然而,我看到 Flask 的官方文档中包含了一个使用 Werkzeug 上传文件的示例,它似乎有一些额外的方法用于文件安全。 我似乎在网络上找不到任何可以说明哪一个更安全的信息。 这里有没有拥有更多网络安全经验的人检查过其中一种或两种替代方案,并就这个问题得出明确的结论?
Flask-Uploads 实际上是使用在Flask 文件上传处理文档中找到的模式。 它使用
werkzeug.secure_filename
,如果出于某种原因,您使用 Flask 0.5 或更早版本,它提供了一种设置 MAX_CONTENT_LENGTH
的方法,并且它提供了一种根据文件扩展名验证文件的方法。
事实上,Flask 的文档实际上明确建议使用 Flask-Uploads:
由于文件上传的通用模式在所有处理上传的应用程序中几乎没有变化,因此有一个名为 Flask-Uploads 的 Flask 扩展,它实现了完整的上传机制,包括扩展的白名单和黑名单等。
我最终使用了我自己的支持阿拉伯语的
secure_filename
:
import os
import re
import unicodedata
# edit this to add more characters
_filename_ascii_strip_re = re.compile(r"[^A-Za-z0-9\u0600-\u06FF_.-]")
_windows_device_files = {
"CON",
"PRN",
"AUX",
"NUL",
*(f"COM{i}" for i in range(10)),
*(f"LPT{i}" for i in range(10)),
}
def secure_filename(filename: str) -> str:
# Normalize the filename to handle UTF-8 characters
filename = unicodedata.normalize("NFKC", filename)
# Replace path separators with spaces
for sep in os.sep, os.path.altsep:
if sep:
filename = filename.replace(sep, " ")
# Remove unsafe characters
filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip("._")
# Ensure the filename is not a reserved device name on Windows
if os.name == "nt" and filename and filename.split(".")[0].upper() in _windows_device_files:
filename = f"_{filename}"
return filename
def secure_filename(filename):
if isinstance(filename, text_type):
from unicodedata import normalize
filename = normalize('NFKD', filename).encode('utf-8', 'ignore')
if not PY2:
filename = filename.decode('utf-8')
for sep in os.path.sep, os.path.altsep:
if sep:
filename = filename.replace(sep, ' ')
filename = str(_filename_gbk_strip_re.sub('', '_'.join(
filename.split()))).strip('._')
if os.name == 'nt' and filename and \
filename.split('.')[0].upper() in _windows_device_files:
filename = '_' + filename
return filename
因此,
secure_filename
仅支持 ASCII,建议的原因是根据文档实现最大可移植性,这意味着如果文件名包含日语字符,它将返回空文件名,在这种情况下,从上述函数派生的以下代码可能会有所帮助。
文档: https://werkzeug.palletsprojects.com/en/2.2.x/utils/
def secure_filename(filename: str) -> str:
r"""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('i contain cool \xfcml\xe4uts.txt')
'i_contain_cool_umlauts.txt'
The function might return an empty filename. It's your responsibility
to ensure that the filename is unique and that you abort or
generate a random filename if the function returned an empty one.
.. versionadded:: 0.5
:param filename: the filename to secure
"""
filename = unicodedata.normalize("NFKD", filename)
filename = filename.encode("ascii", "ignore").decode("ascii")
for sep in os.sep, os.path.altsep:
if sep:
filename = filename.replace(sep, " ")
filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip(
"._"
)
# on nt a couple of special files are present in each folder. We
# have to ensure that the target file is not such a filename. In
# this case we prepend an underline
if (
os.name == "nt"
and filename
and filename.split(".")[0].upper() in _windows_device_files
):
filename = f"_{filename}"
return filename
我确实建议您也查看 Flask-Uploads,因为如上所述,它遵循 werkzeug 中 secure_filename 函数内提供的相同模式。我添加了 UTF-8 支持,指出您可能遇到的问题和源代码,以便您可以了解 werkzeug 究竟如何设法提供安全文件名。