概要
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リクエストを通す方法を整理しました。分かってしまえば簡単・かつ使い回しが効くのですが、初めてだと数時間オーダで悩むネタだと思いますので、是非活用してみてください。