0
votes

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.

Relevant code:

Form handler:

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)

1
What is the handler() function? A Django view, a middleware function, or something else?Aya
Sorry, handler() is a Django view. It's on a URL that's posted to by a form. process() is also a view, similarly a postable URL (my worker perform() method posts there). the perform() method is a method on a model class, used by Pyres.Callum M

1 Answers

1
votes

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.

Update

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.