Giter Club home page Giter Club logo

django-session-csrf's Introduction

What is this?

django-session-csrf is an alternative implementation of Django's CSRF protection that does not use cookies. Instead, it maintains the CSRF token on the server using Django's session backend. The csrf token must still be included in all POST requests (either with csrfmiddlewaretoken in the form or with the X-CSRFTOKEN header).

Installation

From PyPI:

pip install django-session-csrf

From github:

git clone git://github.com/mozilla/django-session-csrf.git

Replace django.core.context_processors.csrf with session_csrf.context_processor in your TEMPLATE_CONTEXT_PROCESSORS:

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'session_csrf.context_processor',
    ...
)

Replace django.middleware.csrf.CsrfViewMiddleware with session_csrf.CsrfMiddleware in your MIDDLEWARE_CLASSES and make sure it is listed after the AuthenticationMiddleware:

MIDDLEWARE_CLASSES = (
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...
    'session_csrf.CsrfMiddleware',
    ...
)

Then we have to monkeypatch Django to fix the @csrf_protect decorator:

import session_csrf
session_csrf.monkeypatch()

Make sure that's in something like your root urls.py so the patch gets applied before your views are imported.

Differences from Django

django-session-csrf does not assign CSRF tokens to anonymous users because we don't want to support a session for every anonymous user. Instead, views that need anonymous forms can be decorated with @anonymous_csrf:

from session_csrf import anonymous_csrf

@anonymous_csrf
def login(request):
    ...

anonymous_csrf uses the cache to give anonymous users a lightweight session. It sends a cookie to uniquely identify the user and stores the CSRF token in the cache. It can be controlled through these settings:

ANON_COOKIE

the name used for the anonymous user's cookie

Default: anoncsrf

ANON_TIMEOUT

the cache timeout (in seconds) to use for the anonymous CSRF tokens

Default: 60 * 60 * 2 # 2 hours

Note that by default Django uses local-memory caching, which will not work with anonymous CSRF if there is more than one web server thread. To use anonymous CSRF, you must configure a cache that's shared between web server instances, such as Memcached. See the Django cache documentation for more information.

If you only want a view to have CSRF protection for logged-in users, you can use the anonymous_csrf_exempt decorator. This could be useful if the anonymous view is protected through a CAPTCHA, for example.

from session_csrf import anonymous_csrf_exempt

@anonymous_csrf_exempt
def protected_in_another_way(request):
    ...

If you want all views to have CSRF protection for anonymous users as Django does, use the following setting:

ANON_ALWAYS

always provide CSRF protection for anonymous users

Default: False

Why do I want this?

  1. Your site is on a subdomain with other sites that are not under your control, so cookies could come from anywhere.
  2. You're worried about attackers using Flash to forge HTTP headers.
  3. You're tired of requiring a Referer header.

Why don't I want this?

  1. Storing tokens in sessions means you have to hit your session store more often.
  2. It's a little bit more work to CSRF-protect forms for anonymous users.

django-session-csrf's People

Contributors

anentropic avatar carljm avatar glogiotatidis avatar jbalogh avatar lucianu avatar moggers87 avatar pcraston avatar peterbe avatar rlr avatar scjody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-session-csrf's Issues

CODE_OF_CONDUCT.md file missing

As of January 1 2019, Mozilla requires that all GitHub projects include this CODE_OF_CONDUCT.md file in the project root. The file has two parts:

  1. Required Text - All text under the headings Community Participation Guidelines and How to Report, are required, and should not be altered.
  2. Optional Text - The Project Specific Etiquette heading provides a space to speak more specifically about ways people can work effectively and inclusively together. Some examples of those can be found on the Firefox Debugger project, and Common Voice. (The optional part is commented out in the raw template file, and will not be visible until you modify and uncomment that part.)

If you have any questions about this file, or Code of Conduct policies and procedures, please reach out to [email protected].

(Message COC001)

Form Submission after Errors throws CSRF Forbidden

I have followed the instructions for this module, and the CSRF tokens seem to work. I have a custom SessionEngine and I can see that the csrf_token is correctly bound to a session.

However, when I submit a form purposely with false data and the form returns with an error, the user is not able to correct their input and submit the form again. On second submit the user receives a CSRF Forbidden message.

I am using django's render() method in my views. So it should take care of all the CSRF for the request/context. Is there anything else that I need to take care of?

CSRF token missing or incorrect

