'Secure' Filenames
werkzeug - Flask - Do I need to use secure_filename() on uploads to S3/Google Cloud? - Stack Overflow
secure_filename in werkzeug.util is problematic when the file name isnon-ASCII characters
python - cannot import name 'secure_filename' from 'werkzeug' - Stack Overflow
Flask's WSGI library werkzeug has a utility function called secure_filename - you're intended to pass the filename of a user uploaded file to it but I'm having trouble understanding what exactly the concern is, and what securing a filename could possibly entail.
The Pooco site notes
what does that secure_filename() function actually do? 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.
What's returned is the exact same filename except I guess sanitized in some way. What my concern is is that I don't really understand what this function accomplishes. I plan on changing the filename of any user submitted file anyways (random uuid string with a suffix of '-small', '-large', etc.). Does it do me any good to first run the filename through secure_filename() and then change the actual filename? Can I just initially change the filename?
That exception looks like Flask-Uploads is trying to from werkzeug import secure_filename which should be from werkzeug.utils import secure_filename, as per your own code.
Going by the Flask-Uploads github repo this appears to have been fixed 12 months ago.
I'd try pip install -U flask-uploads in your virtual environment, to ensure the latest version.
EDIT:
As @mattficke points out, the PyPi version is dated, and there's not a more recent release on the repo. Turns out you can install directly based on a commit hash, so for the latest (at the time of writing this):
pip install git+https://github.com/maxcountryman/flask-uploads.git@f66d7dc
Or in a requirements.txt:
git+https://github.com/maxcountryman/flask-uploads.git@f66d7dc
Then pip install -r requirements.txt.
Which works wonders:
>>> from flask_uploads import UploadSet,configure_uploads,IMAGES,DATA,ALL
>>> # No Exception
Alternatively to above suggested solution, you can use the well maintained fork called Flask-Reuploaded.
You do not even have to change import statements, as it tries to stay compatible with the no longer well maintained Flask-Uploads.
See https://github.com/jugmac00/flask-reuploaded
Also, Flask-Reuploaded certainly provides uptodate packages on PyPI:
https://pypi.org/project/Flask-Reuploaded/
Flask-Uploads is actually using the patterns found in Flask's documentation for file upload handling. It uses werkzeug.secure_filename, it provides a way to set MAX_CONTENT_LENGTH if, for some reason, you are using Flask 0.5 or older, and it provides a way to validate files based on their extension.
In fact, Flask's documentation actually explicitly suggests using Flask-Uploads:
Because the common pattern for file uploads exists almost unchanged in all applications dealing with uploads, there is a Flask extension called Flask-Uploads that implements a full fledged upload mechanism with white and blacklisting of extensions and more.
I ended up with using my own secure_filename in my case that supports Arabic:
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