2016-04-04T17:57:45Z
How Secure Is The Flask User Session?
Many times I hear people say that user sessions in Flask are encrypted, so it is safe to write private information in them. Sadly, this is a misconception that can have catastrophic consequences for your applications and, most importantly, for your users. Don't believe me? Below you can watch me decode a Flask user session in just a few seconds, without needing the application's secret key that was used to encode it.
So never, ever store secrets in a Flask session cookie. You can, however, store information that is not a secret, but needs to be stored securely. I hope the video makes it clear that while the data in the user session is easily accessible, there is very good protection against tampering, as long as the server's secret key is not compromised.
The Example Application
Want to play with sessions using the "Guess the Number" application I created for the video demonstration? No problem, below you can find the code and templates. As you know, I always put my examples on github, but since this is an example of how not to do things, I am reluctant to have this in there, where people can see it out of context and misunderstand it. So I'm sorry, but for this example, you are going to have to copy and paste the four files.
The application is in file guess.py
:
import os
import random
from flask import Flask, session, redirect, url_for, request, render_template
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') or \
'e5ac358c-f0bf-11e5-9e39-d3b532c10a28'
@app.route('/')
def index():
# the "answer" value cannot be stored in the user session as done below
# since the session is sent to the client in a cookie that is not encrypted!
session['answer'] = random.randint(1, 10)
session['try_number'] = 1
return redirect(url_for('guess'))
@app.route('/guess')
def guess():
guess = int(request.args['guess']) if 'guess' in request.args else None
if request.args.get('guess'):
if guess == session['answer']:
return render_template('win.html')
else:
session['try_number'] += 1
if session['try_number'] > 3:
return render_template('lose.html', guess=guess)
return render_template('guess.html', try_number=session['try_number'],
guess=guess)
if __name__ == '__main__':
app.run()
There are also three template files that you will need to store in a templates
subdirectory. Template number one is called guess.html
:
<html>
<head>
<title>Guess the number!</title>
</head>
<body>
<h1>Guess the number!</h1>
{% if try_number == 1 %}
<p>I thought of a number from 1 to 10. Can you guess it?</p>
{% else %}
<p>Sorry, {{ guess }} is incorrect. Try again!</p>
{% endif %}
<form action="">
Try #{{ try_number }}: <input type="text" name="guess">
<input type="submit">
</form>
</body>
</html>
Template number two is win.html
:
<html>
<head>
<title>Guess the number: You win!</title>
</head>
<body>
<h1>Guess the number!</h1>
<p>Congratulations, {{ session['answer'] }} is the correct number.</p>
<p><a href="{{ url_for('index') }}">Play again</a></p>
</body>
</html>
And the last template is lose.html
:
<html>
<head>
<title>Guess the number: You lose!</title>
</head>
<body>
<h1>Guess the number!</h1>
<p>Sorry, {{ guess }} is incorrect. My guess was {{ session['answer'] }}.</p>
<p><a href="{{ url_for('index') }}">Play again</a></p>
</body>
</html>
If Sessions Aren't Secure Enough, Then What Is?
Now you know that the Flask user session is not the right place to store sensitive information. But what do you do if you have secrets that need to be stored, and using a database seems overkill?
One very good solution is to use a different user session implementation. Flask uses cookie based sessions by default, but there is support for custom sessions that store data in other places. In particular, the Flask-Session extension is very interesting, as it stores the user session data in the server, giving you a variety of storage options such as plain files, Redis, relational databases, etc. When the session data is stored in the server you can be sure that any data that you write to it is as secure as your server.
#1 Usman Ehtesham Gul said 2016-04-05T22:06:02Z
#2 Miguel Grinberg said 2016-04-06T19:35:49Z
#3 Mike said 2016-04-23T17:31:01Z
#4 Abhay Godbole said 2016-05-20T16:50:41Z
#5 Miguel Grinberg said 2016-05-22T00:38:06Z
#6 Abhay said 2016-05-22T06:49:59Z
#7 Iron Fist said 2016-06-19T02:05:06Z
#8 Aris said 2016-08-12T04:31:24Z
#9 Miguel Grinberg said 2016-08-16T19:47:14Z
#10 senaps said 2016-11-28T21:43:00Z
#11 Miguel Grinberg said 2016-11-28T22:24:26Z
#12 QiTian said 2017-01-23T13:11:35Z
#13 Miguel Grinberg said 2017-01-23T15:55:59Z
#14 Flipper said 2017-02-16T00:12:28Z
#15 Miguel Grinberg said 2017-02-16T03:38:31Z
#16 Flipper said 2017-02-16T10:15:24Z
#17 Flipper said 2017-02-17T15:05:58Z
#18 Miguel Grinberg said 2017-02-18T02:10:20Z
#19 khan said 2017-04-16T10:03:02Z
#20 Miguel Grinberg said 2017-04-17T03:00:11Z
#21 Thierry Michel said 2017-08-28T02:06:06Z
#22 Miguel Grinberg said 2017-08-28T07:12:47Z
#23 Deepak said 2017-11-24T13:37:53Z
#24 Miguel Grinberg said 2017-11-24T16:26:49Z
#25 Ali said 2017-11-28T19:41:34Z