0

I've written a small standalone python script that's calling my django-based backend and everything is working fine with login and calling views requiring auth and so on.

A bit of code

def dostuff():
    session = login(username, password)
    license = add_license(session)

def _helper(self, url, cookie=None):
    http = httplib2.Http()
    if cookie:
        headers = { "Cookie" : cookie }
    else:
        headers = {}
    response, content = http.request(host + url, "GET", headers=headers, body="")
    return response, content

def login(self, username, password):
    url = "/license/login?username=%s&password=%s" % (username, password)
    response, content = self._helper(url)
    sessioncookie = response["set-cookie"]
    customer_id = re.search("id=(?P<id>\d+)", content)
    if response["status"] == "200":
        return sessioncookie, customer_id.group("id")

def add_license(self, session):
    cookie = session[0]
    customer_id = int(session[1])-1
    url = "/license/add_license?customer_id=%s" % customer_id
    response, content = self._helper(url, cookie)
    content = content[1:-1]
    if response["status"] == "200": #ok
        data = json.loads(content)
        return data["fields"]

If I cahnge "GET" to "POST" I encounter the Django CSRF-error page(CSRF verification failed) in return. How can I send POST data to Django?

My login view in Django, do I need to do anything special to add the csrf token? My plan is to rewrite this to send json once things are working.

 def my_login(request):
    done, username = get_input(request, "username")
    if not done:
        return username
    done, password = get_input(request, "password")
    if not done:
        return password
    user = authenticate(username=username, password=password)
    if user is not None:
       if user.is_active:
            login(request, user)
            return HttpResponse("Done, id=%s" % user.pk)
        else:
            return HttpResponse("User disabled")
    else:
        return HttpResponse("Invalid login")
dutt
  • 7,909
  • 11
  • 52
  • 85

3 Answers3

1

I got it working and this is how I did it. Like suggested by toto_tico I worte a dummy view that I retrieve thought GET to get the CSRF token. At first it didn't send the csrf token over GET so I had to add the decorator ensure_csrf_cookie.

@ensure_csrf_cookie
def dummy(request):
    return HttpResponse("done")

And then I handle login requests normally.

def my_login(request):
    ...handle login...

It turned out that just adding the cookie to the POST wasn't enough, I had to write a token to the POST data as well.

def _helper(self, url, method="POST"):
    req = urllib2.Request(host + url)
    self.cookieMgr.add_cookie_header(req)
    try:
        if method == "GET":
            response = self.opener.open(req)
        else:
            for cookie in self.cookieMgr:
                if cookie.name == "csrftoken":
                    csrf = cookie.value
            values = { "csrfmiddlewaretoken" : csrf}
            params = urllib.urlencode(values)
            response = self.opener.open(req, params)
            code = response.getcode()
            info = response.info()
            content = response.read()
            return code, info, content
    except urllib2.HTTPError as ex:
        print str(ex)
        sys.exit(1)

def get_csrf(self):
    url = "/license/dummy"
    self._helper(url, method="GET")

def login(self, username, password):
    self.get_csrf()
    url = "/license/login?username=%s&password=%s" % (username, password)
    code, info, content = self._helper(url)
    if code == 200:
        #done!
dutt
  • 7,909
  • 11
  • 52
  • 85
0

You have to add the csrftoken cookie value when you make a request to Django. Alternatively you can add @csrf_exempt to your Django backend to accept those requests.

Alagappan Ramu
  • 2,270
  • 7
  • 27
  • 37
  • cookie = { "csrftoken" : ...somthing...? } in my _helper function? Since I get this error when calling login I haven't received any cookies yet. – dutt May 10 '13 at 10:39
  • Check if your csrftoken is secure or not and whether you are making your request to the same protocol as that. https if it is secure and http is it is not. – Alagappan Ramu May 10 '13 at 10:47
  • Added my login view, I'm not sure how to check if the token is secure. – dutt May 10 '13 at 10:58
  • @dutt: cookies doesn't depend on being logged or not. Any http request is enough for receiving a cookie in the browser. Don't use `@csfr_exempt` unless you are sure you don't need it. Read [here](http://stackoverflow.com/questions/6506897/csrf-token-missing-or-incorrect-while-post-parameter-via-ajax-in-django) about CSFR and Django. – toto_tico May 10 '13 at 12:28
  • What's confusing me on this point is that I get csrf denial on my login attempt. At that point I haven't had any contact with the server and I don't know what to send as the csrftoken cookie. – dutt May 10 '13 at 12:53
  • Oh. I understand the problem now. I guess you always have to do a dummy GET petition first and extract the CSFR token from there. Since it is the first time the server doesn't know who you are because you are a new client (not because you are not login). Of course, now the question is how to extract the CSFR from a http request in javascript. – toto_tico May 10 '13 at 22:57
  • The first line of the [django CSFR code](https://docs.djangoproject.com/en/1.3/ref/contrib/csrf/#ajax) bind an event to $(document). So the idea would be call the same code but with the return document of the first GET petition `$(the_document_return_by_GET).ajaxSend(function(event, xhr, settings) {...` – toto_tico May 10 '13 at 23:00
  • I was trying to find who to extract the CSRF code from the `XMLHTTPRequest`. I found (this)[http://stackoverflow.com/questions/220231/accessing-http-headers-in-javascript]. I am able to access the headers but I cannot find the csfr token anywhere. – toto_tico May 10 '13 at 23:55
0

Start reading about CSFR and ajax. I usually do the following with the code provided:

  1. Create a csfr.js file
  2. Paste the code in the csfr.js file
  3. Reference the code in the template that needs it|

If you are using templates and have something like base.html where you extend from, then you can just reference the script from there and you don't have to worry any more in there rest of your programming. As far as I know, this shouldn't represent any security issue.

toto_tico
  • 17,977
  • 9
  • 97
  • 116
  • I'm not using templates, I've used them before and got csrf to work but now I'm curious if it's possible without templates. All solutions I've seen have relied on templates, I'm just using HttpResponse with raw text. But so far it seems the easiest solution is to use a template after all. – dutt May 10 '13 at 12:51
  • I see but in that case just make sure that the code provided is executed. The 3 last lines of code calls the method `xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));`. If you paste the code at the beginning of your current javascript file then it should work. – toto_tico May 10 '13 at 22:45