Postmanを使用してログイン機能をテスト中に「CSRF Failed: Referer checking failed - no Referer」というエラーが発生しました。このエラーの原因を特定するため、Djangoのソースコードを調査しました。
問題の原因は明らかにCSRF検証に関連しているため、CsrfViewMiddlewareミドルウェアを確認しました:
1 class CsrfValidationMiddleware(object):
2
3 def _allow_request(self, request):
4 request.csrf_verified = True
5 return None
6
7 def _block_request(self, request, cause):
8 logger.warning('アクセス禁止 (%s): %s', cause, request.path,
9 extra={
10 'status_code': 403,
11 'request': request,
12 }
13 )
14 return _get_error_view()(request, reason=cause)
15
16 def validate_request(self, request, view_func, view_args, view_kwargs):
17 if getattr(request, 'csrf_verified', False):
18 return None
19
20 try:
21 security_token = _clean_token(
22 request.COOKIES[settings.CSRF_COOKIE])
23 request.META['CSRF_TOKEN'] = security_token
24 except KeyError:
25 security_token = None
26 request.META["CSRF_TOKEN"] = _generate_csrf_token()
27 return None
28
29 if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
30 if getattr(request, 'skip_csrf_validation', False):
31 return self._allow_request(request)
32
33 if request.is_secure():
34 referrer_url = safe_text(
35 request.META.get('HTTP_REFERER'),
36 strings_only=True,
37 errors='replace'
38 )
39 if referrer_url is None:
40 return self._block_request(request, ERROR_NO_REFERER) # ここでエラー発生
41
42 valid_referrer = 'https://%s/' % request.get_host()
43 if not validate_origin(referrer_url, valid_referrer):
44 cause = ERROR_INVALID_REFERER % (referrer_url, valid_referrer)
45 return self._block_request(request, cause)
46
47 if security_token is None:
48 return self._block_request(request, ERROR_MISSING_CSRF_COOKIE)
49
50 client_token = ""
51 if request.method == "POST":
52 try:
53 client_token = request.POST.get('csrf_token', '')
54 except IOError:
55 pass
56
57 if client_token == "":
58 client_token = request.META.get('HTTP_X_CSRF_TOKEN', '')
59
60 if not secure_compare(client_token, security_token):
61 return self._block_request(request, ERROR_INVALID_TOKEN)
62
63 return self._allow_request(request)
まず、エラーメッセージをソースコード内で検索すると、以下の定数が見つかりました:
ERROR_NO_REFERER = "Referer checking failed - no Referer."
この定数を追跡すると、上記コードの40行目がエラー発生箇所であることがわかります。
コードを詳しく見ると、referrer_url = request.META.get('HTTP_REFERER') の部分で、リクエストヘッダーからREFERER情報を取得しようとしています。Djangoはカスタムヘッダーを大文字に変換し、先頭に「HTTP_」を付加するため、Postmanのヘッダー設定で「referer」フィールドを追加する必要があります。
注意点として、refererの値は「https://www.example.com/」のように、ドメイン名の後にスラッシュを含める必要があります。
この設定を行うことで、問題は解決します。
補足として、DjangoのCSRF対策は非常に柔軟です。POSTリクエストの場合、54行目から61行目にあるように、リクエストボディ内のcsrf_tokenだけでなく、HTTPヘッダーからもトークンを取得しようとします。これにより、開発者は2つの異なる方法でCSRFトークンを提供できます。