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