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


180

내 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))

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


1
장고의 어떤 버전을 사용하고 있습니까?
zsquare

올바른 CSRF 미들웨어 클래스를 추가하고 올바른 순서로 배치 했습니까?
대런

Jakub은 아래 질문에 대답했지만 다른 사람들에게 유용한 경우를 대비하여 @zsquare : version 1.2.3입니다. @mongoose_za : 예, 올바른 순서로 추가됩니다.
firebush

답변:


181

실제 솔루션

좋아, 나는 문제를 추적했다. 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'));

?

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


이것은 효과가 있었다! 위에서 붙여 넣을 때 .ajaxSetup 함수를 넣었으며 이제 403 오류없이 게시 할 수 있습니다. Jakub 솔루션을 공유해 주셔서 감사합니다. 잘 찾았어요 :)
firebush

사용 ajaxSetup하기보다는 ajaxSend실행이 jQuery를 워드 프로세서에 대응 : api.jquery.com/jQuery.ajaxSetup
마크 Lavin

1.3을 사용하면 공식 장고 문서 항목이 도움이되었습니다.
monkut

1
나는 시도했지만 이것이 나를 위해 작동하지 않는 것 같습니다, jQuery v1.7.2를 사용하고 있습니다, 내 질문은 stackoverflow.com/questions/11812694/…
daydreamer

나는 주석 추가해야 @ensure_csrf_cookie을 설정 강제로 내보기 기능에 CSRF의 페이지가 모바일 장치에서 요청 될 때 쿠키를.
Kane

172

$.ajax함수 를 사용 csrf하면 데이터 본문에 토큰을 추가하기 만하면됩니다 .

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

2
표시된 답변을 사용하면 효과가 있지만 여기에서 솔루션을 사용하면 효과가 없습니다. 그러나 귀하의 솔루션은 작동해야하지만 왜 그렇지 않은지 이해하지 못합니다. 장고 1.4에서해야 할 다른 일이 있습니까?
Houman

1
감사! 너무 간단합니다. 여전히 django 1.8 및 jquery 2.1.3에서 작동합니다.
Alejandro Veintimilla

19
이 솔루션을 사용하려면 템플릿에 자바 스크립트가 포함되어 있어야합니까?
Mox

15
@Mox : 이것을 html로, 그러나 ajax 함수 인 Js 파일 위에 놓으십시오 <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
HereHere

감사! 매우 간단하고 우아합니다. Django 1.8에서 작동합니다. 전화로 사전에 추가 csrfmiddlewaretoken: '{{ csrf_token }}'했습니다 . data$.post
Pavel Yudaev

75

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

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

그리고 완료.


내 양식에 파일 업로드가있는 것을 제외하고는 이것을 시도했습니다. 내 백엔드 장고하고 여전히 오류 400 얻을CSRF Failed: CSRF token missing or incorrect.
후세인

16

문제는 django가 쿠키의 값이 양식 데이터의 일부로 다시 전달되기를 기대하기 때문입니다. 이전 답변의 코드는 javascript가 쿠키 값을 찾아서 양식 데이터에 넣도록합니다. 그것은 기술적 인 관점에서 그것을하는 멋진 방법이지만 조금 장황하게 보입니다.

과거에는 자바 스크립트가 토큰 값을 포스트 데이터에 넣도록함으로써 더 간단하게 수행했습니다.

템플릿에서 {% csrf_token %}을 (를) 사용하면 값을 전달하는 숨겨진 양식 필드가 생성됩니다. 그러나 {{csrf_token}}을 사용하면 토큰의 가치를 얻을 수 있으므로 자바 스크립트에서 다음과 같이 사용할 수 있습니다 ....

csrf_token = "{{ csrf_token }}";

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


@aehlke 정적 파일을 가질 수 있습니다. 소스 코드에서 django 변수window객체 에 등록 하여 나중에 액세스 할 수있는 좋은 예를 볼 수 있습니다 . 정적 파일에서도.
KitKat

3
@KitKat 실제로 :) 내 고대의 무지한 의견에 대해 죄송합니다. 좋은 지적.
aehlke

