programing

Ajax POST 요청으로 Django CSRF 검사 실패

new-time 2020. 5. 21. 22:10
반응형

Ajax POST 요청으로 Django CSRF 검사 실패


내 AJAX 게시물을 통해 Django의 CSRF 보호 메커니즘을 준수하는 데 도움이 될 수 있습니다. 나는 여기의 지시를 따랐다.

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/

해당 페이지에있는 AJAX 샘플 코드를 정확하게 복사했습니다.

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax

통화

getCookie('csrftoken')

전에 내용을 인쇄하는 경고 를 표시했으며

xhr.setRequestHeader

실제로 일부 데이터로 채워져 있습니다. 토큰이 올바른지 확인하는 방법을 잘 모르겠지만 무언가를 찾아서 보내는 것이 좋습니다.그러나 장고는 여전히 내 AJAX 게시물을 거부하고 있습니다.내 JavaScript는 다음과 같습니다.

$.post("/memorize/", data, function (result) {
    if (result != "failure") {
        get_random_card();
    }
    else {
        alert("Failed to save card data.");
    }
});

장고에서 내가 본 오류는 다음과 같습니다.

[23 / Feb / 2011 22:08:29] "POST / 암기 /HTTP/1.1"403 2332

나는 무언가를 놓치고 있다고 확신하며, 아마도 간단하지만 그것이 무엇인지 모르겠습니다. 나는 SO를 둘러보고

csrf_exempt

데코레이터 를 통해 내보기에 대한 CSRF 검사를 끄는 것에 대한 정보를 보았지만 그다지 매력적이지 않다는 것을 알았습니다. 나는 그것을 시도해 보았지만 효과가 있지만 가능한 경우 장고가 그것을 예상하도록 설계된 방식으로 POST를 수행하고 싶습니다.도움이되는 경우를 대비하여 내 견해가 무엇을하고 있는지에 대한 요점은 다음과 같습니다.

def myview(request):

    profile = request.user.profile

    if request.method == 'POST':
        """
        Process the post...
        """
        return HttpResponseRedirect('/memorize/')
    else: # request.method == 'GET'

        ajax = request.GET.has_key('ajax')

        """
        Some irrelevent code...
        """

        if ajax:
            response = HttpResponse()
            profile.get_stack_json(response)
            return response
        else:
            """
            Get data to send along with the content of the page.
            """

        return render_to_response('memorize/memorize.html',
                """ My data """
                context_instance=RequestContext(request))

답장을 보내 주셔서 감사합니다!


실제 솔루션

좋아, 나는 문제를 추적했다. Javascript (아래에서 제안한대로) 코드에 있습니다.필요한 것은 이것입니다.

$.ajaxSetup({ 
     beforeSend: function(xhr, settings) {
         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;
         }
         if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
             // Only send the token to relative URLs i.e. locally.
             xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
         }
     } 
});

공식 문서에 게시 된 코드 대신

https://docs.djangoproject.com/en/2.2/ref/csrf/

작업 코드는 다음 Django 항목에서 제공됩니다.

http://www.djangoproject.com/weblog/2011/feb/08/security/

따라서 일반적인 해결책은 "ajaxSend 핸들러 대신 ajaxSetup 핸들러 사용"입니다. 왜 작동하는지 모르겠습니다. 그러나 그것은 나를 위해 작동합니다 :)

이전 게시물 (답변 없음)

실제로 동일한 문제가 발생합니다.Django 1.2.5로 업데이트 한 후에 발생합니다-Django 1.2.4의 AJAX POST 요청에 오류가 없었습니다 (AJAX는 어떤 방식으로도 보호되지 않았지만 정상적으로 작동했습니다).OP와 마찬가지로 Django 설명서에 게시 된 JavaScript 스 니펫을 사용해 보았습니다. jQuery 1.5를 사용하고 있습니다. 또한 "django.middleware.csrf.CsrfViewMiddleware"미들웨어를 사용하고 있습니다.미들웨어 코드를 따르려고했는데 이것이 실패한다는 것을 알고 있습니다.

request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')

그리고

if request_csrf_token != csrf_token:
    return self._reject(request, REASON_BAD_TOKEN)

