问题
I'm using a private view in my Django project for an AJAX request.
def HereIsSomeJSON(request, label):
if not request.method == "POST":
raise PermissionDenied
# Here is the job of my AJAX, basically feeding a JSON
json = {...}
return HttpResponse(json, "application/json")
Using JavaScript, I request for the AJAX with jQuery as so:
function FeedMeWithJSON() {
// Django needs his Cross-Site Request Forgery token to welome POST datas
oPostDatas = {
'csrfmiddlewaretoken': '{{ csrf_token }}'
};
jQuery.post("/url/to/HereIsSomeJSON", oPostDatas, function(oData, sStatus) {
// Here is the job to be done with the fetched JSON
}, "json");
}
Everything works just fine thanks to the request.method
verification I do in the view. The user isn't able to manually (by entering my AJAX url in his browser) access to my view.
However, as I will need more AJAX views I was wondering if I was doing the right thing. So I thought of creating a custom Django decorator which I could use above every one of my AJAX views.
Is is the good way of protecting my private views ? And if so, how do I do it ?
Thanks,
Edit
Apparently, this was not clear enough. I am using a Django view to do an AJAX request. But I don't want the user to be able to type in the URL to read database contents. I know one could always use curl
or something similar to send POST datas and thus bypassing my thing, even though he would have to send the right {% csrf_token %}
.
Plus, in a near future the login feature will be implemented and I will add the @login_required
decorator.
Thanks,
回答1:
Your approach of requiring POST for your ajax views is basically OK, and there is an existing decorator to handle it:
from django.views.decorators.http import require_POST
@require_POST
def my_view(request):
# I can assume now that only POST requests make it this far
# ...
pass
Also, there is an easier way to handle adding CSRF tokens to your jQuery AJAX calls, documented here. The basic idea is that you read the CSRF token from the cookie, and use the beforeSend
option in $.ajaxSetup
to add it to your all of your $.ajax
calls (including shortcut syntax like $.post
).
Since this code isn't relying on template variables, it doesn't have to be in an inline <script>
tag.
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
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", csrftoken);
}
}
});
回答2:
As @armonge said, just checking for POST method doesn't do any protection. Suppose you need some checks, that user is registered and maybe have some permissions or needed properties. For this purpose, such decorator can be used (probably needs to be customized):
def apply_permissions(view_func):
def _wrapped_view(request, *args, **kwargs):
# it is possible to add some other checks, that return booleans
# or do it in a separate `if` statement
# for example, check for some user permissions or properties
permissions = [
request.is_ajax(),
request.method == "POST",
request.user.is_authenticated()
]
if not all(permissions):
raise PermissionDenied
return view_func(request, *args, **kwargs)
return _wrapped_view
@apply_permissions
def HereIsSomeJSON(request, label):
# your code here without any permissions checks
# Here is the job of my AJAX, basically feeding a JSON
回答3:
I figured out that requiring POST is not enough, one could possibly cheat using curl
for example.
That is why a custom decorator is - in my opinion - the way to go.
from django.http import HttpResponseBadRequest
def require_AJAX(function):
"""Return a bad request instance if the view is not using AJAX
function -- the view
"""
def wrap(request, *args, **kwargs):
if not request.is_ajax():
return HttpResponseBadRequest()
return function(request, *args, **kwargs)
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap
This decorator does actually less than @stalk's but this what I needed.
来源:https://stackoverflow.com/questions/16569784/how-does-one-protect-his-ajax-views-with-django