Djangoのパスワードハッシュ化入門
安全にパスワードを保存する方法
Djangoでパスワードをハッシュ化して安全に保存する方法を解説します。標準のUserモデルやmake_password関数の使い方を紹介します。
こんな人向けの記事です
- Djangoでパスワードの安全な保存方法を学びたい人
- パスワードのハッシュ化と検証の仕組みを理解したい人
- カスタムUserモデルでパスワード管理をしたい人
Step 1パスワードハッシュ化とは
パスワードのハッシュ化とは、元のパスワードを不可逆な文字列に変換して保存することです。万が一データベースが漏洩しても、元のパスワードを復元できないようにします。
Python
# Djangoのハッシュ化の仕組み
# 元のパスワード: "mypassword123"
# ↓ ハッシュ化
# 保存される値: "pbkdf2_sha256$600000$salt$hash..."
# Djangoのデフォルトハッシュアルゴリズム(PBKDF2)
# - アルゴリズム名: pbkdf2_sha256
# - イテレーション回数: 600000(Django 5.x)
# - ソルト: ランダムに生成される文字列
# - ハッシュ値: SHA-256で計算された値
# なぜハッシュ化が必要か
# 1. データベース漏洩時にパスワードが読めない
# 2. 管理者もユーザーのパスワードを知れない
# 3. 同じパスワードでもソルトにより異なるハッシュ値になるStep 2Djangoの標準Userモデル
Djangoの標準Userモデルは、パスワードのハッシュ化を自動的に行います。
Python
from django.contrib.auth.models import User
# ユーザー作成(パスワードは自動的にハッシュ化される)
user = User.objects.create_user(
username="tanaka",
email="tanaka@example.com",
password="mypassword123"
)
print(user.password)
# pbkdf2_sha256$600000$xxxx$yyyy... (ハッシュ化済み)
# スーパーユーザー作成
admin = User.objects.create_superuser(
username="admin",
email="admin@example.com",
password="adminpass123"
)実行結果
pbkdf2_sha256$600000$aBcDeFgHiJkL$mNoPqRsTuVwXyZ1234567890abcdefghijklmnop==create()ではなくcreate_user()を使う
User.objects.create(password="...")を使うとパスワードが平文で保存されてしまいます。必ずcreate_user()を使ってください。Step 3make_passwordとcheck_password
Djangoにはパスワードのハッシュ化と検証のためのユーティリティ関数があります。
Python
from django.contrib.auth.hashers import make_password, check_password
# パスワードをハッシュ化
hashed = make_password("mypassword123")
print(hashed)
# pbkdf2_sha256$600000$xxxx$yyyy...
# パスワードの検証
is_valid = check_password("mypassword123", hashed)
print(f"正しいパスワード: {is_valid}") # True
is_valid = check_password("wrongpassword", hashed)
print(f"間違ったパスワード: {is_valid}") # False
# Userモデルでの検証
user = User.objects.get(username="tanaka")
print(user.check_password("mypassword123")) # True
print(user.check_password("wrongpass")) # False
# パスワードの変更
user.set_password("newpassword456")
user.save()
print(user.check_password("newpassword456")) # True実行結果
pbkdf2_sha256$600000$aBcDeFgH$iJkLmNoPqRsT...
正しいパスワード: True
間違ったパスワード: Falseset_password()の後にsave()が必要
set_password()はインスタンスのpasswordフィールドを変更するだけで、データベースには保存しません。必ずsave()を呼んでください。Step 4カスタムUserモデルでのパスワード管理
AbstractBaseUserを使ったカスタムUserモデルでも、パスワードハッシュ化の機能を利用できます。
Python
# models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError("メールアドレスは必須です")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password) # ハッシュ化して保存
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
return self.create_user(email, password, **extra_fields)
class CustomUser(AbstractBaseUser):
email = models.EmailField(unique=True)
name = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = "email"
def __str__(self):
return self.emailPython
# 使い方
user = CustomUser.objects.create_user(
email="tanaka@example.com",
password="securepass123",
name="田中太郎"
)
# AbstractBaseUserが提供するメソッドがそのまま使える
print(user.check_password("securepass123")) # True
user.set_password("newpass456")
user.save()Step 5パスワードバリデーション
Djangoには標準のパスワードバリデーターが用意されています。
Python
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{ # 他のユーザー属性と似ていないか
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{ # 最小文字数
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": {"min_length": 8},
},
{ # よく使われるパスワードでないか
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{ # 数字だけでないか
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]Python
# バリデーションを手動で実行
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
try:
validate_password("123") # 短すぎ、数字のみ
except ValidationError as e:
for error in e.messages:
print(f"エラー: {error}")
# ビューでのパスワード変更処理
from django.contrib.auth import update_session_auth_hash
def change_password(request):
if request.method == "POST":
old_pass = request.POST["old_password"]
new_pass = request.POST["new_password"]
if not request.user.check_password(old_pass):
messages.error(request, "現在のパスワードが正しくありません")
return redirect("change_password")
try:
validate_password(new_pass, request.user)
except ValidationError as e:
for error in e.messages:
messages.error(request, error)
return redirect("change_password")
request.user.set_password(new_pass)
request.user.save()
update_session_auth_hash(request, request.user) # セッション維持
messages.success(request, "パスワードを変更しました")
return redirect("profile")まとめ
- Djangoは標準でPBKDF2アルゴリズムによるパスワードハッシュ化を提供
create_user()でユーザー作成すると自動的にハッシュ化されるmake_password()とcheck_password()でハッシュ化と検証ができるset_password()でパスワードを変更し、必ずsave()を呼ぶ- AUTH_PASSWORD_VALIDATORSでパスワードの強度を検証できる