Adding csrf_token to my post request from iPhone app - python

I have an iPhone application from which I would like to call a post service passing parameters in its request, doing so caused a server error 500.
I have read Django documentation here and I still haven't figure out how to get a csrf_token and how to add it to the AFNetworking AFHTTPRequestOperationManager POST method.
On the server side I've added django.middleware.csrf.CsrfViewMiddleware in the MIDDLEWARE_CLASSES section, but it doesn't seem to do the trick.
My view looks like this; I am not doing much, just hoping to pass.
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def foo(request):
c={}
c.update(csrf(request))
if request.method == 'POST':
return HttpResponseRedirect("Post received");

The Django CSRF Middleware uses cookies and forms and whatnot to send a code to the page, then make sure the correct page is the one sending information back. In both cases, you must do a GET request to the server, and if you have the middleware installed correctly, it will put the CSRF token into a cookie for you.
Check out the documentation for more info on this.
Now, I noticed you're using a library that uses NSURLConnection, so that should handle cookies for you. I got this bundle of code (untested) that lets you pull the cookie name that you specify in your settings file (again, check out the documentation link above) then put that in your POST.
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL: networkServerAddress];
for (NSHTTPCookie *cookie in cookies)
{
// get the right cookie
}
Of course, if you're only making POSTs and never GETs first, you don't have a CSRF token to send!
And that's why we have the #csrf_exempt tag. (Docs here) This is the way to go 99% of the time, since most apps you won't do a GET before you do a POST. (in webpages you have to do a GET first). Note that this is intended only when an app is sending only POSTs and there's no session to speak of. You really need to think about your own security when using this, and how you verify that a given app/user really is who they claim to be. And how you disable people from hitting this URL from a webbrowser.
TLDR: Probably use #csrf_exempt on the view, but be careful.

Related

Django REST: OPTIONS request is registered as PUT

Whenever the browser sends an OPTIONS request, Django REST registers that as a PUT request.
I was writing my permission code when I noticed it. I use the default request parameter that is passed into the def has_object_permission( self, request, view, obj ):.
Using request.method will return the correct request method for every request except for OPTIONS.
However, when I use request.method in a my get_permission( self, request, view ): function in a different part of the project, correct response is returned. Could there be something wrong with the has_object_permission() function?
Currently I just check if the request is PUT, as I believe PUT requests are not used by Django anyway. But it would be nice if I could use the actual name instead.
My Django REST version is 3.9.0
OPTIONS requests are often used for what are called "pre-flight" requests in Cross-origin resource sharing (CORS). This is not anything wrong with has_object_permission(), it's just that these pre-flight requests are probably not intended to handled by your view. There is a more detailed answer in this SO post: https://stackoverflow.com/a/29954326/784648

To use Turbolinks 5 with Django, how can I automate inclusion of the Turbolinks-Location header when using redirect()?

