Ajax POST isteği ile Django CSRF denetimi başarısız oluyor


180

AJAX yazı ile Django CSRF koruma mekanizması ile uyumlu bazı yardım kullanabilirsiniz. Buradaki talimatları takip ettim:

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

Ben tam olarak bu sayfada sahip AJAX örnek kod kopyaladım:

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

Aramadan getCookie('csrftoken')önce içeriğini yazdıran bir uyarı koydum xhr.setRequestHeaderve gerçekten bazı verilerle dolduruldu. Simgenin doğru olduğunu nasıl doğrulayacağımdan emin değilim, ancak bir şey bulması ve göndermesi teşvik ediliyor.

Ancak Django hala AJAX gönderimi reddediyor.

İşte benim JavaScript'im:

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

İşte Django'dan gördüğüm hata:

[23 / Şub / 2011 22:08:29] "POST / ezberle / HTTP / 1.1" 403 2332

Eminim bir şeyleri kaçırıyorum ve belki de basit, ama ne olduğunu bilmiyorum. SO etrafında arama yaptım ve csrf_exemptdekoratör aracılığıyla görüşümü için CSRF kontrolünü kapatma hakkında bazı bilgiler gördüm , ama bu hoş bulmuyorum. Bunu denedim ve işe yarıyor, ancak POST'umu mümkünse Django'nun beklediği şekilde tasarlamayı tercih ediyorum.

Her ne kadar yararlı olursa, işte benim görüşümün özü:

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

Cevaplarınız için teşekkürler!


1
Hangi django sürümünü kullanıyorsunuz?
zsquare

Doğru CSRF ara katman yazılımı sınıflarına eklediniz ve bunları doğru sıraya koydunuz mu?
darren

Jakub aşağıdaki sorumu yanıtladı, ancak diğer insanlar için yararlı olması durumunda: @zsquare: version 1.2.3. @mongoose_za: Evet, eklenirler ve doğru sıradadırlar.
firebush

Yanıtlar:


181

Gerçek çözüm

Tamam, sorunu tespit etmeyi başardım. Javascript (aşağıda önerdiğim gibi) kodunda yatar.

İhtiyacınız olan şey:

$.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'));
         }
     } 
});

resmi dokümanlarda yayınlanan kod yerine: https://docs.djangoproject.com/en/2.2/ref/csrf/

Çalışma kodu, bu Django girişinden gelir: http://www.djangoproject.com/weblog/2011/feb/08/security/

Yani genel çözüm: "ajaxSend işleyicisi yerine ajaxSetup işleyicisini kullan". Neden işe yaradığını bilmiyorum. Ama benim için çalışıyor :)

Önceki gönderi (cevapsız)

Aslında aynı problemi yaşıyorum.

Django 1.2.5'e güncellendikten sonra ortaya çıkar - Django 1.2.4'te AJAX POST istekleriyle ilgili bir hata yoktu (AJAX hiçbir şekilde korunmadı, ancak iyi çalıştı).

Tıpkı OP gibi, Django belgelerinde yayınlanan JavaScript snippet'ini denedim. JQuery 1.5 kullanıyorum. Ayrıca "django.middleware.csrf.CsrfViewMiddleware" ara katman yazılımı kullanıyorum.

Middleware kodunu takip etmeye çalıştım ve bu başarısız olduğunu biliyorum:

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

ve sonra

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

bu "if" doğrudur, çünkü "request_csrf_token" boştur.

Temel olarak, başlığın ayarlanmadığı anlamına gelir. Bu JS hattıyla ilgili yanlış bir şey var mı?

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

?

Umarım verilen ayrıntılar sorunu çözmemize yardımcı olur :)


Bu işe yaradı! Yukarıda yapıştırdığınızda .ajaxSetup işlevini koydum ve şimdi 403 hatası olmadan gönderebiliyorum. Çözümü paylaştığın için teşekkürler Jakub. İyi bulmak. :)
firebush

