22

I have read Django - CSRF verification failed and several questions (and answers) related to django and POST method. One of the best-but-not-working-for-me answer is https://stackoverflow.com/a/4707639/755319

All of the approved answers suggest at least 3 things:

  1. Use RequestContext as the third parameter of render_to_response_call
  2. Add {% csrf_token %} in every form with POST method
  3. Check the MIDDLEWARE_CLASSES in settings.py

I've done exactly as suggested, but the error still appeared. I use django 1.3.1 (from ubuntu 12.04 repository) and python 2.7 (default from ubuntu)

This is my View:

# Create your views here.
from django.template import RequestContext
from django.http import HttpResponse
from django.shortcuts import render_to_response
from models import BookModel

def index(request):
    return HttpResponse('Welcome to the library')

def search_form(request):
    return render_to_response('library/search_form.html')

def search(request):
    if request.method=='POST':
        if 'q' in request.POST:
            q=request.POST['q']
            bookModel = BookModel.objects.filter(title__icontains=q)
            result = {'books' : bookModel,}
            return render_to_response('library/search.html', result, context_instance=RequestContext(request))
        else:
            return search_form(request)
    else:
        return search_form(request)

and this is my template (search_form.html):

{% extends "base.html" %}
{% block content %}
<form action="/library/search/" method="post">
    {% csrf_token %} 
    <input type="text" name="q">
    <input type="submit" value="Search">
</form>
{% endblock %}

I've restarted the server, but the 403 forbidden error is still there, telling that CSRF verification failed.

I've 2 questions:

  1. How to fix this error?
  2. Why is it so hard to make a "POST" in django, I mean is there any specific reason to make it so verbose (I come from PHP, and never found such a problem before)?
Community
  • 1
  • 1
goFrendiAsgard
  • 4,016
  • 8
  • 38
  • 64

8 Answers8

20

I maybe wrong however I found the above solutions rather complex.

what worked for me was simply including my csrf token into my post request.

$.ajax({
    type: "POST",
    url: "/reports/",
    data: { csrfmiddlewaretoken: "{{ csrf_token }}",   // < here 
            state:"inactive" 
          },
    success: function() {
        alert("pocohuntus")
        console.log("prototype")
    }
})
laycat
  • 5,381
  • 7
  • 31
  • 46
13

This answer is for people that may encounter this same problem in the future.

The CSRF {{csrf_token}} template tag that is required for forms in Django prevent against Cross Site Request Forgeries. CSRF makes it possible for a malicious site that has been visited by a client's browser to make requests to your own server. Hence the csrf_token provided by django makes it simple for your django server and site to be protected against this type of malicious attack. If your form is not protected by csrf_token, django returns a 403 forbidden page. This is a form of protection for your website especially when the token wasn't left out intentionally.

But there are scenarios where a django site would not want to protect its forms using the csrf_token. For instance, I developed a USSD application and a view function is required to receive a POST request from the USSD API. We should note that the POST request was not from a form on the client hence the risk of CSRF impossible, since a malicious site cannot submit requests. The POST request is received when a user dials a USSD code and not when a form is submitted.

In other words, there are situations where a function will need to get a POST request and there would not be the need of {{csrf_token}}.

Django provides us with a decorator @csrf_exempt. This decorator marks a view as being exempt from the protection ensured by the middleware.

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')

Django also provides another decorator that performs the same function with {{csrf_token}}, but it doesn't reject incoming request. This decorator is @requires_csrf_token. For instance:

@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

The last decorator that will be mentioned in this post does exactly the same thing as {{csrf_token}} and it is called @csrf_protect. However, the use of this decorator by itself is not best practice because you might forget to add it to your views. For instance:

@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

Below are some links that will guide and explain better.

https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#module-django.views.decorators.csrf

https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/

http://www.squarefree.com/securitytips/web-developers.html#CSRF

Iyanuoluwa Ajao
  • 655
  • 1
  • 7
  • 13
5

The easiest way to avoid such problems is to use the render shortcut.

from django.shortcuts import render
# .. your other imports

def search_form(request):
    return render(request, 'library/search_form.html')

def search(request):
    q = request.GET.get('q')
    results = BookModel.objects.all()
    if q:
        results = results.filter(title__icontains=q)
    return render(request, 'library/search.html', {'result': results})
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • Must try this soon, seems very clear, thank you for your answer. Is there any cons of using this method? I wonder why is the documentation and djangobook provide such a verbose syntax if there is such a clear syntax – goFrendiAsgard May 19 '12 at 11:24
  • I know this is an old thread, but I updated it to further clarify the search method. Hopefully this is now (more) useful. – Burhan Khalid May 13 '13 at 19:40
  • Also, search methods are probably the only ones that should use `GET` and not `POST` since they don't change anything. – Burhan Khalid May 14 '13 at 02:36
3

Try putting RequestContext in the search_form view's render_to_response:

context_instance=RequestContext(request)
zubinmehta
  • 4,368
  • 7
  • 33
  • 51
  • That's works, thank you for your answer. But how and why? Can you please give explanation? – goFrendiAsgard May 19 '12 at 08:17
  • 2
    https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-to-use-it - read the point #3 – zubinmehta May 19 '12 at 08:50
  • 3
    Because `csrf_token` must be created in your view so django can pass it to the template. In your situation, since your search view do not create a token, `{% csrf_token %}` in your template is `empty string (None)` and result page fails on verification – Mp0int May 19 '12 at 08:51
2

The response is 403 bcoz, django requires a csrf token (included in the post data) in every POST request you make.

There are various ways to do this such as:

Acquiring the token from cookie and the method has been explained in article enter link description here

or

You can access it from DOM using {{ csrf_token }}, available in the template

So now using the second method:

var post_data = {
  ...
  'csrfmiddlewaretoken':"{{ csrf_token }}"
  ...
}
$.ajax({
  url:'url',
  type:'POST'
  data:post_data,
  success:function(data){
    console.log(data);
  },
  error:function(error){
    console.log(error);
  }
});
Hiro
  • 2,992
  • 1
  • 15
  • 9
1

You need to use RequestContext with your response

for example in view.py file

from django.template import RequestContext

def home(request):
    return render_to_response('home.html',RequestContext(request, {}))
Achrome
  • 7,773
  • 14
  • 36
  • 45
0

You also can use

direct_to_template(request, 'library/search.html', result) 

instead of

render_to_response('library/search.html', result, context_instance=RequestContext(request))

because direct_to_template adds RequestContext automatically. But note that direct_to_template is going to be deprecated and django offers to use CBV TemplateView instead.

RequestContext allows you to use context processors. And this is your mistake: {% csrf_token %} outputed empty string and you got 403.

San4ez
  • 8,091
  • 4
  • 41
  • 62
0

This solved it for me. As the Django documentation says, there is some processing that needs to be done before being able to directly make POST method to your RESTAPI. Here is the code :

function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

const csrftoken = getCookie('csrftoken');
console.log(csrftoken)





$.ajax({
    method:'POST',
    url:"//127.0.0.1:8000/app/homepage/", /*change it to your api endpoint*/
    headers: {'X-CSRFToken': csrftoken},
    data : { "data" : "3"}
    
   
})

One thing I cannot assure with this solution, is the security aspect.

El Bachir
  • 111
  • 1
  • 4