概要
django-axesを使うとDjangoペースで不審なログインや不正なアクセスを防ぐライブラリを使ってアカウントのロック機能を気軽に実装できるので実装方法について解説します
前提
- Djangoのプロジェクトを作成済み
- ログインAPIを作成済み
ディレクトリ構成
tree
・
├── .gitignore
├── README.md
├── application
│ ├── application
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── fixtures
│ │ │ └── fixture.json
│ │ ├── migrations
│ │ │ ├── __init__.py
│ │ │ └── 0001_initial.py
│ │ ├── models.py
│ │ ├── serializers.py
│ │ ├── utils
│ │ │ └── signals.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── manage.py
│ ├── poetry.lock
│ ├── project
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── pyproject.toml
└── docker-compose.yml
初期設定
django-axesをインストールします
pip install django-axes
settings.pyにaxesの設定を追加します
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# axesを追加
'axes',
]
AUTHENTICATION_BACKENDS = [
# AxesStandaloneBackendをAUTHENTICATION_BACKENDSのリストの先頭に記載する必要があります
'axes.backends.AxesStandaloneBackend',
# 今回はDjangoでデフォルトで設定されているModelBackendを認証で使用します
'django.contrib.auth.backends.ModelBackend',
]
MIDDLEWARE = [
# Djangoで使用されるデフォルトのミドルウェア
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# AxesMiddlewareをMIDDLEWAREの配列の一番最後に記載する必要があります
'axes.middleware.AxesMiddleware',
]
# ロックされるまでのログイン回数
AXES_FAILURE_LIMIT = 6
# 自動でロックが解除されるまでの時間
AXES_COOLOFF_TIME = 0.5
# ロック対象をusernameで判断する
AXES_LOCKOUT_PARAMETERS = ["username"]
# ログインに成功したら失敗回数をリセットされるようにする
AXES_RESET_ON_SUCCESS = True
# アクセスログをデータベースに書き込まないようにする
AXES_DISABLE_ACCESS_LOG = True
# ロックアウト中にログインに失敗した場合、クールオフ期間をリセットしないようにする
AXES_RESET_COOL_OFF_ON_FAILURE_DURING_LOCKOUT = False
以上の設定をDBに反映するために以下のコマンドでマイグレーションを実行します
python manage.py migrate
実装
以下のファイルを実装します
- signals.py
- apps.py
シグナルの作成
以下のようにrecieverを使って6回ログインに失敗したら(user_locked_outのSignalを受け取ったら)403を返すよう設定します
signals.py
from axes.signals import user_locked_out
from django.dispatch import receiver
from rest_framework.exceptions import PermissionDenied
@receiver(user_locked_out)
def user_locked(*args, **kwargs):
raise PermissionDenied("ご利用のアカウントは凍結されています。しばらく経ってからログインしてください")
apps.pyにシグナルを設定
先ほど作成したsignalを使用できるようAppConfigに設定します
apps.py
from django.apps import AppConfig
class ApplicationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "application"
def ready(self):
from application.utils import signals
django.apps.config.pyをオーバーライドすることでDjangoが起動した瞬間、signalsがimportされます
django.apps.config.py
def ready(self):
"""
Override this method in subclasses to run code when Django starts.
"""
実際に操作してみよう!
間違ったパスワードを入力してログインAPIを実行します
django=# SELECT * FROM axes_accessattempt;
id | user_agent | ip_address | username | http_accept | path_info | attempt_time
| get_data | post_data | failures_since_start
----+-----------------------------------------------------------------------------------------------------------------------+--------------+----------+------------------+------------+-----------------------
--------+----------+-----------+----------------------
1 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 | 192.168.65.1 | 00000001 | application/json | /api/login | 2024-01-23 08:28:28.55
5871+00 | | | 1
(1 row)
6回以上ログインに失敗して以下のようにエラーメッセージが出たら成功です
また、ログインに失敗して5回目以内にログインに成功したら以下のようにaxes_accessattempt内に失敗した時のログがないことを確認できました
django=# SELECT * FROM axes_accessattempt;
id | user_agent | ip_address | username | http_accept | path_info | attempt_time | get_data | post_data | failures_since_start
----+------------+------------+----------+-------------+-----------+--------------+----------+-----------+----------------------
(0 rows)
参考