在 Web 應用程式開發中,使用者身分驗證和授權是至關重要的環節。Django 框架提供了一套完善的機制來處理這些功能,從基本的登入登出到多因素驗證和密碼管理,都能夠有效地保障應用程式的安全。本文將深入探討 Django 框架中使用者身分驗證和授權的實務技巧,包含登入登出流程的客製化、多因素驗證方案的選擇、密碼安全策略的制定,以及如何利用 Django 的測試框架來確保程式碼的正確性。同時,我們也會探討密碼雜湊、加鹽等進階安全議題,並介紹 PBKDF2 等金鑰衍生函式的應用,以提升 Web 應用程式的安全性。
第8章 身分驗證
8.1 登出功能
Django 的登出檢視(LogoutView)預設會顯示一個標準頁面,通知使用者已成功登出。然而,我們希望使用者在登出後能夠傳回到登入表單。為了實作這個功能,我們需要在設定檔中新增一行程式碼:
LOGOUT_REDIRECT_URL = '/accounts/login/'
這行程式碼告訴 Django,在使用者登出後,應該將他們重定向到登入頁面。
8.2 多因素身分驗證
傳統的密碼認證方式容易被攻擊,因此,多因素身分驗證(Multi-Factor Authentication, MFA)成為了一種重要的安全措施。MFA 需要使用者提供多種不同的認證方式,例如:
- 單次密碼(One-Time Password, OTP)
- 智慧卡(Smart Card)
- 生物識別(Biometric Authentication)
不幸的是,目前還沒有成熟的 Python 函式庫可以輕易地實作 MFA。因此,我們需要謹慎地評估和選擇合適的解決方案。
8.3 預先要求使用者登入
Django 提供了一個 LoginRequiredMixin
類別,可以用來限制只有登入的使用者才能存取某些頁面。這個類別可以自動檢查使用者是否已經登入,如果沒有,則會將使用者重定向到登入頁面。
以下是如何使用 LoginRequiredMixin
的範例:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import View
class ProfileView(LoginRequiredMixin, View):
def get(self, request):
# 只有登入的使用者才能存取這個頁面
return render(request, 'profile.html')
或者,你也可以使用 @login_required
裝飾器來達到相同的效果:
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
@login_required
def profile_view(request):
return render(request, 'profile.html')
8.4 測試身分驗證
Django 的測試框架允許我們輕易地測試身分驗證的功能。以下是如何測試登入和登出的範例:
from django.test import TestCase
from django.contrib.auth import get_user_model
class TestAuthentication(TestCase):
def test_authenticated_workflow(self):
# 建立一個測試使用者
user_model = get_user_model()
user = user_model.objects.create_user('bob', password='password')
# 使用者登入
self.client.login(username='bob', password='password')
# 測試使用者是否能夠存取某些頁面
response = self.client.get('/profile/')
self.assertEqual(response.status_code, 200)
# 使用者登出
self.client.logout()
# 測試使用者是否能夠存取某些頁面
response = self.client.get('/profile/')
self.assertEqual(response.status_code, 302)
這個範例展示瞭如何建立一個測試使用者,登入,存取某些頁面,然後登出,並且驗證每一步的結果。
使用 Django 框架進行使用者認證和授權的測試
在本文中,我們將學習如何使用 Django 框架進行使用者認證和授權的測試。我們將建立測試使用案例來驗證使用者註冊、登入和許可權控制的功能。
測試使用者註冊
首先,我們需要建立一個測試使用案例來驗證使用者註冊的功能。我們可以使用 Django 的 TestCase
類別來建立測試使用案例。
from django.test import TestCase
from django.urls import reverse
class TestAuthentication(TestCase):
def test_user_registration(self):
# 建立一個測試使用者
user_data = {
'username': 'testuser',
'email': 'testuser@example.com',
'password1': 'testpassword',
'password2': 'testpassword'
}
response = self.client.post(reverse('accounts:register'), user_data)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, reverse('accounts:login'))
在這個測試使用案例中,我們建立一個測試使用者,並使用 client.post
方法傳送一個 POST 請求到使用者註冊檢視。我們然後驗證回應狀態碼是否為 302(重定向),並且重定向 URL 是否為登入頁面。
測試使用者登入
接下來,我們需要建立一個測試使用案例來驗證使用者登入的功能。
def test_user_login(self):
# 建立一個測試使用者
user_data = {
'username': 'testuser',
'email': 'testuser@example.com',
'password': 'testpassword'
}
self.client.post(reverse('accounts:register'), user_data)
# 登入測試使用者
response = self.client.post(reverse('accounts:login'), {'username': 'testuser', 'password': 'testpassword'})
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, reverse('accounts:profile'))
在這個測試使用案例中,我們建立一個測試使用者,並使用 client.post
方法傳送一個 POST 請求到使用者登入檢視。我們然後驗證回應狀態碼是否為 302(重定向),並且重定向 URL 是否為使用者個人資料頁面。
測試許可權控制
最後,我們需要建立一個測試使用案例來驗證許可權控制的功能。
def test_prohibit_anonymous_access(self):
response = self.client.get(reverse('accounts:profile'), secure=True)
self.assertEqual(response.status_code, 302)
self.assertIn(reverse('accounts:login'), response['Location'])
在這個測試使用案例中,我們使用 client.get
方法傳送一個 GET 請求到使用者個人資料頁面,並驗證回應狀態碼是否為 302(重定向)。我們還驗證重定向 URL 是否為登入頁面。
執行測試
我們可以使用以下命令執行測試:
$ python manage.py test
如果所有測試使用案例都透過,則表示我們的使用者認證和授權功能正常工作。
Django 中的使用者密碼管理
Django 提供了一套完整的使用者密碼管理系統,包括密碼修改、密碼重置、密碼驗證等功能。在本章中,我們將探討 Django 中的使用者密碼管理,包括密碼修改、密碼重置、密碼驗證等。
9.1 修改密碼
Django 提供了一個名為 PasswordChangeView
的檢視,用於修改使用者密碼。這個檢視會顯示一個表單,要求使用者輸入目前的密碼、新的密碼和新的密碼確認。
from django.contrib.auth.views import PasswordChangeView
from django.urls import path
urlpatterns = [
path('password_change/', PasswordChangeView.as_view(), name='password_change'),
]
9.2 密碼驗證
Django 提供了一個名為 AUTH_PASSWORD_VALIDATORS
的設定,用於定義密碼驗證規則。這個設定是一個列表,包含多個驗證器,每個驗證器都會檢查密碼是否符合某個規則。
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
9.3 自訂密碼驗證規則
我們可以自訂密碼驗證規則,例如新增一個驗證器,檢查密碼是否包含至少一個大寫字母、一個小寫字母和一個數字。
from django.contrib.auth.password_validation import Validator
class CustomPasswordValidator(Validator):
def validate(self, password, user):
if not any(char.isupper() for char in password):
raise ValidationError(_('密碼必須包含至少一個大寫字母'), code='password_no_uppercase')
if not any(char.islower() for char in password):
raise ValidationError(_('密碼必須包含至少一個小寫字母'), code='password_no_lowercase')
if not any(char.isdigit() for char in password):
raise ValidationError(_('密碼必須包含至少一個數字'), code='password_no_digit')
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'path.to.CustomPasswordValidator',
},
]
9.4 密碼重置
Django 提供了一個名為 PasswordResetView
的檢視,用於重置使用者密碼。這個檢視會顯示一個表單,要求使用者輸入電子郵件地址,用於傳送密碼重置郵件。
from django.contrib.auth.views import PasswordResetView
from django.urls import path
urlpatterns = [
path('password_reset/', PasswordResetView.as_view(), name='password_reset'),
]
自訂密碼驗證
在 Django 中,我們可以使用 AUTH_PASSWORD_VALIDATORS
設定來定義密碼驗證規則。這些規則可以幫助我們確保使用者設定的密碼夠複雜和安全。
最小長度驗證
Django 提供了一個 MinimumLengthValidator
來驗證密碼的最小長度。這個驗證器可以設定最小長度,若密碼太短,就會被拒絕。
'OPTIONS': {
'min_length': 12,
}
常見密碼驗證
另一個內建的驗證器是 CommonPasswordValidator
,它會檢查密碼是否為常見的密碼。這個驗證器可以使用 password_list_path
引數來指定一個自訂的常見密碼列表。
'OPTIONS': {
'password_list_path': '/path/to/more-common-passwords.txt.gz',
}
數字密碼驗證
NumericPasswordValidator
會拒絕純數字的密碼。
自訂密碼驗證
我們也可以建立自己的密碼驗證器。例如,以下是一個簡單的驗證器,它要求密碼至少包含四個英文單詞:
from django.utils.translation import gettext_lazy as _
class PassphraseValidator:
def __init__(self, dictionary_file='/usr/share/dict/words'):
self.min_words = 4
with open(dictionary_file) as f:
self.words = set(word.strip() for word in f)
def get_help_text(self):
return _('Your password must contain at least %s words.') % self.min_words
def validate(self, password, user=None):
tokens = password.split(' ')
if len(tokens) < self.min_words:
too_short = _('This password needs at least %s words.') % self.min_words
raise ValidationError(too_short, code='too_short')
if not all(token in self.words for token in tokens):
not_passphrase = _('This password is not a passphrase.')
raise ValidationError(not_passphrase, code='not_passphrase')
啟用自訂驗證器
要啟用自訂驗證器,我們需要在 settings.py
中設定 AUTH_PASSWORD_VALIDATORS
:
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'profile_info.validators.PassphraseValidator',
'OPTIONS': {
'dictionary_file': 'words.txt',
}
},
]
這樣就完成了自訂密碼驗證器的設定和啟用。現在,使用者設定的密碼必須至少包含四個英文單詞,否則會被拒絕。
密碼儲存與安全
在網路安全中,密碼儲存是一個非常重要的議題。當使用者註冊一個帳戶時,系統需要儲存使用者的密碼,以便於日後的登入驗證。但是,儲存密碼的方式如果不當,可能會導緻密碼洩露,從而導致使用者的帳戶被盜。
明文儲存
明文儲存是最簡單的儲存方式,即直接儲存使用者輸入的密碼。但是,這種方式非常危險,因為如果攻擊者獲得了儲存的密碼,他就可以直接使用這些密碼登入使用者的帳戶。
加密儲存
加密儲存是另一種儲存方式,即使用加密演算法將密碼加密後儲存。但是,如果攻擊者獲得了加密金鑰,他就可以解密出原始密碼。
雜湊儲存
雜湊儲存是目前最安全的儲存方式,即使用雜湊演算法將密碼轉換為雜湊值後儲存。雜湊演算法是一種單向函式,無法逆向計算出原始密碼。當使用者登入時,系統會將輸入的密碼進行雜湊,然後比較雜湊值是否與儲存的雜湊值相符。如果相符,則允許登入。
但是,即使用雜湊儲存,也可能存在一些問題。例如,如果攻擊者獲得了大量的密碼雜湊值,他可以使用彩虹表(rainbow table)來快速查找出原始密碼。彩虹表是一種預先計算好的表格,包含了大量的密碼雜湊值和對應的原始密碼。
解決方案
為瞭解決上述問題,可以使用以下方法:
- 使用足夠複雜的密碼:鼓勵使用者使用足夠複雜的密碼,例如包含大小寫字母、數字和特殊字元。
- 使用鹽值:在雜湊演算法中新增一個隨機的鹽值(salt),以增加密碼的複雜度。
- 使用適當的雜湊演算法:選擇一個安全的雜湊演算法,例如 bcrypt、PBKDF2 或 Argon2。
- 定期更新密碼:鼓勵使用者定期更新密碼,以減少攻擊者的風險。
第9章:使用者密碼
9.1 使用者密碼安全
當我們儲存使用者密碼時,為了防止攻擊者輕易地取得密碼,我們通常會使用雜湊函式(hash function)來儲存密碼的雜湊值。然而,僅僅使用雜湊函式並不足以確保密碼的安全,因為攻擊者可以使用預先計算好的彩虹表(rainbow table)來逆向推匯出原始密碼。
9.2 加鹽(Salting)
為瞭解決上述問題,我們可以使用加鹽(salting)的技術。加鹽是指在密碼中新增一串隨機的位元組,這樣即使兩個使用者有相同的密碼,由於加鹽的不同,產生的雜湊值也會不同。這使得攻擊者難以使用彩虹表來逆向推匯出原始密碼。
加鹽的優點
- 加鹽可以使得相同的密碼產生不同的雜湊值,從而增加攻擊者的難度。
- 加鹽可以防止攻擊者使用彩虹表來逆向推匯出原始密碼。
加鹽的實作
import hashlib
import secrets
def hash_password(password):
# 產生隨機的鹽
salt = secrets.token_bytes(16)
# 將密碼和鹽進行雜湊
hashed_password = hashlib.blake2b(password.encode(), salt=salt)
return hashed_password.hexdigest(), salt.hex()
def verify_password(stored_hash, stored_salt, provided_password):
# 將提供的密碼和儲存的鹽進行雜湊
provided_hash = hashlib.blake2b(provided_password.encode(), salt=bytes.fromhex(stored_salt))
# 對比雜湊值
return provided_hash.hexdigest() == stored_hash
# 測試
password = "mysecretpassword"
hashed_password, salt = hash_password(password)
print(f"Hashed Password: {hashed_password}")
print(f"Salt: {salt}")
is_valid = verify_password(hashed_password, salt, password)
print(f"Is Password Valid? {is_valid}")
9.3 雜湊函式的選擇
在實際應用中,我們需要選擇適合的雜湊函式來儲存密碼。常見的雜湊函式包括 SHA-256、BLAKE2b 等。這些函式都具有高安全性和效率。
9.4 實務應用
在實務應用中,我們需要注意以下幾點:
- 使用加鹽技術來儲存密碼。
- 選擇適合的雜湊函式。
- 儲存鹽值和雜湊值。
- 驗證密碼時,需要對比雜湊值。
圖表翻譯:
下圖示範了加鹽技術的流程:
flowchart TD A[密碼] --> B[產生隨機鹽] B --> C[將密碼和鹽進行雜湊] C --> D[儲存雜湊值和鹽] D --> E[驗證密碼] E --> F[對比雜湊值]
內容解密:
上述程式碼中,我們使用 hashlib
函式庫來進行雜湊操作,並使用 secrets
函式庫來產生隨機的鹽。這樣可以確保密碼的安全性。
密碼雜湊與金鑰衍生
在密碼安全中,使用適當的雜湊函式和金鑰衍生函式是非常重要的。這些函式可以幫助保護密碼和其他敏感資訊。
BLAKE2 雜湊函式
BLAKE2 是一種快速且安全的雜湊函式。然而,即使 BLAKE2 可以接受 salt 作為其中一個引數,但它仍然不適合用於密碼雜湊。原因在於 BLAKE2 的設計目標是快速執行,而快速的雜湊函式對於密碼安全來說並不是理想的選擇。
金鑰衍生函式
金鑰衍生函式(Key Derivation Functions, KDFs)是一種特殊的函式,設計用於從密碼或其他資訊中衍生出金鑰。這些函式通常需要耗費大量的計算資源,這使得它們更適合用於密碼安全。
PBKDF2
PBKDF2(Password-Based Key Derivation Function 2)是一種流行的金鑰衍生函式,常用於處理密碼。它可以接受一個密碼、salt 和一個迭代次數作為引數,並使用 HMAC(Keyed-Hashing for Message Authentication)和 SHA-256 等雜湊函式來衍生出金鑰。
下面是一個使用 PBKDF2 的 Python 範例:
import hashlib
import secrets
import sys
import timeit
iterations = int(sys.argv[1])
def test():
message = b'password'
salt = secrets.token_bytes(16)
hash_value = hashlib.pbkdf2_hmac('sha256', message, salt, iterations)
print(hash_value.hex())
if __name__ == '__main__':
seconds = timeit.timeit('test()', number=10, globals=globals())
print('Seconds elapsed: %s' % seconds)
這個範例使用 PBKDF2 來衍生出金鑰,並測量了這個過程所需的時間。
Argon2
Argon2 是另一種金鑰衍生函式,設計用於提供更強的密碼安全性。它可以接受多個引數,包括密碼、salt 和迭代次數,並使用更複雜的演算法來衍生出金鑰。
密碼儲存安全:PBKDF2 的應用
在現代網路安全中,儲存使用者密碼是一個非常重要的議題。為了確保使用者密碼的安全,開發人員通常會使用密碼雜湊函式(Password-Based Key Derivation Function, PBKDF)來保護密碼。其中,PBKDF2 是一個廣泛使用的密碼雜湊函式。
什麼是 PBKDF2?
PBKDF2 是一個根據密碼的金鑰衍生函式,它可以從一個密碼和一個隨機數(salt)中衍生出一個金鑰。這個過程涉及多次迭代的密碼雜湊,從而使得密碼的儲存更加安全。
PBKDF2 的工作原理
PBKDF2 的工作原理如下:
- 輸入: PBKDF2 需要三個輸入:密碼、隨機數(salt)和迭代次數。
- 雜湊: PBKDF2 使用一個密碼雜湊函式(如 SHA-1 或 SHA-256)對密碼和隨機數進行雜湊。
- 迭代: PBKDF2 對上一步的結果進行多次迭代,直到達到指定的迭代次數。
- 輸出: 最終的輸出是迭代結果的雜湊值。
從系統安全形度分析使用者身份驗證機制,本文涵蓋了登出重定向、多因素驗證、登入限制、測試方法以及密碼管理等關鍵環節。分析 Django 內建功能和自訂方案的實作細節,可以發現,雖然框架提供基本的登入登出和密碼修改功能,但對於更進階的安全需求,例如多因素驗證,仍需仰賴第三方套件或自行開發。技術限制主要體現在多因素驗證方案的成熟度和整合成本上。考量安全性與開發效率的平衡,建議優先強化密碼策略,例如強制使用強密碼、匯入加鹽雜湊(salting)和金鑰衍生函式(如 PBKDF2、Argon2)等。未來,隨著生物辨識和無密碼登入技術的發展,使用者身份驗證將更加安全便捷。玄貓認為,持續關注新興技術趨勢,並將其整合至現有系統,才能在保障使用者資訊安全的同時,提升使用者經驗。