Flask Python 3 Secure User Uploads File
Writing a multi-file-upload Python-web app with user authentication
Providing an like shooting fish in a barrel method for collaborators to upload files to a filestore without the need for them to understand whatever lawmaking whatsoever
When edifice a webserver we often wish to present an thought or topic. In the example of a static website, this can be done past including the relevant information within the source files. In more dynamic examples, an API (application programming interface) tin can exist used to pre-process information before returning it to the user.
In this example, we pattern a unproblematic flask app which only requires a user primal and a web browser to work. The idea backside this is that anyone tin can utilise it, and it is a device (and operating system), independent.
Creating the flask app
As usual, we commencement by installing the relevant libraries for the server. The simplest way to do this is through the apply of pip (pythons package manager).
pip install flask, werkzeug The upload-page template
Next, create our HTML template for the login page. To practice this we offset in our application directory and brand a new file named templates . Within this, nosotros create a file named upload.html and paste the following lawmaking within it.
In essence, we take a form which contains our password input, a file upload input and a submit button. When working this will expect as follows
Setting up the upload options
Next, we define a config file for our upload options. To exercise this create a file named config.py in your main app folder. Within this, we can specify a maximum accustomed file size, the upload destination, and what file extensions we can cull to take. Copy the code below into config.py and conform accordingly.
A notation on file types: File extensions do not guarantee file type and nosotros should avert executing any uploaded files. This is why we later introduce a password — such that but approved users can upload.
A flask template
We begin with a bones flask template, which imports our information and serves the upload.html page when navigating to http://127.0.0.1:4000/upload
Upload file checks
Now we have our app, we tin add a number of functions to ensure that nosotros take the right directory, and if not create it.
if not os.path.isdir(upload_dest):
os.mkdir(upload_dest) Nosotros tin can set the maximum file upload size (using the value from config.py)
app.config['MAX_CONTENT_LENGTH'] = file_mb_max * 1024 * 1024 and also bank check the file extensions (once more defined in config.py)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[ane].lower() in extensions Personally, I have added these betwixt app.secret and the @app.route sections within the lawmaking.
What to do on upload
Finally, we have withal to tell the program what to practise upon uploading. We do so this by parsing the post asking from the upload page. For information on what this means — take a read of:
Here we cheque if the asking.method is a POST, and if then handle any files attached. The showtime section deals with an empty asking, whilst the second iterates through each file, checks that they have the correct file extension, and if so saves them to our desired location (upload_dest).
@app.route('/upload', methods=['Mail service'])
def upload_file():
if request.method == 'Mail': if 'files[]' not in asking.files:
flash('No files found, effort over again.')
return redirect(request.url) files = request.files.getlist('files[]') for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.salvage(os.path.bring together( upload_dest, filename)) flash('File(due south) uploaded')
return redirect('/upload')
Testing the usage case
Finally, before standing on to calculation passwords, we want to make sure that the upload functions as intended. To practise this we run the python script, navigate to http://127.0.0.1:4000/upload, select a few files and click upload. If this has been successful, and yous are using the configuration above, your files should now reside within an uploads_folder directory nested in the aforementioned binder as your app.
Calculation an encrypted database for user authentication
And so far we take created a Flask app which we tin can use to upload files. Adjacent, we want to add a layer of basic security, such that nosotros tin can track what files accept been uploaded and by whom. In addition, this allows usa to pass up anyone not authorised to commit data to the directory.
Installing pysqlcipher3
We begin by installing our database tool. This tin can sometimes cause bug for people, so the installation instructions for Mac and Linux have been included below.
MAC
brew install SQLCipher
pip install pysqlcipher3 LINUX
sudo apt install sqlcipher libsqlcipher0 libsqlcipher-dev
sudo -H pip3 install pysqlcipher3 python3 -c 'import pysqlcipher3; print(pysqlcipher3.__path__)'
Connecting to the database
We get-go by connecting to the database. If you accept ever used databases before, the pysqlcipher syntax is pretty much the same as sqlite, or any postgresql libraries. We begin by importing the libraries
from pysqlcipher3 import dbapi2 as sqlite3
from config import app_key, db_loc And then connecting to the database:
conn = sqlite3.connect(db_loc)# where this is /path/examination.db
cursor = conn.cursor() Finally, nosotros need to specify an encryption key for our database in order to be able to admission the data.
cursor.execute("PRAGMA key='%southward'"%app_key) If you do non exercise this, or use a different central, you volition receive the post-obit error: DatabaseError: file is not a database when trying to read data from the database schema.
Creating the Database
Having opened our database, and entered our key, we tin can now create a table to store our values. We do this by executing a create table SQL control with the cursor execute function. The simplest usage case would require a proper name and an upload_key.
cursor.execute(
'''
CREATE TABLE IF Not EXISTS upload (
id INTEGER NOT NULL PRIMARY Fundamental AUTOINCREMENT,
proper noun TEXT NOT Naught,
upload_key TEXT UNIQUE
);
'''
) Additionally, I have gear up a status that each primal has to be unique, as we are not using a user name to log on.
Finally, we have to commit our new tabular array to the database and close it.
conn.commit()
## conn.shut()
## close but when we have finished everything, otherwise nosotros accept to reopen the database each time Adding a User
We demand users to be able to use the upload tool, and then we tin can add some using the insert command.
cursor.execute(
'''
INSERT INTO upload (proper noun, dir, uploadcode)
VALUES ("bob", "bobs elevation clandestine upload key")
'''
conn.commit() Reading the Database
Equally a bank check, nosotros want to see if at that place is a name associated with the upload_key. This can exist washed with the select function, coupled with a where conditional.
user_code = 'bobs top secret upload key' cursor.execute('select * from upload where uploadcode="%south"'%user_code) upshot = cursor.fetchall() # become all the results e.thousand. [("bob",)]
Stringing it all together
Now we take a database and an upload script, we can combine the ii.
Opening the database
Firstly we add together the required library
from pysqlcipher3 import dbapi2 every bit sqlite3
# from config import app_key, db_loc # already imported under the from config_simple import *line.
Reading the countersign
If y'all are using the HTML lawmaking from earlier, nosotros already take a password input field :<p> upload_key: <input proper noun="psw" type="password" /></p>
As this is within the submission class nosotros can read information technology as function of the Postal service request in the @app.route wrapper.
user_code = str(asking.form.become('psw')) We combine this with our database read to course a conditional checking if there is anyone with that access primal /countersign.
cursor.execute('select * from upload where uploadcode="%southward"'%user_code)
result = cursor.fetchall() if len(result)==0:
flash('Not a valid Code')
return redirect(request.url)
However, since the database can only be opened and read from the aforementioned computational thread as the website we demand to identify
conn = sqlite3.connect(db_loc)
cursor = conn.cursor()
cursor.execute("PRAGMA key='%s'"%app_key) earlier the cursor.execute block, and a conn.close() afterward the outcome = line. (see app.py in the GitHub repo at the cease)
Conclusion
And in that location nosotros accept it — the most basic of submission boxes which allows for the uploading of files past pre-canonical users. There are withal a number of improvements that yous may wish to make (these are all in the additional files shown in the GitHub repository below).
- Checking the filename cord for an extension can cause bug. Files may be named incorrectly, or even non take an extension (every bit was the example with many of my files). Instead, nosotros can filter on the
file.mimetypevalues. These are of the formatimage/pngetc. - Drag and Drop uploading. It tin frequently exist cumbersome to manually select files, particularly if they are in different locations. Dragging them from a file browser makes life much simpler for the user.
- Automatic uploads. Another common mistake is to select a file, simply forget to click submit. Having a window that works immediately after you driblet a file in it can help assist productivity and user satisfaction
- File previews. If the user is uploading images, it tin always be helpful to have miniature previews available. This allows a terminal-minute check such that you are not repeatedly submitting the same file.
- File failure indicators. Although there is a message informing us of the upload status, a visual cue (ie a lighter preview paradigm) makes it much easier to see if something has not worked.
- Unique download spaces — if you lot accept many users, it can go confusing which file belongs to who (non to mention the overwriting problem). Instead, we tin add a designated space for each user, and salvage the paths within the database.
Adding all the additional suggestions above and nosotros the following output.
Here nosotros see, two files have failed, with one succeeding in the middle. In this case, it was due to invalid user credentials and an intentionally invalid filetype.
Disclaimer: this is not the most secure method of checking credentials, however for the purposes of many tasks it tin exist a perfectly adequate solution.
Codebase
All the example code for this tutorial has been dumped (quite literally) into a Github repository.
If you lot have any improvements, experience gratis to submit a pull asking.
Source: https://towardsdatascience.com/writing-a-multi-file-upload-python-web-app-with-user-authentication-8f75064b819a
0 Response to "Flask Python 3 Secure User Uploads File"
Post a Comment