3

I am making the backend for a mobile app and using Django with Userena for the user management. I made the sign in and sign up using Django REST framework and everything works fine. The only thing I need to do now is to implement the "forget password" functionality. I wanted to use the already implemented one from Userena, but I cannot get rid of the error "CSRF token missing or incorrect" even after using the csrf_exempt dectorator. What am I doing worng?

urls.py

from django.contrib.auth.views import password_reset
from django.views.decorators.csrf import csrf_exempt
...
urlpatterns = patterns(
    '',
    url(r'^password/mobile/reset/$',
       csrf_exempt(password_reset),
       {'template_name': 'userena/password_reset_form.html',
        'email_template_name': 'userena/emails/password_reset_message.txt',
        'extra_context': {'without_usernames': userena_settings.USERENA_WITHOUT_USERNAMES}
        },
       name='userena_password_mobile_reset'),
)

passowrd_reset_form.html

{% extends 'userena/base_userena.html' %}
{% load i18n %}

{% block title %}{% trans "Reset password" %}{% endblock %}

{% block content %}
<form action="" method="post">
  <fieldset>
    <legend>{% trans "Reset Password" %}</legend>
    {% csrf_token %}
    {{ form.as_p }}
  </fieldset>
  <input type="submit" value="{% trans "Send password" %}" />
</form>
{% endblock %}
kahlo
  • 2,314
  • 3
  • 28
  • 37
  • If you take a look at the request that is sent by that form, for example in your browser's development tools ('Network' tab...), is the CSRF token included in the data sent? Excluding the view from CSRF protection is not a good idea, as it allows attackers to reset the password of your users – sk1p Dec 19 '13 at 19:31
  • can you also add views.py just want to check the csrf_exempt decorator. – sawan gupta Dec 19 '13 at 19:31
  • @sawangupta csrf_excempt is applied in his url configuration, `csrf_exempt(password_reset)` – sk1p Dec 19 '13 at 19:35
  • @sk1p when I use the browser everything works fine because the CSRF token is included. It is when I try to do it with the mobile app (direct POST request) or using curl that it breaks because in those I do not include the CSRF token. My question is how I can disable django asking me for the CSRF token. I am aware of the security hazard by doing that. – kahlo Dec 19 '13 at 19:48

1 Answers1

7

If you do a GET request before POSTing to the password reset view, you get the CSRF token in a cookie, which you can then send in your POST request.

If you insist on exempting the view: I think the problem lies in the way the CSRF protection is applied to the password_reset view. It is explicitly decorated by csrf_protect.

To have a closer look at the problem, lets assume original_password_reset_view is password_reset without the csrf_protect. Basically, you are doing this:

csrf_exempt(csrf_protect(original_password_reset_view))
# ^^ your code
#           ^^ the decorator in django.contrib.auth.views

And adding in the effect of the CsrfViewMiddleware, we get the equivalent of

csrf_protect(csrf_exempt(csrf_protect(original_password_reset_view)))

csrf_protect is just a middleware-turned-decorator from CsrfViewMiddleware. csrf_exempt on the other hand simply sets csrf_exempt=True on its argument. So the middleware, represented by the outer csrf_protect, sees the csrf_exempt=True value on the view and disables its CSRF projection. It negates the outer csrf_protect. So we have:

csrf_protect(original_password_reset_view)

The view is still protected. Basically, there is no sane way around. (An insane way: write a middleware that sets request.csrf_processing_done = True for that specific URL. Don't do that...)

sk1p
  • 6,645
  • 30
  • 35