Django CSRFチェックがAjax POSTリクエストで失敗する


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実際にいくつかのデータが入力されています。トークンが正しいことを確認する方法はわかりませんが、何かを見つけて送信することをお勧めします。

しかし、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_exemptデコレータを介して私のビューのCSRFチェックをオフにすることに関するいくつかの情報を見ましたが、それは魅力的ではありません。私はそれを試してみましたが機能しますが、可能であれば、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))

お返事ありがとうございます!


1
使用しているdjangoのバージョンは何ですか?
zsquare

正しいCSRFミドルウェアクラスを追加し、それらを正しい順序に配置しましたか?
darren

Jakubは以下の私の質問に回答しましたが、他の人に役立つ場合に備えて:@zsquare:バージョン1.2.3。@mongoose_za:はい、正しい順序で追加されます。
firebush、2011

回答:


181

実際のソリューション

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」はtrueです。

基本的には、ヘッダーが設定されていないことを意味します。したがって、このJS行に問題があります:

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

提供された詳細が問題の解決に役立つことを願っています:)


これはうまくいきました!上に貼り付けたとおりに.ajaxSetup関数を入力し、403エラーなしで投稿できるようになりました。解決策を共有してくれてありがとう、Jakub。良い発見。:)
firebush '26

jQueryドキュメントのカウンターajaxSetupではなくajaxSend実行を使用:api.jquery.com/jQuery.ajaxSetup
Mark Lavin

1.3を使用すると、公式のdjangoドキュメントエントリが機能しました。
monkut、2011

1
試しましたが、うまくいかないようです。jQueryv1.7.2を使用しています。私の質問は、stackoverflow.com
daydreamer

モバイルデバイスからページがリクエストされたときにcsrf Cookie を強制的に設定するには、ビュー関数にアノテーション@ensure_csrf_cookieを追加する必要があります。
Kane

172

この$.ajax関数を使用する場合は、csrfトークンをデータ本文に追加するだけです。

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

2
マークされた回答を使用するとうまくいきますが、ここであなたのソリューションを使用するとうまくいきません。しかし、あなたの解決策はうまくいくはずですが、なぜうまくいかないのかわかりません。Django 1.4で他に必要なことはありますか?
ホウマン、

1
ありがとう!とても簡単。django 1.8とjquery 2.1.3でも動作します
Alejandro Veintimilla

19
このソリューションでは、テンプレートにJavaScriptを埋め込む必要がありますか?
Mox、2015年

15
@Mox:これをhtmlに入れますが、jsファイルの上にはajax関数があります <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
HereHere

ありがとう!とてもシンプルでエレガントです。Django 1.8で動作します。通話で辞書に追加csrfmiddlewaretoken: '{{ csrf_token }}'dataました$.post
Pavel Yudaev 2017

75

次の行をjQueryコードに追加します。

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

そして完了。


私のフォームにファイルのアップロードがあることを除いて、私はこれを試しました。私のバックエンドはジャンゴですが、それでもエラー400が発生しますCSRF Failed: CSRF token missing or incorrect.
フセイン

16

問題は、djangoがcookieからの値がフォームデータの一部として渡されることを期待しているためです。前の回答のコードでは、JavaScriptにCookie値を探し出してフォームデータに入れています。これは技術的な観点から見ればすばらしい方法ですが、少し冗長に見えます。

以前は、JavaScriptにトークン値を投稿データに挿入させることで、もっと簡単に実現していました。

テンプレートで{%csrf_token%}を使用すると、値を保持する非表示のフォームフィールドが生成されます。しかし、{{csrf_token}}を使用する場合、トークンの最低限の値を取得するだけなので、これを次のようにJavaScriptで使用できます。

csrf_token = "{{ csrf_token }}";

次に、必要なキー名をハッシュに含めて、それをajax呼び出しへのデータとして送信できます。


@aehlke静的ファイルを持つことができます。ソースコードには、django変数windowオブジェクトに登録して後でアクセスできるようにする、すばらしい例が示されています。静的ファイルでも。
KitKat、

3
@KitKat確かに:)ここに私の古くて無知なコメントを申し訳ありません。いい視点ね。
aehlke 2014年

静的ファイルについて。ほんの少しのjsを気にしないのであれば問題ありません。私は、{{csrf_token}}をメインのhtmlテンプレートに配置するだけで、requirejsの呪文からそれほど遠くありません。魅力のように働いた。
JL Peyret 2015

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以降でテストしました。明らかに、これはDjangoアプリだけでなく、あらゆるHTMLフォームで機能します。


5

FirebugでFirefoxを使用します。ajaxリクエストを実行している間に「コンソール」タブを開きます。とDEBUG=Trueあなたが応答として素敵なDjangoのエラーページを取得し、あなたもコンソール]タブでAJAX応答のレンダリングされたHTMLを見ることができます。

次に、エラーが何であるかがわかります。


