DjangoでPOSTメッセージにCSRFtokenを含ませる方法まとめ

概要

Webサイトに偽りの処理を要求し、攻撃することをCSRF(Cross Site Request Forgery)と呼びます。Djangoではこの攻撃を防ぐため、POSTリクエストにはCookieに含まれるcsrftokenの情報が含まれていなければいけない(無い場合にはリクエストを拒否する)仕様になっています。csrftokenは元々はサーバから受け取る乱数の情報であり、この情報を持っているということは、少なくとも正規の方法でページにアクセスした状態でリスクエストを送っている、ということになり、リスクを低減できると言うわけです。
ただし、慣れないうちはこのcsrftokenをcookieから取得する方法や、メッセージに含める方法が分からないため、POSTメッセージを通すために延々と時間を要する、という事態になりがちです(私がそうでした)。しかもPOSTをする方法もいろいろあり(Htmlのsubmitの場合や、JavaScriptの場合、Ajaxの場合、Pythonの場合、等々)、それらの方法もまちまちなので、この記事で整理しました。

HTMLでformを使う場合

この方法が一番簡単で、templateとなるhtmlファイルの中に、”{% csrf_token %}”というコードを追加しておけば、それだけで自動的にcsfrtokenが送られます。
https://docs.djangoproject.com/en/1.11/ref/csrf/

<form action="" method="post">{% csrf_token %}

JavaScriptでcreateElementを使う場合

この方法の場合、createElementで作ったformの中にcsrftokenを含ませる必要があります。まず、csrftokenは以下のような関数を作って取得できるようにしましょう。
https://docs.djangoproject.com/en/1.11/ref/csrf/

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

具体的には以下のようにすれば取得できます。

var csrftoken = getCookie('csrftoken');

次にcreatElemntでformを作り、TEXTのINPUTとしてNAMEが”csrfmiddlewaretoken”、VALUEがcsrftokenのElementを加え、formをSubmitすればOKです。
【JS】ボタンを押してSubmitする

function post_sample()
{
    var form = document.createElement("form");
    form.setAttribute("action", "sample.html"); // 投げたいURLを書く。
    form.setAttribute("method", "POST"); // POSTリクエストもしくはGETリクエストを書く。
    form.style.display = "none"; // 画面に表示しないことを指定する

    // csrtokenを設定する部分
    my_tb=document.createElement('INPUT');
    my_tb.type='TEXT';
    my_tb.name='csrfmiddlewaretoken';
    my_tb.value=[getCookie('csrftoken')];
    form.appendChild(my_tb);

    document.body.appendChild(form);
    form.submit();
}

Ajaxを使う場合

Ajaxを使う場合は初期設定のような形でcsrftokenを与えるようにしておけばいいようです。
https://docs.djangoproject.com/en/1.11/ref/csrf/

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

あとは普通にAjaxを使う手順でPOSTメッセージを作成すればOKです。
【ajax】通信エラーとなった原因(エラーログ)を取得する

function post_sample()
{
        var data = {
            "sample": "sample",
        };

        $.ajax({
            type: "POST",
            url: "sample.html",
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
            data: JSON.stringify(data),
            dataType:"html",
            
            success:function(res) {
                console.log("ajax通信に成功しました");
                console.log(res);    
            },
            error:function(XMLHttpRequest, textStatus, errorThrown){
                alert("失敗しました");
                console.log("ajax通信に失敗しました");
                console.log("XMLHttpRequest : " + XMLHttpRequest.status);
                console.log("textStatus     : " + textStatus);
                console.log("errorThrown    : " + errorThrown.message);    
            },
        });
}

Pythonのrequestsを使う場合

ブラウザを起動せずにPOSTメッセージを送りたい場合には、例えばPythonのrequestsモジュールを使う方法があります。

URL = "sample.html"
session = requests.session()
res = session.get(URL)
csrf = session.cookies['csrftoken']
 
data = {     
        "csrfmiddlewaretoken" : csrf,
        "sample" : "sample",
        }
 
response = session.post(URL, data=data, headers=dict(Referer=URL))

まとめ

csrftokenをcookieから取得して、POSTリクエストを通す方法を整理しました。分かってしまえば簡単・かつ使い回しが効くのですが、初めてだと数時間オーダで悩むネタだと思いますので、是非活用してみてください。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA