You are correct.
You only need to use the secure_filename function if you are using the value of request.files['file'].filename to build a filepath destined for your filesystem - for example as an argument to os.path.join.
As you're using a UUID for the filename, the user input value is disregarded anyway.
Even without S3, it would also be safe NOT to use secure_filename if you used a UUID as the filename segment of the filepath on your local filesystem. For example:
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
In either scenario you'd then store the UUID somewhere in the database. Whether you store the originally provided request.files['file'].filename value alongside that is your choice.
This might make sense if you want the user to see the original name of the file when they uploaded it. In that case it's definitey wise to run the value through secure_filename anyway, so there's never a situation where the frontend displays a listing to a user which includes a file called ../../../../ohdear.txt
the secure_filename docstring also points out some other functionality:
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'
Answer from v25 on Stack Overflowpython - Secure user-provided filename - Stack Overflow
Uploading and Displaying a Photo in Flask
Import Error when importing secure_filename from Werkzeug.utils : Forums : PythonAnywhere
'Secure' Filenames
» pip install filenames-secure
If you're running in a UNIX variant, you might want a chroot jail to prevent access to the system outside your application.
This approach would avoid you having to write your own code to deal with the problem and let you handle it with infrastructure setup. It might not be appropriate if you need to restrict access to some area within the application as it changes what the process thinks is the system root.
This seems like it would work, provided that open uses the same mechanism to resolve paths as os.path.abspath. Are there any flaws to this approach?
import os
def is_safe(filename):
here = os.path.abspath(".")
there = os.path.abspath(filename)
return there.startswith(here)
>>> is_safe("foo.txt")
True
>>> is_safe("foo/bar/baz")
True
>>> is_safe("../../goodies")
False
>>> is_safe("/hax")
False
So I'm just getting my feet wet with flask, trying to figure out how to upload and display a photo using the flask documentation. I'm running the following code with debug on, and getting a FileNotFoundError: [Errno 2] No such file or directory: '/path/to/the/uploads/XXXXX.jpeg' (I have swapped the file name for XXXXX, but it's actually a long string of numbers and letters).
The code I'm running is:
import os
from flask import Flask, flash, request, redirect, url_for, render_template, send_from_directory
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',
filename=filename))
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form>
'''
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
@app.route("/hello", methods=['POST', 'GET'])
def index():
greeting = "Hello World"
if request.method == "POST":
name = request.form['name']
greet = request.form['greet']
greeting = f"{greet}, {name}"
return render_template("index.html", greeting=greeting)
else:
return render_template("hello_form.html")
if __name__ == "__main__":
app.run()
This is exactly the code from the flask documentation, plus a little bit in the index func from a previous exercise I did with a POST form that collected text input from the user and then displayed it. It seems to me that '/path/to/the/uploads' isn't getting created, so when I upload the jpeg it doesn't go anywhere. It seems to me that app.config should create that path/those directories, but I'm clearly missing something obvious.
» pip install secure-json
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?
In flask_uploads.py
Change
from werkzeug import secure_filename,FileStorage
to
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
According to this issue, it is a bug related to the current version 1.0.0 of workzeug. It's merged but not yet published in pypi.
The workaround know until now is to downgrade from werkzeug=1.0.0 to werkzeug==0.16.0
So for do that you just need run the command:
pip install -U Werkzeug==0.16.0
Looking in the release notes from werkzeug there is a version 0.16.1, but in bug report there is no evidence that using that version could be of any help.