5

受け入れられた答えは、おそらく赤ニシンです。Django 1.2.4と1.2.5の違いは、AJAXリクエストのCSRFトークンの要件でした。

Django 1.3でこの問題に遭遇しましたが、それはそもそもCSRF Coo​​kieが設定されていないことが原因でした。Djangoは、必要がない限りCookieを設定しません。したがって、Django 1.2.4で実行されている排他的または非常にajaxサイトがトークンをクライアントに送信する可能性はなく、トークンを必要とするアップグレードによって403エラーが発生する可能性があります。

理想的な修正はこちらです:http : //docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
しかし、1.4を待たなければなりませんこれは単にコードに追いついたドキュメントです

編集する

また、後のDjangoドキュメントではjQuery 1.5のバグを指摘しているため、Djangoの推奨コードで1.5.1以降を使用していることを確認してください:http : //docs.djangoproject.com/en/1.3/ref/contrib/csrf/# ajax


私の答えはそれを書いている時点では正確でした:) Djangoが1.2.4から1.2.5に更新された直後です。また、最新のjQueryバージョンが1.5だったときもそうです。問題の原因がバグのあるjQuery(1.5)であることが判明し、この情報がDjangoドキュメントに追加されました。私の場合:Cookieが設定され、トークンがAJAXリクエストに追加されませんでした。バグ修正済みのjQuery 1.5で修正が機能しました。現在のところ、そこに記載されているサンプルコードを使用し、最新のjQueryを使用するだけで、公式ドキュメントをそのまま使用できます。あなたの問題の原因は、ここで説明した問題とは異なるもの
でし

2
これでensure_csrf_cookie、ビューをラップしてビューがCookieを送信することを保証できるというデコレータができました。
Brian Neal、2011

これが私が抱えていた問題csrftokenです。そもそもcookie がありません。
2017年

5

X-CSRFTokenヘッダーとを使用して純粋なJSでこれを行う方法について誰も言及していない{{ csrf_token }}ようです。そのため、Cookieまたは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ファイルへの参照の前に置き、jsファイルの辞書に追加csrfmiddlewaretokenしますdata

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

2

少し違うが似たような状況に遭遇しました。あなたのケースの解決策になるかどうかは100%わかりませんが、DjangoのホームHTMLのフォーム内で通常返される適切なCookie値の文字列でPOSTパラメータ 'csrfmiddlewaretoken'を設定することで、Django 1.3の問題を解決しました'{%csrf_token%}'タグ付きのテンプレートシステム。私は古いDjangoを試したのではなく、Django1.3で発生して解決しました。私の問題は、フォームからAjax経由で送信された最初のリクエストは正常に実行されましたが、まったく同じものからの2回目の試行が失敗し、ヘッダー「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

すべてのセッションに(つまり、ログインするたびに)1つのCSRFトークンが割り当てられます。したがって、ユーザーが入力したデータを取得し、csrf_protectデコレーターによって保護されている関数へのajax呼び出しとして送信する前に、ユーザーからこのデータを取得する前に呼び出されている関数を見つけてください。たとえば、ユーザーがデータを入力するテンプレートをレンダリングする必要があります。そのテンプレートは、いくつかの関数によってレンダリングされています。この関数では、次のようにcsrfトークンを取得できます。csrf = request.COOKIES ['csrftoken']次に、問題のテンプレートがレンダリングされているコンテキストディクショナリでこのcsrf値を渡します。次に、そのテンプレートで次の行を記述します。JavaScript関数で、ajaxリクエストを行う前に、次のように記述します:var csrf = $( '#csrf')。val()これにより、テンプレートに渡されたトークンの値が選択され、変数csrfに格納されます。ajax呼び出しを行うときに、投稿データで次の値も渡します。 "csrfmiddlewaretoken":csrf

これは、djangoフォームを実装していなくても機能します。

実際、ここのロジックは次のとおりです。リクエストから取得できるトークンが必要です。したがって、ログイン直後に呼び出される関数を理解する必要があります。このトークンを取得したら、それを取得するために別のajax呼び出しを行うか、ajaxからアクセスできるテンプレートに渡します。


あまりよく構成されていませんが、よく説明されています。私の問題は、私がこの方法でcsrfを送信していたことでした:csrftoken: csrftokenではなくcsrfmiddlwaretoken: csrftoken。変更後、機能しました。ありがとう
ほぼ初心者

1

これに遭遇してデバッグしようとしている人のために:

1)django 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トークンがリクエストから正しく取得されていることを確認します。META

4)ヘッダーなどを変更する必要がある場合-設定ファイルでその変数を変更します



0

私の場合、問題は、メインサーバーから一時的なものにコピーしたnginx構成にあり、プロセスの2番目のものでは不要なhttpsを無効にしていた。

再度機能させるために、設定の次の2行をコメント化する必要がありました。

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