Django の CSRF 保護を利用する

CSRF 対策をあなたのビューで有効にするには、以下の手順に従ってください:

  1. CSRF ミドルウェアは、デフォルトで MIDDLEWARE 設定で有効になっています。もし設定をオーバーライドするときは、'django.middleware.csrf.CsrfViewMiddleware' が、 CSRF 攻撃への対策がされていることを前提とした他の全てのビュー・ミドルウェアの前に来るようにしてください。

    (推奨されませんが) もし対策を無効にする場合は、csrf_protect() を使って特定のビューを保護することができます (下記を参照してください)。

  2. POST フォームを使う全てのテンプレートで、POST が内部 URL に使われる場合は、<form> 要素の内部で csrf_token タグを使用してください。

    <form method="post">{% csrf_token %}
    

    この方法は、外部の URL を対象にする POST フォームで使ってはいけません。CSRF トークンが外部に漏れ、脆弱性の原因となります。

  3. 対応するビュー関数では、{% csrf_token %} が正しく機能するように、レスポンスをレンダリングする際に RequestContext が使用されていることを確認してください。 render() 関数、ジェネリックビュー、または contrib アプリを使用している場合は、これらはすべて RequestContext を使用しているため、すでに対応しています。

AJAXでCSRF保護を利用する

上記の方法は AJAX の POST でも利用可能ですが、多少不便です。すべての POST リクエストで、CSRF トークンを POST するデータに忘れずに含めなければなりません。そのため、別の方法が用意されており、各 XMLHttpRequest に対して、X-CSRFToken という独自ヘッダーに CSRF トークンの値を設定することができます (CSRF_HEADER_NAME 設定でヘッダー名が指定できます)。多くの JavaScript のフレームワークはすべてのリクエストについて、指定したヘッダーを設定するようなフック機能を提供しているので、普通は簡単に設定できます。

まず、CSRFトークンを取得する必要があります。これは、CSRF_USE_SESSIONSCSRF_COOKIE_HTTPONLY の設定が有効になっているかどうかによって異なります。

AJAX 通信でトークンを設定する

最後に、AJAXリクエストにヘッダーを追加する必要があります。 fetch() APIを使用してください:

const request = new Request(
    /* URL */,
    {
        method: 'POST',
        headers: {'X-CSRFToken': csrftoken},
        mode: 'same-origin' // Do not send CSRF token to another domain.
    }
);
fetch(request).then(function(response) {
    // ...
});

Jinja2テンプレートでCSRF保護を利用する

Django の Jinja2 テンプレートバックエンドは、すべてのテンプレートのコンテキストに {{ csrf_input }} を追加します。これは、Django テンプレート言語内の {% csrf_token %} と同じ意味です。例えば:

<form method="post">{{ csrf_input }}

デコレータメソッドの利用

全体を保護するために CsrfViewMiddleware を追加する代わりに、保護を必要とする特定のビューにおいて、まったく同じ機能を持つ csrf_protect() デコレータを使えます。アウトプット内に CSRF トークンを挿入するビューと、POST フォームデータを受け入れるビューの両方で使用する必要があります (多くの場合同じビュー関数ですが、そうでない場合もあります)。

デコレータ自体で使うことは 非推奨 です。もし使い忘れた場合、セキュリティホールを抱えることになるからです。二重対策として両方を使うのは構いませんが、わずかにオーバーヘッドが増加します。

リクエスト拒否に対処する

デフォルトでは、受信リクエストが CsrfViewMiddleware によって実行されるチェックに失敗した場合、ユーザーには "403 Forbidden" レスポンスが送信されます。これは通常、実際の CSRF が発生した場合、またはプログラミングエラーにより POST フォームに CSRF トークンが含まれていない場合にのみ送信されるべきです。

エラーページはとてもユーザーフレンドリーではないので、独自のビューを提供したいと考えるかもしれません。CSRF_FAILURE_VIEW の設定で実現できます。

CSRF の認証失敗は、 django.security.csrf ロガーに警告として記録されます。

キャッシュによるCSRF保護を利用する

