Django の csrf_token について
Django の csrf_token について、Twitter のフォロワーさんが困ってました。
以前同じように困った事があるから助け舟を出したのですが、
気になってググってみると日本語情報があまりないような?
なので blog にまとめておきます。
ちなみに、公式ドキュメントはこちら
Cross Site Request Forgery protection | Django documentation | Django
csrf_token って?
クロスサイトリクエストフォージェリ(CSRF) を防ぐためのモノです。
Django 1.2 から追加されました。
詳細については Ian さんトコで。
Django 1.2 の変更のまとめ - Ian Lewis
何に困るの?
form で method="POST" 使うと以下のエラーページが表示される。
Forbidden (403) CSRF verification failed. Request aborted.
原因
settings.py で CsrfViewMiddleware を追加している(コメントアウトを外している)のに
csrf_token が form から渡されてきていないため。
settings.py (抜粋)
MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware',
)
対処法
HTML 側は Form に csft_token csrf_token を含める。
index.html
<form method="POST" action="{% url search %}" name="SEARCH"> {% csrf_token %} <input type="text" name="keyword" /> <input type="submit" value="検索" /> </form>
view 側は RequestContext 使う。
Context だと宣言した分のパラメータしか渡してくれないため、
Django が作った CSRF トークンが渡せない。
views.py
from django.template import RequestContext from django.shortcuts import render_to_response def root(request): """ index """ ctxt = RequestContext(request, {}) return render_to_response("index.html", ctxt) def search(request): """ search """ if "keyword" in request.POST: keyword = request.POST["keyword"] else: keyword = "" ctxt = RequestContext(request, { "keyword": keyword }) return render_to_response("search.html", ctxt)
そんな感じ。
余談
django.middleware.csrf.CsrfViewMiddleware を使わない場合。
特定の箇所のみ CSRF 対応したい時にでも?
ただし、必要なのに忘れた場合セキュリティホールになるので注意
HTML 側は {% csrf_token %} 必須。
index.html
<form method="POST" action="{% url search %}" name="SEARCH"> {% csrf_token %} <input type="text" name="keyword" /> <input type="submit" value="検索" /> </form>
view 側は 表示(root) と 取得(search) 両方に @csrf_protect が必要。
views.py
# -*- coding: utf-8 -*- from django.template import RequestContext from django.shortcuts import render_to_response from django.views.decorators.csrf import csrf_protect @csrf_protect def root(request): """ index """ ctxt = RequestContext(request, {}) return render_to_response("index.html", ctxt) @csrf_protect def search(request): if "keyword" in request.POST: keyword = request.POST["keyword"] else: keyword = "" ctxt = RequestContext(request, { "keyword": keyword }) return render_to_response("search.html", ctxt)
あとでソースまとめて UP するかな…。