According to the Turbolinks 5 documentation for "Following Redirects" (https://github.com/turbolinks/turbolinks#following-redirects):
When you visit location /one and the server redirects you to location
/two, you expect the browser’s address bar to display the redirected
URL.
However, Turbolinks makes requests using XMLHttpRequest, which
transparently follows redirects. There’s no way for Turbolinks to tell
whether a request resulted in a redirect without additional
cooperation from the server.
And the solution for this is to:
send the Turbolinks-Location header in response to a visit that was
redirected, and Turbolinks will replace the browser’s topmost history
entry with the value you provide.
The Turbolinks Rails engine performs this optimization automatically for non-GET XHR requests that redirect with the redirect_to helper.
I have a great interest in using Turbolinks on my Django (1.11) project and I'm wondering if anyone could point me in the right direction of how to create a new Django redirect() function or modify the existing one to always include the Turbolinks-Location header that is needed for redirects to function as expected. I definitely do not want to be manually setting this header every time I do a redirect.
There is a similar entry in the 'Redirecting After a Form Submission' section (https://github.com/turbolinks/turbolinks#redirecting-after-a-form-submission) I would also appreciate any help in understanding how to implement:
If form submission results in a state change on the server that
affects cached pages, consider clearing Turbolinks’ cache with
Turbolinks.clearCache().
The Turbolinks Rails engine performs this optimization automatically
for non-GET XHR requests that redirect with the redirect_to helper.
I did see there is a "Drop-in turbolinks implementation for Django" package on github but this is forked from turbolinks-classic and sourcecode has no mentions of the Turbolinks-Location header so I am sure this is not what I'm looking for.
I did end up discovering how to do exactly what I was attempting by being pointed to a blob of code in this project https://github.com/viewflow/django-material/blob/v2/material/middleware.py by a reddit user.
I copied the TurbolinksMiddleware class into my own middleware.py under my apps directory and listed it in my settings.py as such
# MIDDLEWARE CONFIGURATION
# --------------------------------------------------------------------------
MIDDLEWARE = [
.
.
.
'apps.middleware.TurbolinksMiddleware',
]
With the regular installation step of including the turbolinks.js in my html template, everything appeared to be working correctly.
Here is the TurbolinksMiddleware class in case it should not be available at the link above:
class TurbolinksMiddleware(object):
"""
Send the `Turbolinks-Location` header in response to a visit that was redirected,
and Turbolinks will replace the browser’s topmost history entry .
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
is_turbolinks = request.META.get('HTTP_TURBOLINKS_REFERRER')
is_response_redirect = response.has_header('Location')
if is_turbolinks:
if is_response_redirect:
location = response['Location']
prev_location = request.session.pop('_turbolinks_redirect_to', None)
if prev_location is not None:
# relative subsequent redirect
if location.startswith('.'):
location = prev_location.split('?')[0] + location
request.session['_turbolinks_redirect_to'] = location
else:
if request.session.get('_turbolinks_redirect_to'):
location = request.session.pop('_turbolinks_redirect_to')
response['Turbolinks-Location'] = location
return response
The django package you mentioned implements a middleware that is responsible for adding the headers. It seems that for old turbolinks the headers had different names. You might write your own middleware to support turbolinks 5.
Instead of submitting forms normally, submit them with XHR.
You can achieve this with plain django. Read the django CSRF doc - they explain in detail what is necessary.

Difficulty having Django find database user

I'm writing a web app which has a page for admin tasks. One of the tasks is that the admin users must be able to edit other users details. Alas, I've fallen at quite a simple roadblock.
I've set up a very simple jQuery AJAX Get request, successfully transferring a string to the server and back. This is just background, but not the issue. The issue lies in retrieving other user's objects.
At the moment, with a username I know exists, this code which is accessed in views.py, produces a 500 Internal Server Error.
#login_required
def user_edit_getuser(request):
# Like before, get the request's context.
context = RequestContext(request)
inputname = request.GET['inputNameSend']
user_obj = User.objects.get(inputname)
return HttpResponse(inputname) #later will return a JSON String
get takes keyword arguments only: the key is the field to look up.
user_obj = User.objects.get(username=inputname)
Also, you should probably deal with the possibility that the GET request has no inputNameSend key.
For JS development, you can usually see the error page in the Chrome dev tools/Firebug console in the Network tab.

Does SessionAuthentication work in Tastypie for HTTP POST?

I am able to do GET to work with SessionAuthentication and Tastypie without setting any headers except for content-type to application/json. HTTP POST however just fails even though the Cookie in the Header has the session id. It fails with a 401 AuthorizationHeader but it has nothing to do with Authorization. Changing SessionAuthentication to BasicAuthentication and passing username/password works too.
Has anyone ever got SessionAuthentication to work with POST with Tastypie?
Yes I have gotten it to work. All you need to do is to pass the csfr token:
SessionAuthentication
This authentication scheme uses the built-in
Django sessions to check if a user is logged. This is typically useful
when used by Javascript on the same site as the API is hosted on.
It requires that the user has logged in & has an active session. They
also must have a valid CSRF token.
This is how you do that in jQuery:
// sending a csrftoken with every ajax request
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
}
});
$.ajax({
type: "POST",
// ...
Notice the part that says $.cookie('csrftoken'). It gets the csrf token from a cookie that Django sets.
Update:
I had some problems with Django not setting the cookie on Firefox and Opera. Putting the template tag {% csrf_token %} in your template solves this. The right solution would probably be to use the decorator ensure_csrf_cookie().
Here are some additions to Dan's answer. Please, correct me if something is wrong, I am still a bit confused about it myself.
Before we continue, read about CSRF protection in Django. Read it carefully. You need to put the token from the cookie into the header X-CSRFToken. This will not work if the cookie is Httponly, that is, if you have set CSRF-COOKIE-HTTPONLY = True in settings.py. Then you have to embed the cookie in the document which, of course, creates further vulnerabilities and reduces protection the gained by using Httponly.
As far as I can tell, if the cookie is not Httponly, jQuery sets X-CSRFToken automatically. Correct me if I am wrong, but I've just spent several hours playing with it, and this is what I am consistently getting. This makes me wonder, what is the point of the advice in Django documentation? Is it a new feature in jQuery?
Further discussion:
Tastypie disables CSRF protection except with Session Authentication, where it has custom code in authentication.py. You have to pass both the cookie csrftoken cookie and the header X-CSRFToken for the authentication to work. (This is Tastypie's requirement.) Assuming same domain, the browser will pass the cookies. JQuery will pass the header for you unless the csrftoken cookie is Httponly. Conversely, if the cookie is Httponly, I was unable to even manually set the header in $.ajaxSetup{beforeSend.... It appears that jQuery automatically sets X-CSRFToken to null if the csrftoken cookie is Httponly. At least I was able to set the header X-CS_RFToken to what I wanted, so I know I passed the value correctly. I am using jQuery 1.10.
If you are using curl for testing, you have to pass two cookies (sessionid and csrftoken), set the headers X-CSRFToken and, if the protocol is HTTPS, also set the Referrer.
I found this in the tastypie source code. Basically implies that HTTP POST is not supported by SessionAuthentication.
class SessionAuthentication(Authentication):
"""
An authentication mechanism that piggy-backs on Django sessions.
This is useful when the API is talking to Javascript on the same site.
Relies on the user being logged in through the standard Django login
setup.
Requires a valid CSRF token.
"""
def is_authenticated(self, request, **kwargs):
"""
Checks to make sure the user is logged in & has a Django session.
"""
# Cargo-culted from Django 1.3/1.4's ``django/middleware/csrf.py``.
# We can't just use what's there, since the return values will be
# wrong.
# We also can't risk accessing ``request.POST``, which will break with
# the serialized bodies.
if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
return request.user.is_authenticated()
So answering my own question here but if someone can explain it better and suggest a good way to do this, that would be great too.
Edit
I am currently using a workaround from https://github.com/amezcua/TastyPie-DjangoCookie-Auth/blob/master/DjangoCookieAuth.py which basically is a custom authentication scheme that fetches the session_id from the cookie and checks with the backend if it is authenticated. Might not be the most full proof solution but works great.

Clear POST in web page

I have a small python/django web site and I'm using a html form POST some information, annoyingly however this information is stored in POST so when a user refreshes in say IE/chrome they get that warning message about the page containing POST data. How do I clear the POST data after it has been processed so a user can refresh and not see this warning message?
Also I have some logic as follows that detects a POST
if request.method == "POST":
do something
Select all
Open in new window
This is fine when I actually post the form, but when I refresh the page it also detects the POST and does the logic that I now dont want to do.
How can I solve this also??
Thanks
After form is validated and it is valid. Then do the redirect to some other page e.g. a success page or redirect to the same view. The redirection will avoid Double Form Submition problem. Read more about it here.
Use HttpResponseRedirect when you return the response for POST request. This is explained in tutorial 4 as
After incrementing the choice count, the code returns an HttpResponseRedirect rather than a normal HttpResponse. HttpResponseRedirect takes a single argument: the URL to which the user will be redirected (see the following point for how we construct the URL in this case).
As the Python comment above points out, you should always return an HttpResponseRedirect after successfully dealing with POST data. This tip isn't specific to Django; it's just good Web development practice.
As Rohan said, you should use HttpResponseRedirect. But also you can use a shortcut:
from django.shortcuts import redirect
def some_view(request):
if request.method == 'POST':
# do smth
return redirect('/page-with-form/')

Categories