Metadata-Version: 2.4
Name: django-cookieless
Version: 1.4
Summary: Django cookie free sessions optional decorator
Home-page: https://github.com/edcrewe/django-cookieless
Author: Ed Crewe
Author-email: edmundcrewe@gmail.com
License: Apache2
Keywords: Django,No cookies,cookieless
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: License :: OSI Approved :: Apache Software License
Description-Content-Type: text/markdown
Requires-Dist: setuptools
Requires-Dist: cryptography
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: requires-dist
Dynamic: summary

# Django Cookieless

Ed Crewe - Sept 2026

## Overview

This package provides a sessions implementation and decorator class for views to
allow forms to maintain state without using cookies, by posting the session ID
between forms, or via URLs.

Django requires cookies to maintain sessions, and hence for authorisation.

This package is designed to cater for anonymous user session maintenance,
without cookies.

**Warning:** there are security issues with this, since it is not possible to
use CSRF protection without session cookies to maintain a separate token from
that passed via the URL or form posts.

However there are cases when forms are used on a public site where setting
cookies is not desirable, for example due to
[privacy legislation](https://ico.org.uk/for-the-public/online/cookies/), since technically they are not
required for anonymous users to respond to forms. So if used, this may
necessitate requesting permission to set cookies from the user.

Hence this package was devised to allow Django to deliver multipage forms,
without using cookies.

To ameliorate the security implications, a whitelist of allowed domains can be
set in the configuration.

Usage can also be restricted to a particular URL.

As another safety measure, handling of GET requests can be turned off, so that
the encrypted session ID is not present in URLs.

Please note, it is not advisable to use this package without some form of the
above restrictions being in place.

For the purposes of using both cookie based and cookieless sessions together,
there is a custom `cookieless_signal(sender=request, created)` and a
`no_cookies` flag when cookieless sessions are saved.

Both cater for hooking up custom code for handling these less secure sessions.

The package provides a decorator utility to turn off cookie setting for
particular views, which also sets the `csrf_exempt` flag.

The package also handles the case of session handling for anonymous users with
cookies disabled in the browser.

You can decorate views to prevent them setting cookies, while still retaining
the use of sessions. Usually this is easiest done in the `urls.py` of your core
application.

```python
from cookieless.decorators import no_cookies

urlpatterns = [
    path("somewhere/index", no_cookies(views.home)),
    re_path(r"^somewhere/page/(\d{1,6})$", no_cookies(views.page)),
]
```

Note that if a number of browser tabs are open on to a site with cookieless,
they will each maintain a completely separate session, since without cookies
the session is tied to the session posted from the pages accessed, not the
client as a whole.

In cases where this is not the desired behaviour, then it can be reduced by
using URL rewriting to make any links to open other windows pass session
across. However this also means that potentially a session can be shared across
browsers.

## Installation

To install add the package via pip or other build tool, for example:

```bash
python -m pip install django-cookieless
```

Then replace the standard session middleware in your settings:

```python
MIDDLEWARE = [
    # "django.contrib.sessions.middleware.SessionMiddleware",
    "cookieless.middleware.CookielessSessionMiddleware",
    ...
]
```

The following settings control behaviour, see the example settings file.

1. Rewrite the response automatically rather than using manual
   `<% session_token %> <% session_url %>`.

   ```python
   COOKIELESS["REWRITE"] = True
   ```

2. Rewrite URLs to add a session ID for `no_cookies` decorated views. If this
   is false then all page navigation must be via form posts.

   Rewriting applies only to in-app `http` and `https` links. External hosts,
   hash-only links, `javascript:` links, and non-http schemes such as
   `mailto:` and `tel:` are not rewritten.

   ```python
   COOKIELESS["USE_GET"] = True
   ```

3. Use client IP and user agent to encrypt the session key, to add some sort
   of CSRF protection given that standard CSRF has to be disabled without
   cookies.

   ```python
   COOKIELESS["CLIENT_ID"] = True
   ```

4. If this list is populated then only hosts that are specifically whitelisted
   are allowed to post to the server. Any domains that the site is served over
   should be added to the list. However, if no referrer is found, the session
   is reset, which will occur with a page reload. This helps protect against
   XSS attacks.

   ```python
   COOKIELESS["HOSTS"] = ["localhost"]
   ```

5. Further security option to avoid finding and persisting cookie based
   sessions as cookieless ones, since these may be tied to a user or other
   data. Instead new sessions are created for cookieless data. This reduces the
   risk of cookieless allowing capture of a user's session, and hence privilege
   escalation attacks.

   ```python
   COOKIELESS["NO_COOKIE_PERSIST"] = True
   ```

6. Further security option to only keep a session for accessing a specific URL.

   ```python
   COOKIELESS["URL_SPECIFIC"] = True
   ```

7. Delete any cookies that are found for a `no_cookies` decorated URL, for
   example ones set by other URLs.

   ```python
   COOKIELESS["DELETE_COOKIES"] = False
   ```

8. Optional, set an explicit shared cipher key for session token encryption.
   If omitted, cookieless derives a stable key from Django `SECRET_KEY`.

   ```python
   COOKIELESS["CIPHER_KEY"] = "shared secret or a fernet key"
   ```

## Tests

The test suite sets up a simple application to test cookies manually, and to
run the functional tests against.

To run the tests, you may want to install from source, or your branch:

```bash
python -m pip install -e .
```

Then run via:

```bash
python -m django test cookieless.tests --settings=cookieless.tests.settings
```

Tested on Django 6.1 and 5.2 with Python 3.14, and in CI on Python
3.13/Django 6.1 and Python 3.12/Django 5.2.

The package was changed from a namespace package due to the issue with pip not
installing `__init__` for running tests when it does an `nspkg.pth` file
instead.

## cookieless/decorator.py

Because the Django test browser has some session implementation specific
mocking, it fails to work if used directly against cookieless. To stop it
breaking other tests, cookieless checks whether `django-admin` has been called
with the `test` argument and sets `settings.TESTING = True`, and does not
decorate with `no_cookies` if so.

To override this automatic disabling setting, add `TESTING = False` to your
test settings.


# Changelog

## 1.4 - 19 Sep 2026

- Derive a stable encryption key from Django `SECRET_KEY` by default, so
  cookieless session tokens survive process restarts.
- Add optional `COOKIELESS["CIPHER_KEY"]` support, accepting either a direct
  Fernet key or a shared passphrase that is deterministically derived.
- Add tests covering cipher-key derivation and override behavior.
- Fix issue/6 by constraining URL rewriting, external links, `#` anchors,
  `javascript:` links, and off-host protocol-relative URLs are no longer
  rewritten, while same-host absolute and protocol-relative URLs still are.

## 1.3 - 19 Sep 2026

- Add Django 5.2 and Django 6.1 compatibility updates. [Ed Crewe]
- Replace deprecated decorator and middleware usage for modern Django.
  [Ed Crewe]
- Convert README to Markdown and update packaging metadata. [Ed Crewe]
- Add CI matrix for Python 3.12/Django 5.2 and Python 3.13/Django 6.1.
  [Ed Crewe]

## 1.2 - 26 Oct 2019

- Fix breach mitigation test for Python 3. [Ed Crewe]
- Fix test settings to correct path. [jonespm]
- Fix content length and decode. [C.Severance]

## 1.1 - 1 May 2019

- Rewrite for Python 3 and Django 2.2.
- Remove xteacrypt and use cryptography.

[Ed Crewe]

## 1.0 - 24 Feb 2014

- Test with latest Django 1.6.2.
- Enforce session keys as strings if the session fails to save, due to the
  move of the session serializer to JSON in Django 1.6.

[Ed Crewe]

## 0.9 - 18 Aug 2013

- Add protection against breach attack via nonce encryption,
  <http://breachattack.com/>.
- Add related test.

[Chris Bailey]

- Remove line return at end of session id introduced by base64 encoding.

[Ed Crewe]

## 0.8 - 4 Jan 2013

- Clean up some pylint.
- Switch to base64 encoding to shorten encrypted session IDs.

## 0.7 - 18 Dec 2012

- Add a `cookieless_signal` to allow custom code to be hooked to cookieless
  sessions.
- Pass a created flag for cookieless sessions to the signal.
- Add a `no_cookies` marker key to cookieless sessions for the same reason.
- Check automatic form rewrites to ensure sessions are not already set
  manually.
- Fix tests check of hidden `session_id` broken by extra space in hidden field.
- Add `post` method for test class view so test posts do not throw
  `HttpResponseNotAllowed`.
- Make the secret generated by settings options more unique.

## 0.6 - 21 Nov 2012

- Never use the cookie session for decorated views, since it may break sessions
  passed by cookieless means.

## 0.5 - 14 Nov 2012

- Only rewrite redirect URLs if `USE_GET` is true and it is the same domain.

## 0.4 - 9 Nov 2012

- Fix issue of not having `no_cookies` to test in `process_request` by getting
  it from url resolvers. Now we only check for cookie session where we should,
  and cookies cannot interfere with cookieless sessions.
- Make deletion of cookies that are passed on to the URL an optional feature.
- Use `settings.TESTING` based on argv to disable, instead of checking server
  name.

## 0.3 - 7 Nov 2012

- Turn off cookieless for Django test browser, since it is hard-coded to use
  dummy sessions if an alternative session provider is in use, otherwise
  cookieless could break other package tests.
- Add server-name switch to re-enable test browser for cookieless functional
  tests.
- Change anon user switch to `NO_COOKIE_PERSIST`, never use cookie-originated
  sessions, move to `process_response`.
- Make session use cookieless POST/GET over cookies when present.
- Delete request cookies if found in response.
- Refactor settings to a dictionary.
- Add tests.
- Move fix for non-unicode key to decrypt method.

[Ed Crewe]

## 0.2 - 6 Nov 2012

- Add `COOKIELESS_ANON_ONLY` setting to avoid cookieless for authorised users.
- Update example settings.
- Add test suite.
- Do not assume request `META` keys exist, so it works with test client.
- Fix session decrypt with wrong secret, generates non-unicode key bug rather
  than new session.
- Add `SPECIFIC_URL` option for extra security for sessions.

[Ed Crewe]

## 0.1 - 4 Nov 2012

- Initial release.
- Django snippets basis for middleware,
  <http://djangosnippets.org/snippets/1540/>.
- Add simple crypt of session ID when used in HTML.
- Call standard `contrib.sessions.Session` if not decorated as `no_cookies`.
- Add CSRF exempt decorator to ensure cookies are not set by that path.
- Add template tags for users who prefer manual adding of session IDs.
- Add settings options to configure level of security applied, for example
  whitelist of referrers and no URL rewriting.

[Ed Crewe, julio carlos and Ivscar (snippet), Paul Chakravarti (xteacrypt)]
