How does one protect his AJAX views with Django?

拈花ヽ惹草 提交于 2019-12-10 15:28:56

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!