自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

【Django】Django4.0以上はsettings.pyにて、CSRF_TRUSTED_ORIGINSにオリジンを指定しないとPOSTリクエスト時に403Forbiddenになる

thumbnail

背景

下記記事に倣って、EC2に独自ドメインを指定して、デプロイを完了させた。

【AWS】EC2にムームドメインで取得した独自ドメインを割り当て、HTTPS通信を行う【Route 53 + Certificate Manager + ロードバランサ(ELB)】

その後、POSTメソッドのリクエストを送信する際、どう頑張ってもCSRF検証に失敗したというエラーが出てしまう。

CSRF Failed: Referer checking failed - https://*********************.com does not match any trusted origins.

これはなぜか。どう対策をすれば良いのかをまとめてみた。

原因

【事前知識】オリジンとは

まず、オリジンというものを理解する必要がある。知っているという方は次の項へ

オリジンとは、httpもしくはhttpsなどのプロトコル、noauto-nolife.comなどのドメイン、8000などのポート番号までの組み合わせのことを言う。

例えば、以下のURLの場合

https://noauto-nolife.com/post/ec2-origin-domain-https/

オリジンはこうなる。(ウェルノウンポート(HTTPSの場合は443番)を使用している場合はポート番号は省略可)

https://noauto-nolife.com

このURLであれば

http://127.0.0.1:8000/single/1

オリジンはこう。

http://127.0.0.1:8000

【原因の解説】ElasticIPによってインスタンスに割り当てられたIPアドレスと、独自ドメインの違いにある

まず、下記記事の仕組みをご覧いただきたい。

【AWS】EC2にムームドメインで取得した独自ドメインを割り当て、HTTPS通信を行う【Route 53 + Certificate Manager + ロードバランサ(ELB)】

このインスタンスにはElasticIPによって割り当てられたIPアドレスで動作している

一方で、クライアント側からは独自ドメインでアクセスしている。Route53によって独自ドメインはロードバランサのIPアドレスに逆引きされ、ロードバランサはインスタンスにリクエストを与えている。

すると、クライアント側から見ると独自ドメイン。インスタンス側から見るとElasticIPによって割り当てられたIPアドレスで動作している。

この場合、クライアントがPOSTリクエストを送信する時、リクエストのヘッダーには独自ドメインが指定されている。

一方で、インスタンス側はElasticIPしか割り当てられていないので、リクエストヘッダーに書かれた独自ドメインを自分の物と認識する事ができない。

結果的にDjangoはよそのサイトからPOSTリクエストが送信されたとみなし、CSRFトークンが一致していたとしても、これをCSRF攻撃と誤認。403Forbiddenになってしまう。

解決策

settings.pyにて、CSRF_TRUSTED_ORIGINSを追加する。

ALLOWED_HOSTS           = [ "noauto-nolife.com" ]

CSRF_TRUSTED_ORIGINS    = [ "https://noauto-nolife.com" ]

ALLOWED_HOSTSは独自ドメイン(ホスト名)のみを記述し、CSRF_TRUSTED_ORIGINSはhttpsからドメイン名、ポート番号(今回は443なので記述の必要はない)もセットしたオリジンを追加しておく。

これでCSRF問題は改善される。

結論

Django4.0からの仕様変更であり、それ以前のバージョンであれば、この問題は発生しない点に注意。

つまり、4.0からはドメイン単位ではなく、より厳しいオリジン単位で判定する。

だから旧バージョンのDjangoでは、プロトコルやポート番号が正規のものではない場合、CSRF攻撃が成立してしまう問題があると思われる。

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