【Django】CSRF verification failed. Request aborted.エラーが出てきた場合の原因と解決方法 Forbidden (403)

# 概要
* djangoアプリを制作してローカル環境で開いたら、以下の403エラー画面が出てしまいました。
* 今回はこのエラーの原因と解決方法をまとめました。

```
Forbidden (403)
CSRF verification failed. Request aborted.

More information is available with DEBUG=True.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/45ab1ede-bc8f-bb9c-b538-a018c8a75c9f.png)


# 原因の追求: CSRFトークンとは？
* CSRF（Cross-Site Request Forgery）攻撃とは：
    * Webアプリケーションのセキュリティに関する問題の1つで、悪意のあるユーザーが被害者のアカウントを使用して操作を行うことを試みる攻撃手法のこと。「サイト横断的に（Cross Site）リクエストを偽装（Request Forgeries）する」攻撃。
    * この攻撃では、攻撃者は被害者を誘導して、攻撃者が用意したWebページを開き、そのページから悪意のあるリクエストを送信させる。
    * このような攻撃を防ぐために、WebアプリケーションはCSRFトークンを使用して、リクエストが正当なものであることを検証する必要がある、
    * Django公式サイト：[Cross Site Request Forgery protection](https://docs.djangoproject.com/en/dev/ref/csrf/)

* Django loggingファイルを確認すると、以下のように記載されています。やはりCSRF tokenがないとか正しくないとか。

```log
2023-04-13 11:22:34,747 | WARNING | django.security.csrf - Forbidden (CSRF token missing or incorrect.): /xxxxxxx/
```

* curlコマンドで叩くと以下のように返ってきます。

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>403 Forbidden</title>
  <style type="text/css">
    html * { padding:0; margin:0; }
    body * { padding:10px 20px; }
    body * * { padding:0; }
    body { font:small sans-serif; background:#eee; color:#000; }
    body>div { border-bottom:1px solid #ddd; }
    h1 { font-weight:normal; margin-bottom:.4em; }
    h1 span { font-size:60%; color:#666; font-weight:normal; }
    #info { background:#f6f6f6; }
    #info ul { margin: 0.5em 4em; }
    #info p, #summary p { padding-top:10px; }
    #summary { background: #ffc; }
    #explanation { background:#eee; border-bottom: 0px none; }
  </style>
</head>
<body>
<div id="summary">
  <h1>Forbidden <span>(403)</span></h1>
  <p>CSRF verification failed. Request aborted.</p>


  <p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
  <p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for “same-origin” requests.</p>

</div>

<div id="explanation">
  <p><small>More information is available with DEBUG=True.</small></p>
</div>

</body>
</html>
```

* `settings.py`で既に`DEBUG=True`としていました。
* 上記内容から、このエラーは、リクエストがCSRF保護されたフォームを送信する場合に、CSRFトークンが不足していることが原因で発生していることがわかります。
    * CSRFトークン：WebフォームやAjaxなどのHTTP POST要求の送信時に、フォームまたはHTTPヘッダに含まれるランダムなトークン

# 解決方法: フォームにCSRFトークンを設定
* グーグル先生に聞いたところ、Djangoではデフォルト設定でCSRF検証が有効化されているとのこと。
* そして、以下どちらの記事にも記載されている`{% csrf_token %}`なるものを発見。
    * [Django｜フォームをCSRF攻撃から守る方法|CSRF verification failed. Request aborted.](https://super-vitality.com/django-error-csrf-verification-failed-request-aborted/)
    * [Forbidden (403) CSRF verification failed. Request aborted.](https://jpn.itlibra.com/article?id=20990)
* どうやらPOST通信した際に発生するエラーで、テンプレート側のフォームにCSRFトークンを設定することで、このエラーを解消できるらしい。
* djangoでは`{% csrf_token %}`は、`<form>`タグ内のどこかに配置する必要があるらしい。
* `type="hidden"`属性が指定されているため、ページに表示はされません。
* ただし、`settings.py`に以下が記載されている前提です（自分は既に記載していました）。
    * 逆に、この登録を外すことで`CSRF`検証を無効化することができます。

```settings.py
MIDDLEWARE = [
    'django.middleware.csrf.CsrfViewMiddleware',
]
```

* 以下、`{% csrf_token %}`が今回追加したところです。

```html
##略
		<div class="col-md-12">
			<form method="POST" id="upload-form" enctype="multipart/form-data">
				{% csrf_token %} ##これを追加
				<div class="form-group">
					<label for="id_file">HTTP Request Header</label>
					{{ form.header }}
				</div>
				<div class="form-group">
					<label for="id_file">JSONファイル</label>
					{{ form.file }}
				</div>
				<button type="submit" class="btn btn-primary">Submit</button>
			</form>
		</div>
##略
```

* サイトを立ち上げてみると、、、エラーページが表示されることがなくなりました。解決！
* DjangoのCSRFトークンは、Webアプリケーションのセキュリティを向上させるために重要な機能。適切に使用される場合、ユーザーのデータが不正な要求から保護され、Webアプリケーションのセキュリティが向上しますので、必ず設定した方が良いでしょう。
* 参考記事：
    * [【Django】 csrf_tokenの仕組みとCSRF無効化・画面カスタマイズする方法](https://djangobrothers.com/blogs/django_csrf/)
    * [【Python/Django】CSRF対策について解説](https://daeudaeu.com/django-csrf/)