csrf_token テンプレートタグがテンプレートで使用されている場合(または get_token 関数が他の方法で呼び出されている場合)、CsrfViewMiddleware はレスポンスにクッキーと Vary: Cookie ヘッダをレスポンスに追加します。これは、UpdateCacheMiddleware が指示通りに使用された場合、このミドルウェアがキャッシュミドルウェアと協調的に動作することを意味します( UpdateCacheMiddleware は他のすべてのミドルウェアよりも優先されます)。

しかし、個々のビューでキャッシュデコレータを使用すると、CSRF ミドルウェアはまだ Vary ヘッダや CSRF クッキーを設定できておらず、レスポンスはどちらも設定されずにキャッシュされます。この場合、CSRF トークンを挿入する必要があるビューでは、 django.views.decorators.csrf.csrf_protect() デコレータを最初に使うべきです:

from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import csrf_protect


@cache_page(60 * 15)
@csrf_protect
def my_view(request): ...

クラスベースのビューを使っている場合は、Decorating class-based views を参照してください。

テストと CSRF 保護

通常、 CsrfViewMiddleware はビュー関数をテストする際に大きな障害となります。なぜなら、 POST リクエストごとに CSRF トークンを送信する必要があるからです。このため、Django のテスト用 HTTP クライアントは、ミドルウェアと csrf_protect デコレータを緩和するフラグをリクエストに設定するように変更されました。それ以外の点 (例えばクッキーの送信など) では同じように動作します。

何らかの理由でテストクライアントに CSRF チェックを実行して ほしい ときには、CSRF チェックの実行を強制するテストクライアントのインスタンスを作ることができます:

>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)

エッジケース

ビューによっては、ここで想定している通常のパターンに当てはまらないような特殊な要求がある場合があります。このような状況では、多くのユーティリティが役に立ちます。それらが必要とされる可能性のあるシナリオについては、次のセクションで説明します。

特定のビューで CSRF 保護を無効化する

ほとんどのビューでCSRF保護が必要だが、わずかのビューでは必要でないケースがあります。

解決策: ミドルウェアを無効にして、必要なビュー全てに csrf_protect を適用するのではなく、ミドルウェアを有効にして csrf_exempt() を使ってください。

CsrfViewMiddleware.process_view() が使われない場合のトークン設定

ビューが実行される前に CsrfViewMiddleware.process_view が実行されない場合があります(例えば、404ハンドラーや500ハンドラー)その場合でもフォームでCSRFトークンが必要となる場合。

解決策: requires_csrf_token() を使用する

保護されていないビューにCSRFトークンを含める

csrf_exempt によって除外され、保護されていないビューがあるかもしれない中で、CSRFトークンを含めなければならないこともあります。

解決策: csrf_exempt() の後に続けて requires_csrf_token() を使用してください (つまり、 requires_csrf_token が最も内側のデコレータとなるようにしてください)。

1つのパスに対してのみビューを保護する

ビューが CSRF 保護を必要とするのは、ある条件のときだけで、それ以外のときは CSRF 保護をしてはいけないときがあります。

解決方法: ビュー関数全体には csrf_exempt() を使用し、その中で保護が必要なパスに対して csrf_protect() を使用します。例えば:

from django.views.decorators.csrf import csrf_exempt, csrf_protect


@csrf_exempt
def my_view(request):
    @csrf_protect
    def protected_path(request):
        do_something()

    if some_condition():
        return protected_path(request)
    else:
        do_something_else()

HTML フォームなしで AJAX を使用するページの保護

AJAX 経由で POST リクエストを行うページにおいて、必要な CSRF クッキーを送信する csrf_token を持つ HTML フォームがないことがあります。

解決策: ページを送信するビューで ensure_csrf_cookie() を使います。

再利用可能なアプリケーションにおける CSRF 保護

開発者は CsrfViewMiddleware を個別にオフにすることができるので、CSRF に対するアプリケーションのセキュリティを保証するために、contrib アプリケーションの関連するビューには、すべて csrf_protect デコレータを適用しています。その他の再利用可能なアプリの開発者も、同様にセキュリティを保証したいのであれば、ビューに csrf_protect デコレータを適用してください。

Back to Top