\ ポイント最大11倍! /

【Django】クリックで即時ログアウトするボタンを作る方法

  • 確認画面なしでログアウトさせたい!

このような疑問にお答えします。

先日ナビゲーションバーを作っているときに、ログアウトボタンをクリックしたら即時ログアウトできるようにしたいと考えました。

ところが、通常の LogoutView では一度「ログアウトするかどうかの確認画面」が表示されてます。

今回はこれを不要にする方法です。

Django 5.0 からは LogoutView に GET メソッドでリクエストを送るとエラーになります。本記事ではこの対策もお伝えします。

クリックで即時ログアウトさせるボタンを作成

以下の流れで実装を進めます。

  • urls.py でルーティングを行う
  • テンプレートにリンクを設置

順番に解説していきます。

urls.py でルーティングを行う

ログアウト用のルーティングを行います。

from django.contrib.auth.views import LogoutView

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path("logout/", LogoutView.as_view(next_page=reverse_lazy("index")), name="logout"),
]

自動的にリダイレクトをさせるため、next_page=reverse_lazy()as_view()に渡しました。

ちなみにreverse_lazy()の引数に渡したindexは4行目のビューを指します。(name="index"のところを指定している)

テンプレートにリンクを設置

テンプレート HTML には、以下のように書きます。

<form action="{% url 'logout' %}" method="post">
    {% csrf_token %}
    <input type="submit" value="ログアウト">
</form>

Django 5.0以降のバージョンではLogoutViewに対してPOSTメソッドだけしか受け付けなくなってしまいました。

そのため、上記のようにformタグで実行する必要があります。

例えば以下のようにaタグでリンクを作るとGETメソッドでリクエストが送られてしまうので、エラーが出てしまいます。

<a href="{% url 'logout' %}">ログアウト</a>

LogoutView で GET メソッドが廃止された理由

Django 5.0 からはログアウトビューの GET メソッドが完全に廃止され、POST メソッドを使う必要があります。

この点について、少し詳しく解説します。

廃止された理由

Django のチケット 7989 によると、廃止の理由は「HTTP 1.1 の仕様に基づくもの」です。

つまり「GETおよびHEADメソッドは、取得以外の行動を引き起こすべきではない」という思想からくるものだそうです。

GET や HEAD は安全なメソッドであるべきで、ログアウト操作はアプリの状態を変更するものだから、原則として GET は不適切ということになります。

その他の考えられる理由

フォーラムの議論にはありませんでしたが、潜在的に以下の理由も考えられると思います。

  • CSRF 対策
  • アクションの非可逆性
  • プリフェッチングによる誤操作

それぞれ、解説します。

CSRF 対策

CSRF の一例として、「強制ログアウト攻撃」があります。

GET メソッドでログアウトボタンを実装していると、攻撃者がログイン中のユーザーになりすまして代わりにログアウトさせてしまうものです。

POST メソッドだと CSRF トークンが発行されるので、攻撃者はログイン中のユーザーになり変わることが難しくなります。

またこの「強制ログアウト攻撃」を起点として、さらなる攻撃が加えられることもあるので、やはり CSRF は注意すべきことの一つとなります。

「さらなる攻撃」とは具体的に、リダイレクトの悪用、セッションハイジャックなどが挙げられます。

アクションの非可逆性

ユーザーがブラウザの履歴から「過去にログアウトした際に利用したリクエスト」を実行してしまうと、新しいセッションでもログアウトできてしまいます。

これが POST メソッドで実行される場合には、CSRF トークンが付与されているので有効期限が切れたリクエストということで弾かれます。

つまり POST メソッドを使えば、ユーザーが履歴から誤ってリクエストを送信した際にログアウトしてしまうことを防ぐことができます。

プリフェッチングによる誤操作

最近のブラウザは、あらかじめページの内容を読み込みます。(プリフェッチング)

GET リクエストによるログアウトがリンクとして設置されていると、プリフェッチングの段階でログアウトされてしまう可能性があります。

Django のリリースノートと議論

Django 4.1 では、以下のように廃止予告がされていました。

【原文】

Logging out via GET requests to the built-in logout view is deprecated. Use POST requests instead.

If you want to retain the user experience of an HTML link, you can use a form that is styled to appear as a link.

【Deepl による翻訳】

組み込みのログアウトビューへのGETリクエストによるログアウトは非推奨です。代わりにPOSTリクエストを使用してください。

HTML リンクのユーザーエクスペリエンスを維持したい場合は、リンクとして表示されるようにスタイルされたフォームを使用できます。

出典:Log out via GET

まとめ

Django 5.0 以前は GET メソッド(aタグを使ったリンク設置)でも実装可能だったので、先日はこれでハマってしまいました。

POST メソッドで実装することで実現できますので、本記事を参考にしてみてください。

この記事が気に入ったら
フォローしてね!

シェア・記事の保存はこちら!

この記事を書いた人

karo@プログラマのアバター karo@プログラマ プログラマ

「書くことで人の役にたつ」をモットーに活動中。
本職はプログラマで、Pythonが得意。
基本情報技術者試験合格。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)