JavaScriptを有効にしてください

【Python】DjangoのCSRFエラー「403 Forbidden」解決方法

 ·  ☕ 5 分で読めます

【Python】DjangoのCSRFエラー「403 Forbidden」解決方法

DjangoでフォームやAPIリクエストを送信したとき、403 Forbidden - CSRF verification failed. Request aborted. というエラーに遭遇したことはありませんか?
これはDjangoのCSRF(Cross Site Request Forgery)保護機構によって、セキュリティ上の問題が検出されたときに表示されるエラーです。

この記事では、CSRFエラーの意味とその対処方法を、わかりやすくチェックリスト形式で対処します。


Djangoで「CSRF verification failed」が出る理由とは?

Djangoには、悪意あるクロスサイト攻撃を防ぐための「CSRF保護機構」が標準搭載されています。
ユーザーの意図しないPOSTリクエストによってデータ改ざんや情報漏洩が起こらないよう、CSRFトークンという仕組みを使ってリクエストの正当性を確認しています。

このチェックに失敗すると、403 Forbiddenエラーが返されます。
開発中に頻出するため、正しい原因を突き止めて適切に対応することが重要です。


エラーメッセージの読み解き方

DjangoでDEBUG = Trueの設定時、以下のような詳細なエラー画面が表示されます。

Forbidden (403)

CSRF verification failed. Request aborted.

Reason given for failure:

    Origin checking failed - https://example.com does not match any trusted origins.

この例では、リクエストのOriginが信頼されていないことが原因でブロックされています。

DEBUG = Falseの場合は、簡素なエラー画面のみ表示され、詳細情報は省略されます。
本番環境ではセキュリティ上の理由から、詳細な情報は非表示になる仕様です。


原因と解決策チェックリスト

CSRFトークンがフォームに含まれていない

テンプレート内でPOSTフォームを作る際は、必ず{% csrf_token %}をフォームタグの中に含めましょう。

1
2
3
4
<form method="post">
  {% csrf_token %}
  <!-- フォームの内容 -->
</form>

トークンが埋め込まれていないと、リクエストはすべて拒否されます。


ブラウザがCookieをブロックしている

CSRFトークンはDjangoがCookieと連動して管理しています。
ブラウザの設定や拡張機能がCookieをブロックしている場合、エラーが発生します。

とくにSafariやChromeのプライバシー設定は要注意です。開発中はCookieの保存が有効になっているか確認しましょう。


JavaScriptによる非同期POSTでトークンが付与されていない

JavaScriptからAJAXやfetch APIでPOSTリクエストを送信する場合、ヘッダーにCSRFトークンを明示的に含める必要があります。

例(JavaScript):

1
2
3
4
5
6
7
8
fetch("/api/update/", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-CSRFToken": getCookie("csrftoken")
  },
  body: JSON.stringify({ data: "..." })
});

Django公式ドキュメントにもあるように、csrftokenはクッキーから取得し、X-CSRFTokenヘッダーにセットします。


ALLOWED_HOSTS または CSRF_TRUSTED_ORIGINS の不備

Django 4.0以降では、クロスオリジンからのPOSTリクエストを許可するために CSRF_TRUSTED_ORIGINS を正しく設定する必要があります。

例(settings.py):

1
2
3
CSRF_TRUSTED_ORIGINS = [
    "https://example.com",
]

また、ALLOWED_HOSTS に対象ドメインを追加することも忘れずに。


HTTPSとHTTPが混在している

HTTPSで配信されているページから、HTTPで送信されたリクエストは「安全でない」と判断されてブロックされます。
とくにフロントエンドとAPIが別ドメイン・別プロトコルで動いている場合に起こりやすいです。

→ 全体をHTTPSに統一するのが基本です。


セッションが切れてトークンが無効になった

ログイン後に時間が経過してからPOST操作をすると、セッショントークンの期限切れでエラーが発生することがあります。

→ ページをリロードしてトークンを更新することで解消できます。


よくあるケース別の対処法

ログイン後にエラーが出る

ログイン処理後はCSRFトークンが再生成されるため、ブラウザの「戻る」ボタンでフォームを開いた場合、トークンが古い状態になります。

→ ログイン後は必ずリダイレクトさせ、新しいトークンを生成させるのが安全です。


フロントエンド(React/Vueなど)からAPIを叩いたとき

SPA構成では、CSRFトークンの取得・保持をJavaScript側で行う必要があります。

→ Djangoテンプレートからトークンを埋め込む、あるいは /api/csrf/ のような専用エンドポイントを作って取得させましょう。


管理画面にログインできないとき

管理画面ログインでCSRFエラーが出る場合、ブラウザのCookie設定やプロキシ構成が影響していることがあります。

→ Cookie制限やX-Forwarded-Protoヘッダーを見直してください。


セキュリティを損なわない対処のポイント

CSRFチェックを無効化するのは推奨されません
開発中にどうしてもチェックを外す必要がある場合は、以下のようにビュー関数単位で一時的に無効化します。

1
2
3
4
5
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    ...

しかし、本番環境ではCSRF保護を常に有効にし、安全な実装を徹底しましょう。


まとめ:CSRFエラーは「疑ってチェック」から始めよう

CSRFエラーはDjangoが安全に動作している証拠でもあります。
焦らず以下のチェックリストを辿って確認しましょう。

  • フォームに {% csrf_token %} を含めたか
  • Cookieがブロックされていないか
  • JavaScript経由のリクエストにトークンを付与したか
  • CSRF_TRUSTED_ORIGINS に正しいドメインを設定したか
  • HTTPとHTTPSの混在がないか
  • セッションやログイン直後の動作に問題がないか

これらを確認するだけで、大半の403エラーは解決できます。


📎参考リンク

共有

こぴぺたん
著者
こぴぺたん
Copy & Paste Engineer