Django POST request to my view from Pyres worker - CSRF token
I'm using Pyres workers to do some processing of data users enter in a form. Their processing is done by a view on my form, which I make a POST request to, with data including the data to process and a CSRF middleware token for the user. My issue is that this is apparently not enough, as Django still rejects my request with a 403 forbidden.
def handler(request): if(request.method == "POST"): if(request.POST.__contains__("taskdata")): #valid post of the form taskdata = escape(request.POST.get("taskdata","")) t = TaskData(data=taskdata, time_added=timezone.now(), token=request.POST.get("csrfmiddlewaretoken","")) t.save() r = ResQ(server="127.0.0.1:6379") r.enqueue(TaskData, t.id) return HttpResponse(t.id) else: #invalid post of the form raise Http404 else: raise Http404
Pyres worker job:
@staticmethod def perform(taskData_id): #Get the taskData from this id, test it for tasky stuff task_data = TaskData.objects.get(pk=taskData_id) post_data = [('id',task_data.id),('data',task_data.data), ('csrfmiddlewaretoken',task_data.token)] # a sequence of two element tuples result = urllib2.urlopen('http://127.0.0.1:8000/tasks/nlp/process/', urllib.urlencode(post_data)) content = result.read() return
View being posted to by that job:
def process(request): if(request.method == "POST"): return HttpResponse("HEY, it works!") if(request.POST.__contains__("data") and request.POST.__contains__("id")): #valid post to the form by the model #taskdata = escape(request.POST.get("taskdata","")) #data = get_times(taskdata) return HttpResponse("Hey from process!") #return HttpResponse(json.dumps(data)) else: #invalid post of the form raise Http404 else: raise Http404
What I'm basically trying to do is save some raw data at form submission, along with the CSRF token for it. The workers then send that data + token to a processing view.
Unfortunately, posting the token doesn't seem to be enough.
Does anybody know what the csrf protection actually looks for, and how I can make my Pyres workers compliant?
(Suggested tag: pyres)
I think I see the problem.
The way Django's CSRF protection works is by generating a nonce, then setting a cookie to the value of the nonce, and ensuring the csrfmiddlewaretoken POST value matches the value of the cookie. The rationale is that it makes it a stateless system, which works without any persistent session data.
The problem is that the request you make in the Pyres worker job...
result = urllib2.urlopen('http://127.0.0.1:8000/tasks/nlp/process/', urllib.urlencode(post_data))
...is coming from the server, not the client, so it won't have the cookie set.
Assuming the /tasks/nlp/process/ URL is protected such that it can only be accessed by the server, then it's probably simplest to make the process() view exempt from CSRF checking with...
@csrf_exempt def process(request): ...
...otherwise you'll have to manually grab the cookie value in the handler() view, and pass it on to the Pyres worker job.
To ensure the process() method can only be called by the server, one simple way would be to check the request object with something like...
@csrf_exempt def process(request): if request.META['REMOTE_ADDR'] != '127.0.0.1': # Return some error response here. # 403 is traditional for access denied, but I prefer sending 404 # so 'hackers' can't infer the existence of any 'hidden' URLs # from the response code raise Http404 # Now do the thing ....
...although there may be some built-in decorator or somesuch to do this for you.