revision-up-to: | 17812 (1.4) |
---|
CSRF ミドルウェアとテンプレートタグは、簡単に使える クロスサイトリクエストフォージェリ (Cross Site Request Forgeries) 対策を提供しています。このタイプの攻撃は、悪意のあるウェブサイトを訪れたユーザー のログイン済みの権限で、あなたのサイトに対して何らかの操作を行うことを目的とした リンクやフォームボタン、 JavaScript を設置したウェブサイトによって行われます。 また、関連する攻撃として、ユーザーを騙して別のユーザー権限でログインさせる ログイン CSRF と呼ばれる攻撃もありますが、これも含まれます。
CSRF 攻撃に対する第一の防御は、 GET (と、9.1.1 Safe Methods,HTTP 1.1, RFC 2616#section-9.1.1 で定義された ‘安全な’ メソッド) リクエストから副作用を 取り除くというものです。そして、 POST, PUT, DELETE のような、’安全でない’ メソッ ドによるリクエストについては、下記の手順に従うことで対策することができます。
CSRF 対策をあなたのビューで有効にするには、以下の手順に従ってください。:
'django.middleware.csrf.CsrfViewMiddleware'
ミドルウェアを
MIDDLEWARE_CLASSES
に追加してください。 (このミドルウェアは、
CSRF 対策が為されていることを前提として動作するどのミドルウェアよりも前に
追加します。)
また、代わりに 対策したい特定のビューに対して、
csrf_protect()
デコレータを使用することも
できます (下記参照)
自身のサイト内で POST リクエストを送るすべてのフォームの <form>
タグ内
で、 csrf_token
テンプレートタグを使用します。例:
<form action="." method="post">{% csrf_token %}
外部のサイトに対してリクエストを送るフォームについては使用すべきではあり ません。 CSRF トークンが流出し、脆弱性を生むからです。
対応するビューの内部で、 'django.core.context_processors.csrf'
コンテキストプロセッサーを使用出来るようにします。通常、2つの方法のうち、
どちらかの方法を選択します。:
'django.core.context_processors.csrf'
を常に使用します。もし、ジェ
ネリックビューや Django 付属のアプリを使用している場合は、あなたは既に
この方法を使用しています。なぜなら、これらのアプリは常に RequestContext
を使用しているからです。手動でインポートし、プロセッサーを使って CSRF トークンを生成して、 テンプレートのコンテキストに追加する。例:
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def my_view(request):
c = {}
c.update(csrf(request))
# ... view code here
return render_to_response("a_template.html", c)
このような処理を行う、 render_to_response()
のラッパー関数を作成しても良いかもしれません。
補助スクリプトの extras/csrf_migration_helper.py
を使えば、これらの手順を
行わなければならないテンプレートやコードを自動的に見つけてくれます。
また、どうやって使えばよいかのヘルプもすべて用意されています。
上記の手順を AJAX を用いた POST リクエストで行うのは、少し不便です。あなたは、
すべての POST リクエストについて、 CSRF トークンを POST するデータに含めることを
覚えておかなければなりません。なので、別の方法が用意されています。それは、各
XMLHttpRequest に対して、 X-CSRFToken という独自ヘッダーに CSRF トークンの
値を設定することです。多くの JavaScript のフレームワークはすべてのリクエストに
ついて、ヘッダーを設定するようなフック機能を提供しているので、この操作は多くの
場合、簡単に行うことができます。 jQuery の場合、 ajaxSend
イベントを以下の
ように記述します:
jQuery(document).ajaxSend(function(event, xhr, settings) {
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;
}
function sameOrigin(url) {
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function safeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
});
Note
jQuery 1.5 ではバグがあるため、上記の例は正しく動作しません。使用している jQuery のバージョンが 1.5.1 以上であることを確認してください。
これを サイトで使用される JavaScript のファイルに追加すれば、 jQuery 経由で 送信された AJAX POST リクエストは、CSRF 対策に引っかかることはなくなります。
上記のコードは、 jQuery cookie plugin を用いて、 getCookie
を置き換え
、jQuery 1.5 以降で追加された settings.crossDomain を用いて sameOrigin
を置き換えることに
よって、さらに簡略化できます。
加えて、 csrf_token
を使用して、クライアントに CSRF クッキーを送って
いなかった場合、ユーザーがクッキーを受け取っていることを
ensure_csrf_cookie()
を用いて、保証する
必要があるかもしれません。
Django の組み込みではないテンプレートエンジンを使用する場合、テンプレートの コンテキストからトークンが取得できることを確認した上で、フォームに手動で追加して ください。
例えば、 Cheetah テンプレート言語を使用する場合、フォームは以下のコードを含む でしょう。:
<div style="display:none">
<input type="hidden" name="csrfmiddlewaretoken" value="$csrf_token"/>
</div>
JavaScript に関しては、 取得した CSRF トークンの値を使用することによって、上記と 同じ方法で使用することができます。
CsrfViewMiddlewre
を追加して、サイト全体で対策する代わりに、まったく同じ機能
を保護が必要な特定のビューのみに持たせたいという場合には、 csrf_protect
デコレータを使用することができます。ただし、 CSRF トークンを出力に埋め込むビュー
と、フォームから POST されたデータを受け取るビュー (これらは、同じビューの
時も多いですが、違うビューの時もあります) の 両方で 使用しなけれなりません。
デコレータのみを使用することは 推奨されません 。なぜなら、もし、あなたが デコレータをつけ忘れてしまった場合、それがセキュリティホールを生むことになる からです。’念には念を (belt and blaces)’ の原則から両方を使用することは良いこと ですし、使用してもわずかなオーバーヘッドにしかならないでしょう。
csrf_protect
(view)¶CsrfViewMiddleware
の CSRF 対策機能をビューに付加するデコレータ
使い方:
from django.views.decorators.csrf import csrf_protect
from django.shortcuts import render
@csrf_protect
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
受け取ったリクエストが CsrfViewMiddleware
によって行われる認証に失敗した場合
、デフォルトでは、ユーザに ‘403 Forbidden’ が送信されます。これは普通、本当に
CSRF が行われたか、プログラマのミスによって、 POST フォームに CSRF トークンを
含め忘れたかでない限りは起こりません。
けれども、エラーページがあまり親切でないと感じるのならば、このような場面を処理
するために、自作のビューを設定することができます。 そうするには、単に settings
の CSRF_FAILURE_VIEW
を設定してください。
CSRF 対策は以下の要素からなります :
ランダムな値 (いわゆる、独立一時セッション (session independent nonce) ) が設定されており、外部のサイトから参照できない CSRF クッキー。
このクッキーは CsrfViewMiddleware
によって設定されます。これは、クッキー
がずっと保持されることを前提としていますが、絶対に破棄されないクッキーを作成
することは不可能なので、 django.middleware.csrf.get_token()
(この関数
は、内部的に CSRF トークンを取り出すために使用されています。) が呼び出された
すべてのレスポンスについて、 CSRF クッキーが送信されます。
すべての POST リクエストを送信するフォームに含まれる、 ‘csrfmiddlewaretoken’ という名前を持つ hidden フィールド。この値フィールドの値には、 CSRF クッキー の値が使われます。
この処理は、テンプレートタグによって行われます。
HTTP GET, HEAD, OPTIONS, TRACE メソッドを除く、すべてのメソッドについて、 CSRF クッキーと ‘csrfmiddlewaretoken’ フィールドを保持しており、なおかつ 正しい値を保持しているリクエスト。もし、そうでないならば、ユーザーは 403 エラーを受け取ることになります。
この確認は、 CsrfViewMiddleware
によって行われます。
CsrfViewMiddleware
によって厳密にチェックされます。これは、 独立一時
セッションを用いた HTTPS 通信における中間者攻撃(Man-In-The-Middle atack)
に対応する必要があるからです。(不幸にも) HTTPS を使用しているサイトにおい
ても、 HTTP の’Set-Cookie’ ヘッダーが認められてしまうという事実によります。
(HTTP では、リファラヘッダの内容は十分に信頼出来ないため、リファラのチェック
は行われません。)これらの処理により、あなたのウェブサイト由来のフォームだけが POST を送り返せる ことを保証できます。
ここでは、故意に GET リクエスト (と、その他 ‘安全’ と RFC 2616 によって定義 されたリクエスト) を無視しています。 これらのリクエストは危険な副作用を持た ないはずなので、 GET リクエストを使った CSRF 攻撃は威力を持たないのです。 RFC 2616 では、POST, PUT, DELETE が ‘安全でない’ ものとして定義されており、 その他のメソッドについては、安全でないものとみなして、最大限の対策を行なって います。
csrf_token
がテンプレート内で使用されている (もしくは、 get_token
関数が何らかの別の方法で呼び出されていた) 場合、 CsrfViewMiddleware
は、
クッキーと Vary: Cookie
ヘッダをレスポンスに付加するでしょう。これは、この
ミドルウェアがキャッシュの扱い方を教えれば、キャッシュを扱うミドルウェアがうまく
処理してくれることを期待することを意味しています (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):
# ...
CsrfViewMiddleware
は、すべての POST リクエストについて CSRF トークンを
必要とするため、普通、ビュー関数のテストの大きな障害になるでしょう。そのため、
Django のテスト用の HTTP クライアントは、 このミドルウェアと csrf_protect
デコレータの制限を緩めるフラグを各リクエストに付加するので、これらがリクエストを
拒否することはなくなります。その他のすべての処理 (例えば、 クッキーの送信など)
は、通常と同様に働きます。
もし、何らかの理由で CSRF のチェックを 有効にしたい と考えるのであれば、 CSRF のチェックを有効にしたテストクライアントを作成することができます。:
>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)
サイト内のサブドメインは、ドメイン全体に対してクッキーを設定することができる でしょう。クッキーをセットしたり、対応するトークンを使用することによって、サブ ドメインからは CSRF 対策を巧みにすり抜けることができるでしょう。この問題を回避 するための唯一の手段は、サブドメインを信用できるユーザーによって管理させる(もし くは、最低でもクッキーを設定させないようにする) ことです。特筆すべき点は、 信用出来ない人間にサブドメインを与えることは、 CSRF 以外にも、セッションフィク セーション (session fixation) のような他の脆弱性も生むため、良い案ではありま せんし、この脆弱性は現在一般に使用されているブラウザにおいて簡単に修正できるも のではないことです。
ある特定のビューでは、ここで紹介した通常のパターンに当てはまらないような特殊な 使い方が必要になることがあるでしょう。これらの状況では、いくつかのユーティリティ が役に立つでしょう。これらが必要になると思われるシナリオは、以下の節に書かれて います。
csrf_exempt
(view)¶このデコレータは、CSRF 対策のチェックが無効化されるビューを表します。例:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
requires_csrf_token
(view)¶通常、 csrf_token
テンプレートタグは
CsrfViewMiddleware.process_view
や、それに準ずる csrf_protect
の
ようなものが実行されない限りは、動作しません。ビューに対するデコレータの
requires_csrf_token
は、このテンプレートタグが動作することを保証する
ために使用されます。このデコレータは csrf_protect
に似ていますが、到達
したリクエストを拒否することは決してありません。
例:
from django.views.decorators.csrf import requires_csrf_token
from django.shortcuts import render
@requires_csrf_token
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
リリースノートを参照してください
このデコレータはビューに対して、強制的に CSRF クッキーを送出させます。
ほとんどのビューでは CSRF 対策が必要としますが、いくつかだけはそうでない場合 です。
解決策: ミドルウェアを無効化し、必要とするビューすべてに対して csrf_protect
を付加するよりも、ミドルウェアを有効にした上で
csrf_exempt()
を使用する方が良いでしょう。
ビューが実行される前に、CsrfViewMiddleware.process_view
が実行されていない
かもしれない場合があります。 - 例としては、404 や 500 のエラーハンドラーです。 -
そのような状況でも CSRF トークンがフォーム内に必要になる場合です。
解決策: requires_csrf_token()
を使用して
ください。
いくつかのビューでは csrf_exempt
によって、対策が解除され、無効化されている
かもしれません。そのような状況でも、 CSRF トークンが必要になる場合です。
解決策: csrf_exempt()
を
requires_csrf_token()
の後に使用してくだ
さい。 (すなわち、 requires_csrf_token
が最も内側のデコレータになるという
ことです)
あるビューにおいて、特定の状態でのみ 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()
あるページにおいて、 POST リクエストを AJAX 経由で送信し、なおかつ、ページが
CSRF クッキーが送出されるために必要な csrf_token
を含む HTML 上のフォ
ームを持たない場合です。
解決策: そのページを生成するビューに対して、
ensure_csrf_cookie()
を使用してください。
開発者は CsrfViewMiddleware
を無効化することができるので、組み込みアプリ
のうち、 CSRF に対する安全性を保証する必要があるすべてのアプリには
csrf_protect
デコレータが使用されています。他の場面で再利用されるアプリを
作成する開発者は、これと同じ保証が必要なビューに対しては csrf_protect
デコ
レータを使用することが推奨されています。
Django の CSRF の動作を調整するためのいくつかの設定があります。
リリースノートを参照してください
デフォルト値: None
CSRF クッキーを設定する際に使用されるドメインです。これを利用すれば、サブドメ
イン間のリクエストを通常の CSRF 対策から除外することを手軽に行うために便利です。
この項目には、サブドメイン上のフォームから送信されたリクエストのうち、ビューが
受け取ることを認めるサブドメインについて ".lawrence.com"
のような文字列で
設定します。
ただし、この設定を使っても使わなくても、この CSRF 対策のメカニズムはサブドメ イン間の攻撃に対しては安全ではないことに注意してください。 — 詳しくは、 制限 を参照してください。
リリースノートを参照してください
デフォルト値: 'csrftoken'
CSRF 認証トークンとして使用されるクッキーの名前です。この値には、好きな値を 自由に設定できます。
リリースノートを参照してください
デフォルト値: '/'
CSRF クッキーに設定されるパスです。この値はあなたの Django が設置されている URL のパスとその親のパスにマッチするはずです。
これは、同じホスト名でいくつもの Django のインスタンスを起動する場合に便利です。 それぞれが、別のクッキーのパスを使用することができ、それぞれのインスタンスは自分 の CSRF クッキーのみを参照します。
リリースノートを参照してください
デフォルト値: False
CSRF クッキーにセキュアクッキー (secure cookie) を使用するかどうかを設定します。
True
を設定すると、クッキーは “secure” に設定されます。これは、ブラウザが
HTTPS 接続でのみ、このクッキーを送信することを保証するであろうことを意味します。
リリースノートを参照してください
デフォルト値: 'django.views.csrf.csrf_failure'
到達したリクエストが CSRF 対策によって拒否された場合に使用されるビュー関数への ドット区切りのパスです。 ビュー関数は以下の引数を取ります:
def csrf_failure(request, reason="")
reason
は、リクエストが拒否された理由を表す短いメッセージです。 (開発者や
ログをとるのためのもので、一般のユーザーのためのものではありません)
Oct 26, 2017