web-dev-qa-db-ja.com

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

AJAX投稿を介して、DjangoのCSRF保護メカニズムに準拠するいくつかのヘルプを使用できます。私はここの指示に従いました:

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

そのページにあるAJAXサンプルコードを正確にコピーしました。

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

xhr.setRequestHeader呼び出しの前にgetCookie('csrftoken')の内容を出力するアラートを配置すると、実際にいくつかのデータが取り込まれます。トークンが正しいことを確認する方法がわかりませんが、何かを見つけて送信することをお勧めします。

しかし、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チェックをオフにすることに関する情報を見ましたが、それは魅力的ではありません。私はそれを試してみましたが、うまくいきますが、POSTが、可能であればDjangoがそれを期待するように設計された方法で動作するようにします。

それが役に立つ場合に備えて、ここに私の見解がしていることの要点があります:

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

返信いただきありがとうございます!

168
firebush

実際のソリューション

わかりました、私はなんとか問題を追跡しました。これは、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に更新した後に発生します-AJAX 1.2.4でPOST Djangoリクエストにエラーはありませんでした(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'));

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

171

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

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

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

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

完了しました。

72
Kambiz

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

過去には、JavaScript値を取得してトークン値を投稿データに入れることで、より簡単にそれを行ってきました。

テンプレートで{%csrf_token%}を使用すると、値を保持する非表示のフォームフィールドが出力されます。ただし、{{csrf_token}}を使用する場合は、トークンの裸の値を取得するだけなので、このようにjavascriptで使用できます。..

csrf_token = "{{ csrf_token }}";

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

15
fatgeekuk

{% csrf_token %}は、<form></form>内にHTMLテンプレートを配置します

次のようなものに変換されます。

<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);
});
13
andilabs

フォームが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フォームでも機能します。

8
GivP

非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);
7
user1428660

受け入れられた答えは、おそらくニシンです。 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を待つ必要があります。

Edit

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

5
Steven

FirebugでFirefoxを使用します。 ajaxリクエストの実行中に「コンソール」タブを開きます。 DEBUG=Trueを使用すると、応答としてNice Djangoエラーページが表示され、コンソールタブでajax応答のレンダリングされたhtmlを確認することもできます。

その後、エラーが何であるかがわかります。

4
jammon

X-CSRFTokenヘッダーと{{ csrf_token }}を使用してこれを純粋なJSで行う方法について誰も言及していないようです。そのため、CookieまたはDOMを検索する必要のない簡単なソリューションを次に示します。

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

この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>
2
nooobpan

ちょっと違うが似たような状況に出会ったばかりです。それがあなたのケースの解決策であるかどうか100%はわかりませんが、Djangoパラメータ 'csrfmiddlewaretoken'を適切なCookie値文字列で設定することにより、POST 1.3の問題を解決しました通常は、Djangoのテンプレートシステムによって '{%csrf_token%}'タグを使用してホームHTMLの形式で返されます。私は古いDjangoを試さず、Django1.3で問題を解決しました。私の問題は、フォームからAjaxを介して送信された最初のリクエストが正常に実行されたが、ヘッダー「X-CSRFToken」がCSRFトークン値とともに正しく配置されていても、まったく同じからの2回目の試行が失敗したことです最初の試行の場合のように。お役に立てれば。

よろしく、

ヒロ

2

誰かがこの仕事をするためにaxiosで苦労しているなら、これは私を助けました:

import axios from 'axios';

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

ソース: https://cbuelter.wordpress.com/2017/04/10/Django-csrf-with-axios/

1
Mikko Pöri

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

1)Django csrfチェック(送信する場合)は here

2)私の場合、settings.CSRF_HEADER_NAMEが 'HTTP_X_CSRFTOKEN'に設定され、AJAX呼び出しが 'HTTP_X_CSRF_TOKEN'という名前のヘッダーを送信していたため、機能していませんでした。 AJAX呼び出しで変更するか、Django設定で変更できます。

3)サーバー側で変更することを選択した場合、Djangoのインストール場所を見つけ、virtualenvを使用しているcsrf middleware .. fにブレークポイントをスローすると、次のようになります:~/.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トークンがrequest.METAから正しく取得されていることを確認します

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

1
daino3

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

これは、Djangoフォームを実装していない場合でも機能します。

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

1

現在の回答のどこにも記載されていないため、テンプレートに埋め込みではない jsの場合の最速の解決策は次のとおりです。

テンプレートのscript.jsファイルへの参照の前に<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>を置き、jsファイルのcsrfmiddlewaretoken辞書にdataを追加します。

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

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/

0
Braden Holt

私の場合、問題はメインサーバーから一時的なサーバーにコピーしたnginxの設定にあり、プロセスの2番目のサーバーではhttpsが無効になっています。

私はそれを再び機能させるために設定のこれらの2行をコメントアウトする必要がありました:

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;
0
int_ua