Have you ever wanted to store data across multiple requests in your Flask web application? Or do you want to remember a user's preferences or keep track of items in their shopping cart?
Sessions can help you do that! Sessions are Flask's way to send data across multiple requests securely. This means you can access the data later without asking the user again.
In this topic, we'll cover everything you need to know about sessions, including what they are, how they work, and how to use them in your Flask web applications. We'll also provide tips for securing your sessions and avoiding common pitfalls.
HTTP and cookies
HTTP is a stateless protocol. This means that each request made by a client to the server is self-contained and independent of any previous requests. This is great for performance, but it can make it difficult to store data between requests.
For example, consider the following scenarios:
- A web application that personalizes the user experience by remembering user's preferences, such as preferred language, other pages they have seen before, the preferred font size, color scheme, and other settings;
- Shopping websites that remember what items are in the cart;
- Keeping track of whether a particular member has viewed a message or contributed content in a group chat application;
- Determining whether the user has logged in or not and providing the right web page accordingly;
- Remember the user's location so the website can display the correct weather forecast.
These are the scenarios where we need a way to pass information between consecutive requests instead of asking the users for their response every single time. One way to solve this problem is to use cookies.
When a server wants to store information about a user, it can send a cookie to the client (browser, application, curl). The cookie will contain a name/value pair, where the name is a unique identifier for the cookie, and the value is the data the server wants to store. The browser will then store the cookie and send it back to the server with subsequent requests. This allows the server to identify the user and retrieve the data stored in the cookie. This is achieved by adding the Set-Cookie header in the HTTP response. This header will contain the cookie's name, value, and other attributes.
In Flask, you can view the content of the request header with request.headers
While cookies resolve the statelessness of HTTP, they pose a severe security issue. For example, cookie data can be intercepted, modified, or stolen if transmitted over an unencrypted connection. They are vulnerable to various kinds of attacks. It's always important to assume that the cookie data you get with each request might have been tampered with; hence, you must always be careful about the type of data you store in cookies and if that data can be used to get other information.
The set_cookies and delete_cookies methods manage cookies in Flask applications. The set_cookies method can be used to create or update a new cookie. The delete_cookies method can be used to delete a cookie. These methods are provided by the Werkzeug package, one of the main packages installed when you install Flask. However, Flask also offers a more secure way to remember the user's state. This is done using the session object.
Flask Sessions
Fingerprints are one of the most unique and personal things about us. They are like snowflakes — no two alike. Even identical twins don't have the same fingerprints! This makes fingerprints a very reliable way to identify a person.
Flask sessions employ a similar approach by implementing digital signatures to ensure the authenticity and integrity of the stored data. With Flask, you can encrypt your cookies using a secret key that you establish initially. Any attempt to modify these signed cookies by a user without knowledge of the secret key results in a distinctive signature from the original. Flask verifies the signature after each request, marking the cookie invalid should any differences arise. This makes sessions much safer than the default cookies.
How to use sessions in Flask
Sessions are very easy to use. They are just your usual Python dictionaries, the only difference being that they can also keep track of modifications, and they will expire and be invalidated automatically after a certain period (expiration time). With that, you can create, access, and delete them the same way you would with regular dictionaries.
os.urandom(x) which will produce a random string of bytes with the length of x.from flask import Flask,session
import os
app = Flask(__name__)
app.SECRET_KEY = os.urandom(32)
# set session data
session['user_id'] = '00001'
session['logged_in'] = True
#modify already existing session data
session['logged_in'] = False
#delete session data
session.pop('user_id') #removes the session data with 'user_id' key
#accessing session data
is_logged_in = session['logged_in'] # if the session doesn't have a data with key "user_id" then this will throw an error.
#OR
is_logged_in = session.get("logged_in", False) # if the session doesn't have a data with key "logged_in" then this will return False.
Sessions are often used to check if the user is in a certain state and then, depending on the state, respond with a specific response.
Let's see such an example:
from flask import Flask, request,session
import os
app = Flask(__name__)
app.SECRET_KEY = os.urandom(32)
@app.route('/',methods = ['POST'])
def index():
language = request.form.get('language')
session['language'] = language
language = session.get('language', 'en') # if 'language' key not found in session, choose english by default
return render_template('index.html', language = language) #change the HTML page accordingly
if __name__ == '__main__':
app.run()
Session cookies structure
Now that you know how to create/use Flask sessions, you may wonder what they look like in the HTTP header. As before, request.headers will give us:
From the image above, you can see that session cookies have the following format:
Cookie: session=[session-data].[Timestamp].[Cryptographic-Hash]
Let's take a look at each part:
Session-data: while the session data might seem unreadable, it is a base64 encoded string and can easily be decoded.Timestampis a UNIX-type time stamp of the last time the cookie was modifiedCryptographic-Hashis the signature, the last and most important part that will ensure no modification is done to the session cookie after it is sent by the server. It is produced cryptographically from the session data and timestamp using your secret key. Every time the client sends the session data, Flask will check if the hash has been changed, which will happen if the session data has been tampered with. If it is altered, this will invalidate the session cookie.
If someone can get their hand on the secret key, they can easily alter the session data without detection from the Flask's end by reproducing the right hash value. Therefore it's essential to have a complex, hard-to-guess secret key.
Let's look at another example: setting, modifying, and accessing session cookies data.
from flask import Flask,request,session
import os
app = Flask(__name__)
app.secret_key = os.urandom(32)
@app.route("/")
def index():
"""display the items in the cart"""
if 'items' not in session:
session['items'] = [] #create empty list session data if it doesn't exist
items = session['items']
return {"cart":items}
@app.route("/add_item")
def add_item():
"""add item to the cart(.../add_item?item=Shirt)"""
item = request.args.get("item")
session["items"].append(item) #use append as the value for the session is a list
session.modified = True
return {"message":"Item added to cart!"}
@app.route("/delete_item")
def delete_item():
"""add item to the cart(.../delete_item?item=Shirt)"""
item = request.args.get("item")
session["items"].remove(item)
session.modified = True
return {"message":"Item deleted from cart!"}
if __name__ == "__main__":
app.run()
Most of the time, when using sessions, we want the initial state to be something specific. We can achieve that using the if/else as in the above example or using the get method to define the session: session['item'] = session.get('item',[], the second argument here will be set as the initial value for the session, as the item key doesn't exist yet.
This code will create a session containing a list of items inside the cart and add/remove items from the cart. You don't have to worry about multiple users; each user's session object will differ.
Managing session lifetimes in Flask
Flask sessions, by default, are set to expire when the browser is closed. However, you can modify this behavior by setting the SESSION_PERMANENT configuration option to True. When SESSION_PERMANENT is True, sessions will last for a duration specified by the PERMANENT_SESSION_LIFETIME configuration option. This option defaults to 31 days, but you can set it to any datetime.timedelta object to specify a different duration. Any changes you make to the session object will be saved and available in future requests until the session expires.
from flask import Flask, session
from datetime import timedelta
app = Flask(__name__)
# Set the secret key
app.secret_key = 'your_secret_key'
# Set SESSION_PERMANENT to True
app.config['SESSION_PERMANENT'] = True
# Set PERMANENT_SESSION_LIFETIME to 7 days
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
# ...Additional tips
We have seen how sessions, which are cryptographically signed cookies, solve the security issues associated with default vanilla cookies. We also noticed that the session variable in Flask is just a dictionary object, so we saw how to set, modify, and delete session data. The following are tips and best practices to remember when working with Flask's sessions.
- Always choose a secret code that will be hard to guess; it's better to use the
ospackage, or other third-party packages instead of trying to create random characters by yourself. - Do not store your secret keys in version control systems. This is a common mistake that can have serious consequences. If you accidentally commit your secret keys to a version control system, they will be exposed to the public. This could allow attackers to access your session data and do much harm.
- To avoid this mistake, it is better to store your secret keys in a separate file that is not tracked by version control. You can then use a package like
python-dotenvto load the secret keys into your main module. This will ensure that your secret keys are never exposed to the public. - Storing sensitive information in session cookies may be unsafe since users or hackers can view the cookie's contents. Session cookies are meant to hold temporary data that does not require high levels of confidentiality. Instead, consider storing sensitive details on the server using tools like databases, Redis, Memcached, etc. Assign a unique user ID to the session cookie and use it to retrieve information from the server. The user ID alone won't reveal any sensitive information. This technique is referred to as server-side handling of session data, in contrast to the default client-side handling provided by Flask. To manage server-side sessions, you can use packages like
Flask-Sessions. - You can use sessions inside Jinja templates as well (
{{ session['user_id'] }}).
Conclusion
In conclusion, sessions in Flask are a valuable tool for maintaining the state between requests. They allow storing user-specific data, such as login information or shopping cart items, without constantly passing that information back and forth between the client and server. By using sessions, Flask developers can create more efficient and personalized web applications for their users.