Android sending post requests to django server csrf failing

11,172

Solution 1

Write own decorator and add some "secret" header to your request. https://code.djangoproject.com/browser/django/trunk/django/views/decorators/csrf.py

def csrf_exempt(view_func):
        """
        Marks a view function as being exempt from the CSRF view protection.
        """
        # We could just do view_func.csrf_exempt = True, but decorators
        # are nicer if they don't have side-effects, so we return a new
        # function.
        def wrapped_view(request,*args, **kwargs):
            return view_func(request, *args, **kwargs)
            if request.META.has_key('HTTP_X_SKIP_CSRF'):
                wrapped_view.csrf_exempt = True
        return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)

Solution 2

First you need to read the csrf token from the cookies from a preview request:

httpClient.execute(new HttpGet(uri));
CookieStore cookieStore = httpClient.getCookieStore();
List <Cookie> cookies =  cookieStore.getCookies();
for (Cookie cookie: cookies) {
    if (cookie.getDomain().equals(Constants.CSRF_COOKIE_DOMAIN) && cookie.getName().equals("csrftoken")) {
        CSRFTOKEN = cookie.getValue();
    }
}

If your view is not rendering a template containing the csrf_token template tag, Django might not set the CSRF token cookie. This is common in cases where forms are dynamically added to the page. To address this case, Django provides a view decorator which forces setting of the cookie: ensure_csrf_cookie(). -- https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax

Then you can pass it to the server on the post request headers and cookies when doing the post request:

httpPost.setHeader("Referer", Constants.SITE_URL);
httpPost.setHeader("X-CSRFToken", CSRFTOKEN);

DefaultHttpClient client = new DefaultHttpClient();
final BasicCookieStore cookieStore =  new BasicCookieStore();

BasicClientCookie csrf_cookie = new BasicClientCookie("csrftoken", CSRFTOKEN);
csrf_cookie.setDomain(Constants.CSRF_COOKIE_DOMAIN);
cookieStore.addCookie(csrf_cookie);

// Create local HTTP context - to store cookies
HttpContext localContext = new BasicHttpContext();
// Bind custom cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);

HttpResponse response = client.execute(httpPost, localContext);

Solution 3

Switching off CSRF verification certainly works! but are you sure you want to do that? your original line of thinking; getting the token from the server and sending that along with the POST data is far better.

The csrf token is usually present in the form of a cookie. For example with the Django framework you have a cookie named csrftoken, you need to grab that value and post it to the server as 'X-CSRFToken'

Share:
11,172
Jayyyyy
Author by

Jayyyyy

Updated on July 19, 2022

Comments

  • Jayyyyy
    Jayyyyy almost 2 years

    I would like my android app to be able to send some information to my django server. So I made the android app send a post request to the mysite/upload page and django's view for this page would do work based on the post data. The problem is the response the server gives for the post request complains about csrf verication failed. Looking in to the problem it seems I might have to get a csrf token from the server first then do the post with that token But I am unsure how I do this. Edit: I have found out that I can knock off the crsf verification for this view using a view decorator @csrf_exempt but I am not sure if this is the best solution. My android code:

    // Create a new HttpClient and Post Header
                        HttpClient httpclient = new DefaultHttpClient();
                        HttpPost httppost = new HttpPost(URL);
    
                        // Add your data
                        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
                        nameValuePairs.add(new BasicNameValuePair("scoreone", scoreone));
                        nameValuePairs.add(new BasicNameValuePair("scoretwo", scoretwo));
                        httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                        System.out.println("huzahhhhhhh");
                        // Execute HTTP Post Request
                        HttpResponse response = httpclient.execute(httppost);
                        BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                        StringBuffer sb = new StringBuffer("");
                        String line = "";
                        String NL = System.getProperty("line.separator");
                        while ((line = in.readLine()) != null) {
                            sb.append(line + NL);
                        }
                        in.close();
                        String result = sb.toString();
                        System.out.println("Result: "+result);
    

    and my views code for handling the upload:

    # uploads a players match
    def upload(request):
        if request.method == 'POST':
            scoreone = int(request.POST['scoreone'])
            scoretwo = int(request.POST['scoretwo'])
            m = Match.objects.create()
            MatchParticipant.objects.create(player = Player.objects.get(pk=1), match = m, score = scoreone)
            MatchParticipant.objects.create(player = Player.objects.get(pk=2), match = m, score = scoretwo)
        return HttpResponse("Match uploaded" )
    
    enter code here
    
  • Harshit Gupta
    Harshit Gupta almost 10 years
    I'm doing the exactly same thing, but I'm getting 403 error. I have tried sending cookies, headers.but still I'm getting the same error.
  • remus
    remus over 8 years
    You're bypassing security here.
  • baklarz2048
    baklarz2048 about 8 years
    @remus CSRF for Android app doesn't make sense. Cross site request forgery is for classical web pages. Its nonsense for API.