JQuery belgelerine sayaç çalıştırmak ajaxSetupyerine kullanma ajaxSend: api.jquery.com/jQuery.ajaxSetup
Mark Lavin

1.3 kullanarak, resmi django belgeleri girişi benim için çalıştı.
monkut

1
Denedim ama bu benim için çalışmıyor gibi görünüyor, jQuery v1.7.2 kullanıyorum, sorum şu stackoverflow.com/questions/11812694/…
daydreamer

Sayfa mobil cihazlardan istendiğinde set csrf çerezini zorlamak için görünüm fonksiyonuma ek açıklama @ensure_csrf_cookie eklemek zorundayım .
Kane

172

$.ajaxİşlevi kullanırsanız csrf, veri gövdesine jetonu ekleyebilirsiniz :

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

2
işaretli yanıtı kullandığımda benim için işe yarıyor, ancak çözümünüzü burada kullanırsam işe yaramaz. Ama çözümünüz işe yarıyor olmalı, neden işe yaramadığını anlamıyorum. Django 1.4'te yapılması gereken başka bir şey var mı?
Houman

1
Teşekkürler! çok basit. Hala django 1.8 ve jquery 2.1.3 üzerinde çalışıyor
Alejandro Veintimilla

19
Bu çözüm, şablonda gömülü javascript gerektirir değil mi?
Mox

15
@Mox: Bunu html'ye koyun, ancak ajax işlevi olan Js dosyanızın üstüne <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
HereHere

Teşekkürler! Çok basit ve zarif. Benim için Django 1.8 ile çalışıyor. Eklediğim csrfmiddlewaretoken: '{{ csrf_token }}'benim için databir sözlüğe $.postçağrısı.
Pavel Yudaev

75

Bu satırı jQuery kodunuza ekleyin:

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

ve bitti.


Formumun bir dosya yüklemesi olması dışında bunu denedim. Benim arka uç django ve hala hata 400 olsunCSRF Failed: CSRF token missing or incorrect.
Hussain

16

Sorun, django'nun tanımlama bilgisindeki değerin form verilerinin bir parçası olarak geri aktarılmasını beklemesidir. Önceki yanıtın kodu, çerez değerini avlamak ve form verilerine koymak için javascript alıyor. Bu teknik açıdan yapmanın güzel bir yolu, ama biraz ayrıntılı görünüyor.

Geçmişte, javascript'i token değerini yazı verilerine koymak için daha basit yaptım.

Şablonunuzda {% csrf_token%} kullanırsanız, değeri taşıyan gizli bir form alanı alırsınız. Ancak, {{csrf_token}} kullanırsanız, jetonun çıplak değerini alırsınız, böylece javascript'te bu şekilde kullanabilirsiniz ....

csrf_token = "{{ csrf_token }}";

Daha sonra bunu karma içinde gerekli anahtar adıyla birlikte ajax çağrısına veri olarak gönderebilirsiniz.


@aehlke Statik dosyalarınız olabilir. Nerede kaynak kodunda, güzel bir örnek görebilirsiniz Django değişkenleri kayıt içinde windownesnenin onlar sonradan erişilebilir, böylece. Statik dosyalarda bile.
KitKat

3
@KitKat gerçekten :) Buradaki eski, cahil yorumum için özür dilerim. İyi bir nokta.
aehlke

statik dosyaları yeniden. Sorun değil, senin html js biraz küçük sakıncası yoksa. Sadece {{csrf_token}} ana html şablonuna koydum, isteklerin büyüsünden uzak değil. bir cazibe gibi çalıştı.
JL Peyret

14

{% csrf_token %}Html içinde koymak içini şablonları<form></form>

şöyle bir şeye çevirir:

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

Öyleyse neden sadece JS'nizde böyle grep değil:

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

ve daha sonra geçirin, örneğin:

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

11

Jquery olmayan cevap:

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

kullanımı:

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

Formunuz JS olmadan Django'da doğru bir şekilde yayınlanıyorsa, csrf jetonunu hacklemeden veya dağınık geçmeden ajax ile aşamalı olarak geliştirebilmelisiniz. Sadece tüm formu serileştirdiğinizde, gizli csrf alanı da dahil olmak üzere tüm form alanlarınız otomatik olarak alınır:

$('#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;
});

Bunu Django 1.3+ ve jQuery 1.5+ ile test ettim. Bu sadece Django uygulamaları için değil, herhangi bir HTML formu için de işe yarayacaktır.


5

Firebug ile Firefox kullanın. Ajax isteğini başlatırken 'Konsol' sekmesini açın. İleDEBUG=True size cevap olarak hata sayfasında django güzel olsun ve hatta konsol sekmede ajax yanıt render html görebilirsiniz.

Sonra hatanın ne olduğunu bileceksiniz.


5

Kabul edilen cevap büyük olasılıkla kırmızı bir ringa balığıdır. Django 1.2.4 ve 1.2.5 arasındaki fark, AJAX istekleri için bir CSRF belirteci gereksinimi idi.

Bu sorunla Django 1.3 üzerinde karşılaştım ve CSRF tanımlama bilgisinin ayarlanmamasından kaynaklandı ve ilk etapta . Django, zorunlu olmadığı sürece çerezi ayarlamaz. Bu nedenle, Django 1.2.4 üzerinde çalışan özel veya yoğun bir ajax sitesi potansiyel olarak istemciye hiçbir zaman bir simge göndermez ve daha sonra jetonu gerektiren yükseltme 403 hatalarına neden olur.

İdeal düzeltme burada: http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
ancak sürece 1.4 beklemeniz gerekir bu sadece kodu yakalayan belgelerdir

Düzenle

Ayrıca, daha sonraki Django belgelerinin jQuery 1.5'te bir hata not ettiğine dikkat edin, bu nedenle Django tarafından önerilen kodla 1.5.1 veya üstünü kullandığınızdan emin olun: http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/# ajax


Cevabım yazarken doğruydu :) Django 1.2.4'ten 1.2.5'e güncellendikten hemen sonraydı. Ayrıca en yeni jQuery sürümü 1.5 idi. Sorunun kaynağının jQuery (1.5) hata aldığı ortaya çıktı ve bu bilgi belirttiğiniz gibi Django doc'a eklendi. Benim durumumda: çerez WAS ayarlandı ve jeton AJAX isteğine eklenmedi. Verilen düzeltme bugged jQuery 1.5 için çalıştı. Şu andan itibaren, burada verilen örnek kodu kullanarak ve en yeni jQuery kullanarak resmi belgelere sadık kalabilirsiniz. Sorununuz burada tartışılan sorunlardan farklı bir kaynağa sahipti :)
Jakub Gocławski

2
Artık ensure_csrf_cookieçerezi gönderdiğinden emin olmak için bir görünümün etrafına sarılabileceğiniz bir dekoratör var .
Brian Neal

Bu benim yaşadığım sorun csrftoken, ilk başta çerez yok , teşekkürler!
17'de

5

Kimse bunu X-CSRFTokenbaşlığı kullanarak saf JS'de nasıl yapacağından bahsetmedi ve {{ csrf_token }}bu yüzden çerezler veya DOM'da arama yapmanız gerekmeyen basit bir çözüm:

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

4

Mevcut cevaplarında hiçbir yerinde belirtilmeyen gibi, en hızlı çözümü ise gömme değil şablonunuza js geçerli:

Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>Şablonunuzdaki script.js dosyasına referans önce ve sonra eklemek csrfmiddlewaretokeniçin içine datasizin js dosyasında sözlüğe:

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

2

