1. ホーム
  2. パイソン

[解決済み】AjaxのPOSTリクエストでDjangoのCSRFチェックが失敗する件

2022-04-17 04:40:55

質問

私の 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 を呼び出すと、確かに何らかのデータが入力されています。トークンが正しいかどうかを確認する方法はわかりませんが、何かを見つけて送信してくれているようで心強いです。

しかし、Djangoはまだ私のAJAXポストを拒否しています。

これが私のJavaScriptです。

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

以下は、Djangoから表示されるエラーです。

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

何か見落としていると思うし、単純なことかもしれないが、それが何なのか分からない。 SOを検索してみたところ、ビューのCSRFチェックをオフにするには csrf_exempt デコレーターがありますが、それは魅力的ではありませんね。 しかし、できれば Django が期待するように設計された方法で 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))

ご返信ありがとうございます。

どのように解決するのですか?

実際の解決策

OK、問題を突き止めることに成功しました。それは、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'));

?

提供された詳細が問題解決の一助となることを願っています :)