정적 파일을 다시 작성하십시오. 작은 js로 HTML을 신경 쓰지 않는다면 문제가되지 않습니다. requirejs incantations에서 멀지 않은 메인 HTML 템플릿에 {{csrf_token}}을 넣었습니다. 매력처럼 일했다.
JL Peyret

14

{% 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);
});

11

비 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);

8

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 양식에서 작동합니다.


5

Firebug와 함께 Firefox를 사용하십시오. 아약스 요청을 실행하는 동안 '콘솔'탭을 엽니 다. 와DEBUG=True 하면 응답으로 오류 페이지 장고 좋은 얻을 당신은 심지어 콘솔 탭에서 아약스 응답의 렌더링 된 HTML을 볼 수 있습니다.

그러면 오류가 무엇인지 알게됩니다.


5

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

Django 1.3 에서이 문제가 발생했으며 CSRF 쿠키가 설정되지 않았기 때문에 발생했습니다. 가 처음 . 장고는 쿠키를 설정하지 않으면 쿠키를 설정하지 않습니다. 따라서 Django 1.2.4에서 실행되는 독점적이거나 많은 아약스 사이트는 잠재적으로 클라이언트에게 토큰을 보내지 않았으며 토큰을 요구하는 업그레이드로 인해 403 오류가 발생합니다.

이상적인 수정 사항은 다음과 같습니다. http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
이지만 1.4가 아니면 기다려야합니다. 이것은 코드를 따라 잡는 문서 일뿐입니다.

편집하다

또한 최신 장고 문서는 jQuery 1.5의 버그를 참고하므로 장고 제안 코드와 함께 1.5.1 이상을 사용하고 있는지 확인 하십시오 . http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/# 아약스


내 대답은 그것을 쓸 당시에 정확했습니다 :) 장고가 1.2.4에서 1.2.5로 업데이트 된 직후였습니다. 최신 jQuery 버전이 1.5 일 때도있었습니다. 문제의 원인이 jQuery (1.5)에 버그가 있음이 밝혀 졌으며이 정보는 이제 진술 한 것처럼 장고 문서에 추가됩니다. 제 경우에는 쿠키가 설정되었고 토큰이 AJAX 요청에 추가되지 않았습니다. 버그가있는 jQuery 1.5에 대한 수정이있었습니다. 현재로서는 제공된 예제 코드와 최신 jQuery를 사용하여 공식 문서를 고수 할 수 있습니다. 귀하의 문제는 여기서 논의 된 문제와 다른 소스를 가지고 있습니다 :)
Jakub Gocławski

2
이제 ensure_csrf_cookie뷰를 감싸서 쿠키를 보낼 수 있도록 하는 데코레이터 가 있습니다.
Brian Neal

이것은 내가 겪고있는 문제 csrftoken입니다. 처음 에는 쿠키 가 없습니다 . 감사합니다!
crhodes

5

X-CSRFToken헤더를 사용하여 순수 JS 에서이 작업을 수행하는 방법을 아무도 언급하지 않은 것 같습니다 {{ csrf_token }}. 따라서 쿠키 또는 DOM을 검색 할 필요가없는 간단한 솔루션이 있습니다.

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

4

현재 답변의 어느 곳에도 언급되어 있지 않으므로 js를 템플릿에 포함하지 않으면 가장 빠른 솔루션 은 다음과 같습니다.

넣어 <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>템플릿에 script.js 파일을 참조하기 전에, 다음 추가 csrfmiddlewaretoken당신에 data당신의 js 파일에 사전 :

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

2