Biraz farklı ama benzer bir durumla karşılaştım. Durumunuz için bir çözüm olup olmayacağından% 100 emin değilim, ancak genellikle Django tarafından ev HTML'niz şeklinde döndürülen uygun çerez değeri dizesiyle bir POST parametresi 'csrfmiddlewaretoken' ayarlayarak Django 1.3 için sorunu çözdüm '{% csrf_token%}' etiketli şablon sistemi. Eski Django'da denemedim, sadece Django1.3'te oldu ve çözüldü. Benim sorunum, bir formdan Ajax aracılığıyla gönderilen ilk istek başarıyla yapıldı, ancak tam olarak aynı gelen ikinci denemenin başarısız oldu, 'X-CSRFToken' başlığı da CSRF token değeri ile doğru bir şekilde yerleştirilmiş olsa bile 403 durumu ile sonuçlandı ilk denemede olduğu gibi. Bu yardımcı olur umarım.

Saygılarımızla,

Hiro


2

Bu js'yi html dosyanıza yapıştırabilirsiniz, diğer js işlevinden önce koymayı unutmayın

<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

Her oturuma bir CSRF jetonu atanır (yani, her oturum açışınızda). Bu nedenle, kullanıcı tarafından girilen bazı verileri almak ve bunu csrf_protect dekoratör tarafından korunan bir işleve ajax çağrısı olarak göndermeden önce, bu verileri kullanıcıdan almadan önce çağrılan işlevleri bulmaya çalışın. Örneğin, kullanıcınızın veri girdiği bir şablon oluşturulmalıdır. Bu şablon bir işlev tarafından oluşturuluyor. Bu işlevde csrf jetonunu aşağıdaki gibi alabilirsiniz: csrf = request.COOKIES ['csrftoken'] Şimdi bu csrf değerini, söz konusu şablonun işlenmekte olduğu bağlam sözlüğüne aktarın. Şimdi bu şablonda şu satırı yazın: Şimdi javascript işlevinizde, ajax isteğinde bulunmadan önce şunu yazın: var csrf = $ ('# csrf'). val () bu, şablona iletilen belirtecin değerini seçecek ve csrf değişkeninde saklayacaktır. Şimdi ajax çağrısı yaparken, yazı verilerinizde de bu değeri iletin: "csrfmiddlewaretoken": csrf

Bu, django formları uygulamıyor olsanız bile çalışacaktır.

Aslında, buradaki mantık: İstekten alabileceğiniz jetona ihtiyacınız var. Bu yüzden, giriş yaptıktan hemen sonra çağrılan işlevi anlamanız gerekir. Bu belirteci aldıktan sonra, almak için başka bir ajax çağrısı yapın veya ajax'ınız tarafından erişilebilen bir şablona geçirin.


Çok iyi yapılandırılmamış, ancak iyi açıklanmış. Benim sorunum, csrf'i şu şekilde gönderiyordum: csrftoken: csrftokenbunun yerine csrfmiddlwaretoken: csrftoken. Değişiklikten sonra işe yaradı. Teşekkürler
neredeyse bir acemi

1

bununla karşılaşan ve hata ayıklamaya çalışan biri için:

1) django csrf kontrolü (bir tane gönderdiğinizi varsayalım) burada

2) Benim durumumda, settings.CSRF_HEADER_NAME'HTTP_X_CSRFTOKEN' olarak ayarlandı ve AJAX çağrım 'HTTP_X_CSRF_TOKEN' adlı bir başlık gönderiyordu, bu yüzden işler işe yaramadı. AJAX çağrısında veya django ayarında değiştirebilirim.

3) Sunucu tarafını değiştirmeyi seçerseniz, django'nun kurulum konumunuzu bulun ve kullandığınız csrf middleware.f'ye bir kesme noktası atın virtualenv, şöyle bir şey olacaktır:~/.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, '')

Ardından, csrfbelirtecin isteğin doğru bir şekilde kaynaklandığından emin olun.

4) Başlığınızı vb. Değiştirmeniz gerekiyorsa - ayarlar dosyanızdaki bu değişkeni değiştirin



0

Benim durumumda sorun ana sunucudan geçici ikinciye kopyaladığım nginx config ile işlemde ikinci olan gerekli https devre dışı bırakma ile oldu.

Ben yeniden çalışması için yapılandırma bu iki satır yorum vardı:

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;

0

İşte Django tarafından sağlanan daha az ayrıntılı bir çözüm:

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

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.