"request_csrf_token"이 비어 있기 때문에이 "if"는 참입니다.기본적으로 헤더가 설정되지 않았 음을 의미합니다. 이 JS 라인에 문제가 있습니까?

xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));

?제공된 세부 정보가 문제를 해결하는 데 도움이되기를 바랍니다. :)


$.ajax

함수 를 사용

csrf

하면 데이터 본문에 토큰을 추가하기 만하면됩니다 .

$.ajax({
    data: {
        somedata: 'somedata',
        moredata: 'moredata',
        csrfmiddlewaretoken: '{{ csrf_token }}'
    },

이 줄을 jQuery 코드에 추가하십시오.

$.ajaxSetup({
  data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

그리고 완료.


문제는 django가 쿠키의 값이 양식 데이터의 일부로 다시 전달되기를 기대하기 때문입니다. 이전 답변의 코드는 javascript가 쿠키 값을 찾아서 양식 데이터에 넣도록합니다. 그것은 기술적 인 관점에서 그것을하는 멋진 방법이지만 조금 장황하게 보입니다.과거에는 자바 스크립트가 토큰 값을 포스트 데이터에 넣도록함으로써 더 간단하게 수행했습니다.템플릿에서 {% csrf_token %}을 (를) 사용하면 값을 전달하는 숨겨진 양식 필드가 생성됩니다. 그러나 {{csrf_token}}을 사용하면 토큰의 가치를 얻을 수 있으므로 자바 스크립트에서 다음과 같이 사용할 수 있습니다 ....

csrf_token = "{{ csrf_token }}";

그런 다음 해시에 필요한 키 이름과 함께이를 포함시켜 ajax 호출에 데이터로 제출할 수 있습니다.


 

{% csrf_token %}

HTML에 넣어 내부를 템플릿

<form></form>

다음과 같이 번역됩니다.

<input type='hidden' name='csrfmiddlewaretoken' value='Sdgrw2HfynbFgPcZ5sjaoAI5zsMZ4wZR' />

따라서 JS와 같이 다음과 같이 grep하지 마십시오.

token = $("#change_password-form").find('input[name=csrfmiddlewaretoken]').val()

그런 다음 POST와 같이 전달하십시오.

$.post( "/panel/change_password/", {foo: bar, csrfmiddlewaretoken: token}, function(data){
    console.log(data);
});

JS없이 Django에 양식이 올바르게 게시되면 csrf 토큰을 해킹하거나 지저분하게 전달하지 않고 ajax로 점진적으로 향상시킬 수 있어야합니다. 전체 양식을 직렬화하면 숨겨진 csrf 필드를

포함

하여 모든 양식 필드가 자동으로 선택됩니다 .

$('#myForm').submit(function(){
    var action = $(this).attr('action');
    var that = $(this);
    $.ajax({
        url: action,
        type: 'POST',
        data: that.serialize()
        ,success: function(data){
            console.log('Success!');
        }
    });
    return false;
});

Django 1.3 이상 및 jQuery 1.5 이상에서 이것을 테스트했습니다. 분명히 이것은 장고 앱뿐만 아니라 모든 HTML 양식에서 작동합니다.


비 jquery 답변 :

var csrfcookie = function() {
    var cookieValue = null,
        name = 'csrftoken';
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
};

용법:

var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('X-CSRFToken', csrfcookie());
request.onload = callback;
request.send(data);

Firebug와 함께 Firefox를 사용하십시오. 아약스 요청을 실행하는 동안 '콘솔'탭을 엽니 다. 함께

DEBUG=True

하면 응답으로 오류 페이지 장고 좋은 얻을 당신은 심지어 콘솔 탭에서 아약스 응답의 렌더링 된 HTML을 볼 수 있습니다.그러면 오류가 무엇인지 알게됩니다.


허용 된 답변은 붉은 청어 일 가능성이 높습니다. Django 1.2.4와 1.2.5의 차이점은 AJAX 요청에 대한 CSRF 토큰의 요구 사항이었습니다.

I came across this problem on Django 1.3 and it was caused by the CSRF cookie not being set in the first place. Django will not set the cookie unless it has to. So an exclusively or heavily ajax site running on Django 1.2.4 would potentially never have sent a token to the client and then the upgrade requiring the token would cause the 403 errors.

The ideal fix is here: http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
but you'd have to wait for 1.4 unless this is just documentation catching up with the code

Edit

Note also that the later Django docs note a bug in jQuery 1.5 so ensure you are using 1.5.1 or later with the Django suggested code: http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/#ajax


It seems nobody has mentioned how to do this in pure JS using the X-CSRFToken header and {{ csrf_token }}, so here's a simple solution where you don't need to search through the cookies or the DOM:

var xhttp = new XMLHttpRequest();
xhttp.open("POST", url, true);
xhttp.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
xhttp.send();

I've just encountered a bit different but similar situation. Not 100% sure if it'd be a resolution to your case, but I resolved the issue for Django 1.3 by setting a POST parameter 'csrfmiddlewaretoken' with the proper cookie value string which is usually returned within the form of your home HTML by Django's template system with '{% csrf_token %}' tag. I did not try on the older Django, just happened and resolved on Django1.3. My problem was that the first request submitted via Ajax from a form was successfully done but the second attempt from the exact same from failed, resulted in 403 state even though the header 'X-CSRFToken' is correctly placed with the CSRF token value as well as in the case of the first attempt. Hope this helps.

Regards,

Hiro


you can paste this js into your html file, remember put it before other js function

 

<script>
  // 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;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  $(document).ready(function() {
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
      beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
          xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
      }
    });
  });