I used this package but am getting an error like this:
A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext. Also when I submit my form I get Forbidden (CSRF token missing or incorrect.)

The test `test_anon_token_from_cookie` is breaking

Pulled down the latest master and ran tests.

======================================================================
ERROR: test_anon_token_from_cookie (session_csrf.tests.TestCsrfMiddleware)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/peterbe/dev/DJANGO/django-session-csrf/session_csrf/tests.py", line 95, in test_anon_token_from_cookie
    self.mw.process_request(request)
  File "/Users/peterbe/dev/DJANGO/django-session-csrf/session_csrf/__init__.py", line 42, in process_request
    if request.user.is_authenticated():
AttributeError: 'WSGIRequest' object has no attribute 'user'

:(

Internal server error

Hello,

I have tried to install your application inside django.

I followed the steps described in the "README.rst" :
-> pip install django-session-csrf
-> add the context processor
-> replace the csrf middleware and check that SessionMiddleware is present
-> fix the @csrf_protect with "import session_csrf" and "session_csrf.monkeypath()" in the "manage.py"

Yet I get a 500 Internal Server Error with no log in Apache.

What should I do to get this working ?

Add changelog

Please add a high level changelog so it's easier to see what to do when upgrading

AttributeError: 'module' object has no attribute '_get_new_csrf_key

In the module session_csrf, I get this error with Django==1.10

session_csrf/__init__.py", line 55, in process_request
token = django_csrf._get_new_csrf_key()
AttributeError: 'module' object has no attribute '_get_new_csrf_key

while running the server locally.

Is it compatibility error with new version of Django

Error in Django 1.10.5 - previously working

Hi there,

I have been using the django-session-csrf successfully for quite some time. But recently I have received an error in my login page:

UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value.  This is usually caused by not using RequestContext.

I have the exact setup as described in the readme.

I am using a FBV for the login with the @anonymous_csrf decorator.

@anonymous_csrf
def login_solo(request):
    if request.user.is_authenticated():
        return redirect('/')

    context = {}
    if request.GET.get('next'):
        next_url = request.GET.get('next', "")
        context.update({'next': next_url})

    form = LoginForm(request.POST or None)
    if request.POST:
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']
            if email and password:
                user = authenticate(email=email, password=password)
                if user is not None:
                    if user.has_fa_enabled():
                        request.session['user_id'] = user.id
                        return redirect(reverse("login_tfa"))
                    else:
                        login(request, user)
                        if user.language is not None:
                            set_user_language(request, user.language.lang_code)
                        render(request, 'login/login_success.html', context)
                else:
                    message = log_failed_login_attempt(request, email)
                    if message != "":
                        messages.error(request, message)

    context.update({'form': form})
    return render(request, 'login/login_solo.html', context)

When I successfully login I like to redirect, which did not work anymore, so my current test code is as above. The line

render(request, 'login/login_success.html', context)

Throws the error and I am presented with a CSRF Failure view.

In my template I am using

{% csrf_token %}

From my setttings.py:

TEMPLATES = [....
'OPTIONS': {
            'context_processors': [
                'django.contrib.auth.context_processors.auth',
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.template.context_processors.i18n',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.tz',
                'django.contrib.messages.context_processors.messages',
                'session_csrf.context_processor',
...


MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'session_csrf.CsrfMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
....
...

I currently run the app via runserver on localhost and proxy via gunicorn.

MemcachedKeyLengthError with a long anoncsrf cookie

From a traceback on affiliates:

Traceback (most recent call last):

File "/data/www/affiliates.mozilla.org/affiliates-app/vendor/src/django/django/core/handlers/base.py", line 89, in get_response
response = middleware_method(request)

File "/data/www/affiliates.mozilla.org/affiliates-app/vendor/src/django-session-csrf/session_csrf/init.py", line 53, in process_request
token = cache.get(PREFIX + key, '')

File "/data/www/affiliates.mozilla.org/affiliates-app/vendor/src/django/django/core/cache/backends/memcached.py", line 58, in get
val = self._cache.get(key)

File "/data/www/affiliates.mozilla.org/affiliates-app/vendor/lib/python/memcache.py", line 779, in get
return self._get('get', key)

File "/data/www/affiliates.mozilla.org/affiliates-app/vendor/lib/python/memcache.py", line 747, in _get
self.check_key(key)

File "/data/www/affiliates.mozilla.org/affiliates-app/vendor/lib/python/memcache.py", line 940, in check_key
% self.server_max_key_length)

MemcachedKeyLengthError: Key length is > 250

Update README with Django=>1.8 settings

Currently it refers to MIDDLEWARE_CLASSES and TEMPLATE_CONTEXT_PROCESSORS only. However, neither of these settings will be used in a new Django app and it's pretty confusing for new users.

Arbitrary session fixation

The middleware will echo any received value of the anoncsrf cookie in the Set-Cookie response as there seems to be no validation its value. This is a bit similar to a session fixation attack but i can't think of an easy way of exploiting this because the cookie must be already there - the ability of echoing an arbitrary string could however be used in some attacks against TLS and possibly DoS.

Request:

Cookie: anoncsrf=%0d%0a%00asd

Response:

Set-Cookie: anoncsrf=%0d%0a%00asd; expires=Fri, 28-Apr-2017 16:10:05 GMT;
      httponly; Max-Age=3600; Path=/; secure

The following code in init.py:151 should probably not echo the cookie value if it wasn't set by the server or at least validate its lenght and charset:

if use_anon_cookie:
            if ANON_COOKIE in request.COOKIES:
                key = request.COOKIES[ANON_COOKIE]
...
if use_anon_cookie:
            # Set or reset the cache and cookie timeouts.
            response.set_cookie(ANON_COOKIE, key, max_age=ANON_TIMEOUT,
                                httponly=True, secure=request.is_secure())

session_csrf accesses POST on non-POST requests, which breaks piston

When session_csrf is used in conjunction with piston to protect a PUT request, things break:

File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/views/decorators/vary.py", line 19, in inner_func
  response = func(*args, **kwargs)
File "/home/scjody/pwbank/src/hg/web-banking/src/trustcentric/resource.py", line 101, in __call__ 
  coerce_put_post(request)
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/piston/utils.py", line 144, in coerce_put_post
  request._load_post_and_files()
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/http/__init__.py", line 360, in _load_post_and_files
  self._post, self._files = self.parse_file_upload(self.META, data)
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/http/__init__.py", line 317, in parse_file_upload
  warning = "You cannot alter upload handlers after the upload has been processed."
File "/home/scjody/pwbank/src/hg/web-banking/lib/python2.6/site-packages/django/http/__init__.py", line 302, in _set_upload_handlers
  raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
AttributeError: You cannot set the upload handlers after the upload has been processed.

This is because of the way Piston works around Django's lack of PUT support: it sets request.method to POST, calls request._load_post_and_files(), and then sets request.method back to PUT. This works fine unless session_csrf is being used, in which case _load_post_and_files() fails with the error above.

I can't think of a good way to fix Piston. A fairly gross way to work around it in session_csrf is to do:

--- a/session_csrf/__init__.py
+++ b/session_csrf/__init__.py
@@ -87,7 +87,9 @@ class CsrfMiddleware(object):

         # Try to get the token from the POST and fall back to looking at the
         # X-CSRFTOKEN header.
-        user_token = request.POST.get('csrfmiddlewaretoken', '')
+        user_token = ''
+        if request.method == 'POST':
+            user_token = request.POST.get('csrfmiddlewaretoken', '')
         if user_token == '':
             user_token = request.META.get('HTTP_X_CSRFTOKEN', '')

This will prevent any request that sets a "csrfmiddlewaretoken" field in a PUT rather than using an X-CSRFToken header from passing CSRF checks, but that wasn't going to work anyway with the current session_csrf (it only checks POST, which will be empty.)

Given that Django's PUT support is lacking anyway - anyone doing a PUT is probably using Piston or Tastypie, which uses _load_post_and_files too - is this the best we can do, or can someone think of a better way?

Should I submit a pull request with this fix given that it's better than what session_csrf currently does?

Push git tags for 0.6 and 0.7

There aren't any git tags for the releases on PyPI and it's kind of hard to see what has changed since there isn't a prose changelog either

Python 3 support

While working on PR #19 I found that django-session-csrf doesn't work under Python 3.

Probably not much work as this module is quite small.

@csrf_protect should apply @anonymous_csrf automatically

django.contrib.auth.views.login uses the @csrf_protect decorator to set up an anonymous token if there is no logged in user.

Having to apply @anonymous_csrf to anything that is wrapped by @csrf_protect is silly, because that decorator means force CSRF protection.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.