Table of Contents
All communication between the servers and the web browsers and vice-versa is done through stateless HTTP. Because the protocol is stateless, client and server communications are entirely independent of one another; there is no sense of “sequence” or “behavior” depending on previous messages. As a result, you’ll have to develop your website if you want to keep track of ongoing client contacts.
Django and most web frameworks employ sessions to keep track of a site’s “state” and a specific browser. Sessions enable you to save any amount of data per browser and make it available whenever the browser connects to the site. A “key,” which saves and retrieves data, is then used to refer to individual session data elements.
Django uses a cookie with a unique session id to identify each browser and its associated session with the site. By default, the actual session data is saved in the site database rather than in a cookie, more exposed to unwanted or malicious users. Although Django allows you to save session data in various locations like cache, files, and ‘safe’ cookies, the default location is a wise and secure.
Django fully supports anonymous sessions. The session structure allows you to store and retrieve arbitrary data per-site-visitor. It abstracts the sending and receiving of cookies by storing data on the server-side. Cookies store a session ID rather than data unless you use a cookie-based backend.
How to enable sessions in Django
A piece of middleware is used to implement sessions. You can follow these steps to activate session functionality:
Ensure that ‘django.contrib.sessions.middleware.SessionMiddleware’ is in the MIDDLEWARE configuration. Then, SessionMiddleware should be enabled in the default settings.py provided by django-admin startproject.
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
Remove the SessionMiddleware line from MIDDLEWARE and ‘django.contrib.sessions’ from your INSTALLED_APPS if you don’t want to use sessions.
Setting up the session engine
Django saves sessions in your database by default (using the model django.contrib.sessions.models.Session). Though this is useful, some systems require session data to be stored elsewhere. Therefore Django can be configured to store session data on your filesystem or in your cache.
Using database-dependent sessions
You must add ‘django.contrib.sessions’ to your INSTALLED_APPS option if you wish to use a database-backed session.
Run manage.py migrate to install the single database table that keeps session data after you’ve configured your installation.
How to use Cached sessions
You might wish to utilize a cache-based session backend for more incredible speed. Thus, to use Django’s cache system to save session data, make sure your cache is configured correctly. However, Django will use the default cache if multiple caches are configured in CACHES. Set SESSION_CACHE_ALIAS to the name of the cache you want to utilize.
After you’ve configured your cache, you have two options for storing data in it:
For a simple caching session storage, set SESSION_ENGINE to “django.contrib.sessions.backends.cache.” As a result, session data will be saved in your cache directly.
On the other hand, Session data may not be persistent: if the cache fills up or the cache server is restarted. For instance, cached data may be evicted.
Set SESSION_ENGINE to “django.contrib.sessions.backends.cached_db” for permanent, cached data.The latter makes use of a write-through cache, which means that any changes made to the cache are likewise updated in the database. If the data isn’t already in the cache, session reads use the database.
Both session stores are quick, but the simple cache is faster because it doesn’t care about persistence. In most circumstances, the cached_db backend is enough. Still, if you require that extra boost and are ready to allow session data to be deleted occasionally, the cache backend is for you.
You must follow the database-backed session setting instructions to utilize the cached db session backend.
Using sessions based on files
Set the SESSION_ENGINE setting to “django.contrib.sessions.backends.file” to use file-based sessions.
To customize where Django keeps session files, set the SESSION_FILE_PATH setting, which defaults to output from tempfile.gettempdir(), and is most likely /tmp. Check that your Web server has read and write permissions to this directory.
If the SECRET_KEY’s identity is not hidden and the PickleSerializer is used, arbitrary remote code execution is possible.
Because the data is serialized using pickle, an attacker in possession of the SECRET_KEY can not only generate faked session data that your site will believe but also remotely execute arbitrary code.
If you use cookie-based sessions, take extra precautions to ensure that your secret key is kept hidden for any system that may be accessed remotely.
The data in the session is signed but not encrypted.
The client can read the session data when utilizing the cookies backend.
The Message Authentication Code (MAC) is used to safeguard data from client alterations, ensuring that when the session data is changed, the session data is invalidated. For instance, if the client is storing the cookie, your user’s browser cannot save all of the session cookies and drops data, the same invalidation occurs. It’s still possible to go over the standard cookie limit of 4096 bytes as much as Django compresses the data.
There is no assurance of freshness
Note that while the MAC can guarantee the authenticity of the data that was generated by your site and not by someone else and the integrity of the data that it is all there and accurate, it cannot guarantee freshness -that you are getting back the last thing you sent to the client. It means that the cookie backend may expose you to replay attacks if you use session data in specific ways.
Cookie-based sessions upon a user logout are not invalidated, unlike other session backends that preserve a server-side record of each session and invalidate it when a user signs out. As a result, if an attacker takes a user’s cookie, they can log in as that user even after logging out. Stale cookies are only recognized if they are older than your SESSION_COOKIE_AGE.
Sessions in Views
Using sessions in views is a great way to save time
Each HttpRequest object will contain a session attribute, a dictionary-like object when SessionMiddleware is enabled. The latter is the first argument to any Django view function.
You can read and write to request.session at any time in your view. You can make changes to it as many times as you like.
All session objects inherit from this basic class. The standard dictionary has various methods that include:
Example: fav_color = request.session.get(‘fav_color’, ‘yellow’)
Example: fav_color = request.session.pop(‘fav_color’, ‘green’)
Removes the current session data and the session cookie from the session. It is called by the django.contrib.auth.logout() function because it ensures that the prior session data can’t be retrieved again via the user’s browser.
Sets a test cookie to see if the user’s browser is cookie-compatible. Because of how cookies function, you won’t test this until the user requests the next page.
You’ll need to run set_test_cookie() on a previous, separate page request because of the way cookies function. That does not consider whether the user’s browser accepted the test cookie returns as either True or False
Sets the session’s expiration time. You can specify a variety of values:
The session will expire after many seconds of inactivity if the value is an integer. If you use request.session.set_expiry(300), the session will expire in 5 minutes.
The session will expire at that precise date/time if the value is a datetime or timedelta object. Note that only the PickleSerializer allows you to serialize datetime and timedelta values.
When the user’s Web browser is closed, the session cookie will expire if the value is 0.
If the value is None, the session reverts to using the global session expiry policy. For expiration, reading a session is not considered an activity. The last time the session was changed was to calculate the session expiration.
Ensures that the test cookie is deleted. Make use of this to tidy up after yourself.
Returns the number of seconds since session cookies were created. SESSION_COOKIE_AGE is the default value.
The number of seconds till this session expires is returned. It will equal sessions with no custom expiration or those configured to expire when the browser is closed -SESSION_COOKIE_AGE.
This function takes two optional keyword arguments:
modification: the session’s most recent modification, represented as a datetime object. Usually, the current time is the default choice.
expiry: the session’s expiry information as a datetime object, an int (in seconds), or None. If there is no value put in the session by set expiry(), it defaults to None.
The expiration date for this session is returned. It will equal the date for sessions with no custom expiration or those configured to expire when the browser is closed. It will be SESSION_COOKIE_AGE seconds later.
The keyword arguments for this function are the same as those for get_expiry_age ().
If the user’s session cookie expires when the Web browser is closed, this method will return True or False.
Removes expired sessions from the session store with clear_expired(). Clearsessions invokes this class function.
The existing session data is preserved when a new session key is created. This method is called by django.contrib.auth.login() to prevent session fixation.
Serialization of sessions
Django serializes session data using JSON by default. We strongly advise utilizing JSON serialization despite the restrictions in creating your serializer, especially if you’re using the cookie backend. To customize the session serialization format, use the SESSION_SERIALIZER parameter.
The method is straightforward and readily available on the internet. For example, if you use pickle to serialize session data, here’s an attack scenario. Suppose you’re using the signed cookie session backend, and an attacker knows the SECRET_KEY (there’s no inherent vulnerability in Django that would allow it to leak). In that case, the attacker might introduce a string into your session that, when unpickled, executes arbitrary code on the server. A SECRET_KEY leak instantly escalates to a remote code execution vulnerability, even though the cookie session storage signs the cookie-stored data to prevent manipulation.
These are serializers in a bundled class of serializers. JSONSerializer. It is a wrapper for the django.core.signing JSON serializer. Only fundamental data types can be serialized.
Furthermore, because JSON only supports string keys, utilizing non-string keys in request.session will not work as expected.
Data such as non-UTF8 bytes like ‘xd9’, which raises UnicodeDecodeError, can’t be stored either. The latter is because this data cannot be encoded in JSON.
serializers for classes
PickleSerializer supports arbitrary Python objects. However, as previously stated, if an attacker discovers SECRET_KEY, it can lead to a remote code execution vulnerability.
Creating your serializer
The JSONSerializer, unlike the PickleSerializer, cannot handle arbitrary Python data types. As is generally the case, there has to be a trade-off between security and convenience. You’ll need to develop a custom serializer to save more advanced data types in JSON-backed sessions, including datetime and decimal. Or convert such values to a JSON serializable object before storing them in the request.session.
While serializing these values is usually trivial, DjangoJSONEncoder can help develop a decoder that reliably returns the same thing. You run the danger, for example, of producing a datetime that was a string that happened to be in the same format as datetimes).
Your serializer class should implement two methods to successfully serialize and deserialize the session data that comes as a dictionary. These methods comprise dumps(self, obj) and loads(self, data).
Django provides a means to check whether the user’s browser accepts cookies conveniently. Request’s set_test_cookie() function should be used. In a subsequent view, call test_cookie_worked() — not in the same view call.
The way cookies work necessitates this unpleasant separation between set_test_cookie() and test_cookie_worked(). You won’t know whether it was accepted until the browser makes another request when you set a cookie.
Using delete_test_cookie() to clean up after yourself is a good idea. After you’ve confirmed that the test cookie worked, proceed to the next step.
Here’s an example of how to use it:
from django.http import HttpResponse from django.shortcuts import render def login(request): if request.method == 'POST': if request.session.test_cookie_worked(): request.session.delete_test_cookie() return HttpResponse("Welcome. You have logged in successfully.") else: return HttpResponse(" Try enabling cookies and give it a go!") request.session.set_test_cookie() return render(request, user/user_login_form.html')
Persistent sessions vs. browser-length sessions
The SESSION_EXPIRE_AT_BROWSER_CLOSE option determines whether the session framework employs browser-length or permanent sessions.
The latter parameter is set to False by default, which means session cookies will be saved in users’ browsers for the amount of time specified in SESSION_COOKIE_AGE. However, it is the option if you don’t want people to log in every time they open a browser.
Django will employ browser-length cookies if SESSION_EXPIRE_AT_BROWSER_CLOSE is set to True. Browser-length cookies expire when the user shuts their browser. Use this option if you want people to log in every time they open a browser.
This global default can be overwritten by explicitly executing the set_expiry() method of request.session, as mentioned above in the section on using sessions in views.
How to clear the session store
Session data might accumulate in your session store as users establish new sessions on your website. The django_session database table will increase if you’re utilizing the database backend. If you’re using the file backend, the number of files in your temporary directory will grow.
Consider what happens in the database backend to understand the issue better. Django creates a row in the django_session database table when a user logs in. Each time the session data changes, Django updates this row. Django deletes the record if the user logs out manually. However, the row is never erased if the user does not log out. The file backend follows a similar pattern.
Expired sessions are not automatically purged in Django. As a result, it’s your responsibility to remove expired sessions regularly. Clearsessions is a clean-up management command provided by Django for this purpose.
This command should be run regularly, such as with a daily cron job.
Because caches immediately erase outdated data, the cache backend is not prone to this vulnerability. Because the users’ browsers save the session data, neither is the cookie backend.
Subdomains inside a site can set cookies for the entire domain on the client. If cookies from subdomains not controlled by trusted users are allowed, session fixation is possible.
An attacker may, for example, log onto good.example.com and obtain a legitimate session for their account. Because a subdomain is allowed to put cookies on *.example.com, if the attacker has access over bad.example.com, they can use it to relay their session key to you. You’ll be logged in as the attacker when you visit good.example.com, and you may mistakenly submit sensitive personal data (such as credit card information) into the attacker’s account. When example.com’s SESSION_COOKIE_DOMAIN is set to “example.com,” session cookies from that site are delivered to bad.example.com.
You should only employ cache-based sessions to utilize the Memcached cache backend. It will be faster when using database or file sessions directly rather than sending everything through the file or database cache backends because the local-memory cache backend doesn’t keep data long enough. Furthermore, the local-memory cache backend is not multi-process safe, making it unsuitable for production environments.
Some of the memorable Session object guidelines worth remembering include:
- On request.session, use regular Python strings as dictionary keys. It is more of a procedure than a strict requirement.
- Django reserves session dictionary keys that begin with an underscore for internal use.
- Don’t ignore the request.
- Don’t access or set the attributes of a new object during your session. It can be used as a Python dictionary.
Django’s sessions system is totally and solely reliant on cookies. It does not, like PHP, resort to adding session IDs in URLs as a last resort. It is a deliberate design choice. Not only does this make URLs seem bad, but it also exposes your site to session-ID theft via the “Referer” header.