</script>

 


As it is not stated anywhere in the current answers, the fastest solution if you are not embedding js into your template is:

Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> before your reference to script.js file in your template, then add csrfmiddlewaretoken into your data dictionary in your js file:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

One CSRF token is assigned to every session ( i.e. every time you log in). So before you wish to get some data entered by user and send that as ajax call to some function which is protected by csrf_protect decorator, try to find the functions that are being called before you are getting this data from user. E.g. some template must be being rendered on which your user is entering data. That template is being rendered by some function. In this function you can get csrf token as follows: csrf = request.COOKIES['csrftoken'] Now pass this csrf value in context dictionary against which template in question is being rendered. Now in that template write this line: Now in your javascript function, before making ajax request, write this: var csrf = $('#csrf').val() this will pick value of token passed to template and store it in variable csrf. Now while making ajax call, in your post data, pass this value as well : "csrfmiddlewaretoken": csrf

This will work even if you are not implementing django forms.

In fact, logic over here is : You need token which you can get from request. So you just need to figure out the function being called immediately after log in. Once you have this token, either make another ajax call to get it or pass it to some template which is accessible by your ajax.


for someone who comes across this and is trying to debug:

1) the django csrf check (assuming you're sending one) is here

2) In my case, settings.CSRF_HEADER_NAME was set to 'HTTP_X_CSRFTOKEN' and my AJAX call was sending a header named 'HTTP_X_CSRF_TOKEN' so stuff wasn't working. I could either change it in the AJAX call, or django setting.

3) If you opt to change it server-side, find your install location of django and throw a breakpoint in the csrf middleware.f you're using virtualenv, it'll be something like: ~/.envs/my-project/lib/python2.7/site-packages/django/middleware/csrf.py

import ipdb; ipdb.set_trace() # breakpoint!!
if request_csrf_token == "":
    # Fall back to X-CSRFToken, to make things easier for AJAX,
    # and possible for PUT/DELETE.
    request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')

Then, make sure the csrf token is correctly sourced from request.META

4) If you need to change your header, etc - change that variable in your settings file


If someone is strugling with axios to make this work this helped me:

import axios from 'axios';

axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'

Source: https://cbuelter.wordpress.com/2017/04/10/django-csrf-with-axios/


In my case the problem was with the nginx config that I've copied from main server to a temporary one with disabling https that is not needed on the second one in the process.

I had to comment out these two lines in the config to make it work again:

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;

Here's a less verbose solution provided by Django:

<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

// Ajax call here
$.ajax({
    url:"{% url 'members:saveAccount' %}",
    data: fd,
    processData: false,
    contentType: false,
    type: 'POST',
    success: function(data) {
        alert(data);
        }
    });
</script>

Source: https://docs.djangoproject.com/en/1.11/ref/csrf/

참고URL : https://stackoverflow.com/questions/5100539/django-csrf-check-failing-with-an-ajax-post-request

반응형