本文へスキップ
BecomeCoder

Djangoコース · 第4章 フォームと入力 · レッスン16

POSTとCSRF ― 送信を安全に受ける

ブラウザで完結

導入

投稿や登録は POST で送ります。ただし POST には「なりすまし送信(CSRF)」という攻撃があり、Django は既定でそれを防いでいます。仕組みを理解しておきましょう。

説明

CSRF(クロスサイト・リクエスト・フォージェリ) は、悪意あるサイトがユーザーになりすまして、あなたのサイトへ勝手に POST を送る攻撃です。Django はこれを防ぐため、POST フォームに秘密のトークン埋め込み、送信時に照合します。テンプレートでは {% csrf_token %} を書くだけです。

<form method="post">
  {% csrf_token %}   <!-- これが無いと本番では 403 で拒否される -->
  <input name="title">
  <button>投稿</button>
</form>

ビュー側は、GET(フォーム表示)と POST(受け取り)を分けて書きます。

import django
from django.conf import settings
if not settings.configured:
    settings.configure(DEBUG=True, SECRET_KEY="dev",
        ROOT_URLCONF=__name__, ALLOWED_HOSTS=["testserver"])
    django.setup()

from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import path

posts = []

def create(request):
    if request.method == "POST":
        title = request.POST.get("title", "").strip()
        if title:
            posts.append(title)
        return redirect("/")          # 保存後は一覧へリダイレクト(PRGパターン)
    return HttpResponse("投稿一覧: " + " / ".join(posts))

urlpatterns = [path("", create)]

from django.test import Client
client = Client()
client.post("/", {"title": "はじめての投稿"})
client.post("/", {"title": "2件目"})
print(client.get("/").content.decode())   # 投稿一覧: はじめての投稿 / 2件目
  • request.POST.get("title") … 送信されたフォームの値を読む。
  • 保存後に redirect("/") するのは PRG(Post/Redirect/Get) という定番。これでブラウザの「更新」による二重投稿を防げます。
  • このコースのテストクライアントは学習のため CSRF チェックを省いています。本物のフォームでは、上記の {% csrf_token %} を必ず入れてください。入れないと Django は 403(拒否)を返します。

やってみよう

client.post("/", {"title": "..."}) を何度か呼んでから client.get("/") を見て、投稿が貯まることを確かめましょう。title を空文字にすると if title: により保存されないことも確認できます。

演習

空白だけの投稿(例 " ")が保存されないことを利用しています。create を改造して、保存に成功したときだけ末尾に何件貯まったかを含めた文字列を返す代わりに、/ の一覧表示を 件数: N 件(内容: ...) の形式に変えてください。print(client.get("/").content.decode())件数: が含まれれば成功です。

ヒント1を見る

GET 側の return HttpResponse(...)f"件数: {len(posts)} 件(内容: {' / '.join(posts)})" にします。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

Python — ライブラリ付きで実行(Pyodide)

numpy / pandas / matplotlib が使える本物のPython(Pyodide)を読み込みます。初回のみ読み込みに少し時間がかかります(以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。