방금 조금 다르지만 비슷한 상황이 발생했습니다. 귀하의 사례에 대한 해결책인지 100 % 확실하지는 않지만, Django의 홈 HTML 형식으로 반환되는 적절한 쿠키 값 문자열로 POST 매개 변수 'csrfmiddlewaretoken'을 설정하여 Django 1.3의 문제를 해결했습니다. '{% csrf_token %}'태그가있는 템플릿 시스템. 나는 더 오래된 장고를 시도하지 않았고, 그냥 장고 1.3에서 해결되었습니다. 내 문제는 양식에서 Ajax를 통해 제출 된 첫 번째 요청이 성공적으로 수행되었지만 정확히 동일한 시도에서 실패한 두 번째 시도는 헤더 'X-CSRFToken'이 CSRF 토큰 값으로 올바르게 배치되었지만 403 상태가되었다는 것입니다 첫 번째 시도의 경우와 같습니다. 도움이 되었기를 바랍니다.

문안 인사,

히로


2

이 js를 html 파일에 붙여 넣을 수 있습니다. 다른 js 함수 앞에 놓으십시오.

<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>


1

하나의 CSRF 토큰이 모든 세션에 할당됩니다 (즉, 로그인 할 때마다). 따라서 사용자가 일부 데이터를 입력하고 csrf_protect 데코레이터로 보호되는 일부 함수에 대한 ajax 호출로 보내려면 사용자 로부터이 데이터를 가져 오기 전에 호출되는 함수를 찾으십시오. 예를 들어 사용자가 데이터를 입력하는 일부 템플릿을 렌더링해야합니다. 해당 템플릿은 일부 기능에 의해 렌더링됩니다. 이 함수에서 다음과 같이 csrf 토큰을 얻을 수 있습니다. csrf = request.COOKIES [ 'csrftoken'] 이제 해당 템플리트가 렌더링되는 컨텍스트 사전에서이 csrf 값을 전달하십시오. 이제 해당 템플리트에서 다음 행을 작성하십시오. 이제 javascript 함수에서 ajax 요청을 작성하기 전에 다음을 작성하십시오. var csrf = $ ( '# csrf'). val () 템플릿에 전달 된 토큰의 값을 선택하여 변수 csrf에 저장합니다. 이제 아약스 호출을 수행하는 동안 게시물 데이터에서이 값도 전달하십시오. "csrfmiddlewaretoken": csrf

장고 양식을 구현하지 않은 경우에도 작동합니다.

실제로 여기있는 논리는 다음과 같습니다. 요청에서 얻을 수있는 토큰이 필요합니다. 따라서 로그인 한 직후에 호출되는 함수를 파악하면됩니다.이 토큰을 가져 오면 다른 ajax 호출을 수행하여 가져 오거나 ajax에서 액세스 할 수있는 일부 템플리트로 전달하십시오.


잘 구조화되어 있지 않지만 잘 설명되어 있습니다. 내 문제는 다음과 같은 방식으로 csrf를 보내는 것이 었 csrftoken: csrftoken습니다 csrfmiddlwaretoken: csrftoken. 변경 후 효과가있었습니다. 감사합니다
거의 초보자

1

이 문제를 겪고 디버깅하려고하는 사람을 위해 :

1) 장고 csrf 검사 (보내고 있다고 가정)는 여기에 있습니다

2) 제 경우에는 settings.CSRF_HEADER_NAME'HTTP_X_CSRFTOKEN'으로 설정되었고 AJAX 호출이 'HTTP_X_CSRF_TOKEN'이라는 헤더를 보내서 작동하지 않았습니다. AJAX 호출 또는 django 설정에서 변경할 수 있습니다.

3) 서버 측을 변경하기로 선택한 경우 django의 설치 위치를 찾고 csrf middleware사용중인 .f에 중단 점을 던지 virtualenv십시오.~/.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, '')

그런 다음 csrf요청에서 토큰이 올바르게 제공 되는지 확인하십시오.

4) 헤더 등을 변경 해야하는 경우 설정 파일에서 해당 변수를 변경하십시오.



0

필자의 경우 문제는 메인 서버에서 프로세스의 두 번째 서버에는 필요하지 않은 https를 비활성화하는 임시 서버로 복사 한 nginx 구성에 문제가있었습니다.

구성 에서이 두 줄을 주석 처리하여 다시 작동하게해야했습니다.

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;

0

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>

출처 : https://docs.djangoproject.com/en/1.11/ref/csrf/

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.