密碼管理通常不應被不必要地重新發明,Django提供一套安全靈活的工具來管理用戶密碼。如何存儲密碼,如何配置存儲哈希以及一些使用哈希密碼的實用程序。
也可看看
即使用戶可能使用強密碼,攻擊者也可能能夠竊聽其連接。使用HTTPS避免通過純HTTP連接發送密碼(或任何其他敏感數據),因為它們很容易被密碼嗅探。
Django如何存儲密碼
Django提供了一個靈活的密碼存儲系統,默認情況下使用PBKDF2。
<algorithm>$<iterations>$<salt>$<hash>
這些是用於存儲用戶密碼的組件,由美元符號字符分隔,包括以下各項:哈希算法,算法迭代次數(工作因子),隨機鹽和所得的密碼哈希。該算法是Django可以使用的多種單向哈希或密碼存儲算法之一;見下文。迭代描述算法在哈希上運行的次數。鹽是所使用的隨機種子,散列是單向函數的結果。
默認情況下,Django將PBKDF2算法與SHA256哈希結合使用,這是NIST推薦的密碼擴展機制。對於大多數用戶來說,這應該足夠了:這是相當安全的,需要大量的計算時間才能中斷。
但是,根據您的要求,您可以選擇其他算法,甚至可以使用自定義算法來匹配您的特定安全情況。同樣,大多數用戶不需要這樣做-如果您不確定,則可能不需要。如果您這樣做,請繼續閱讀:
Django通過咨詢PASSWORD_HASHERS
設置來選擇要使用的算法 。這是此Django安裝支持的哈希算法類的列表。此列表中的第一個條目(即settings.PASSWORD_HASHERS[0]
)將用於存儲密碼,所有其他條目都是有效的哈希表,可用於檢查現有密碼。這意味着,如果要使用其他算法,則需要進行修改PASSWORD_HASHERS
以在列表中首先列出首選算法。
的默認PASSWORD_HASHERS
值為:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
這意味着Django將使用PBKDF2來存儲所有密碼,但將支持檢查PBKDF2SHA1,argon2和bcrypt存儲的密碼。
接下來的幾節介紹了高級用戶可能希望修改此設置的幾種常用方法。
將Argon2與Django一起使用
Argon2是2015年密碼哈希競賽的贏家,該競賽是一個社區組織的公開競賽,旨在選擇下一代哈希算法。它的設計不比在普通CPU上計算更容易在自定義硬件上計算。
Argon2不是Django的默認設置,因為它需要第三方庫。但是,“密碼哈希競賽”面板建議立即使用Argon2,而不是Django支持的其他算法。
要將Argon2用作默認存儲算法,請執行以下操作:
-
安裝argon2-cffi庫。這可以通過運行來完成,它等效於 (以及Django的任何版本要求)。
pip install django[argon2]``pip install argon2-cffi``setup.py
-
修改
PASSWORD_HASHERS
以Argon2PasswordHasher
首先列出。也就是說,在您的設置文件中,您將:PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ]
如果您需要Django 升級密碼,請在此列表中保留和/或添加任何條目。
bcrypt
與Django一起使用
Bcrypt是一種流行的密碼存儲算法,專門用於長期密碼存儲。它不是Django的默認設置,因為它需要使用第三方庫,但是由於許多人可能想要使用它,因此Django只需最小的努力即可支持bcrypt。
要將Bcrypt用作默認存儲算法,請執行以下操作:
-
安裝bcrypt庫。這可以通過運行來完成,它等效於 (以及Django的任何版本要求)。
pip install django[bcrypt]``pip install bcrypt``setup.py
-
修改
PASSWORD_HASHERS
以BCryptSHA256PasswordHasher
首先列出。也就是說,在您的設置文件中,您將:PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', ]
如果您需要Django 升級密碼,請在此列表中保留和/或添加任何條目。
就是這樣–現在,您的Django安裝將使用Bcrypt作為默認存儲算法。
增加工作系數
PBKDF2和bcrypt
PBKDF2和bcrypt算法使用許多迭代或哈希運算。這故意降低了攻擊者的速度,使對散列密碼的攻擊更加困難。但是,隨着計算能力的提高,迭代次數需要增加。我們選擇了一個合理的默認值(並將在每個Django版本中增加默認值),但您可能希望根據您的安全需求和可用的處理能力對其進行調高或調低。為此,您將子類化適當的算法並覆蓋iterations
參數。例如,要增加默認PBKDF2算法使用的迭代次數:
-
創建一個子類
django.contrib.auth.hashers.PBKDF2PasswordHasher
:from django.contrib.auth.hashers import PBKDF2PasswordHasher class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher): """ A subclass of PBKDF2PasswordHasher that uses 100 times more iterations. """ iterations = PBKDF2PasswordHasher.iterations * 100
將此保存在項目中的某個位置。例如,您可以將其放在類似的文件中
myproject/hashers.py
。 -
將新的哈希器添加為中的第一個條目
PASSWORD_HASHERS
:PASSWORD_HASHERS = [ 'myproject.hashers.MyPBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ]
就是這樣–現在,當您使用PBKDF2存儲密碼時,您的Django安裝將使用更多迭代。
Argon2
Argon2具有三個可以自定義的屬性:
time_cost
控制哈希中的迭代次數。memory_cost
控制哈希計算期間必須使用的內存大小。parallelism
控制可以並行處理哈希計算的CPU數量。
這些屬性的默認值可能適合您。如果確定密碼哈希值太快或太慢,則可以按以下方式對其進行調整:
- 選擇
parallelism
您可以節省計算哈希的線程數。 - 選擇
memory_cost
作為您可以保留的內存KiB。 - 調整
time_cost
並測量散列密碼所需的時間。選擇一個time_cost
需要您接受的時間。如果time_cost
設置為1太慢,則降低memory_cost
。
memory_cost
解釋
argon2命令行實用程序和其他一些庫對memory_cost
參數的解釋與 Django使用的值不同。轉換由給出。memory_cost == 2 ** memory_cost_commandline
密碼升級
當用戶登錄時,如果他們的密碼與首選算法不同,則Django會自動將其升級為首選算法。這意味着當用戶登錄時,舊版本的Django安裝將自動變得更加安全,這也意味着您可以在發明新的(更好的)存儲算法時切換到新的存儲算法。
但是,Django只能升級使用中提到的算法的密碼 PASSWORD_HASHERS
,因此在升級到新系統時,應確保切勿從該列表中刪除條目。如果這樣做,使用未提及算法的用戶將無法升級。當增加(或減少)PBKDF2迭代次數或bcrypt輪次時,哈希密碼將被更新。
請注意,如果數據庫中的所有密碼均未使用默認的哈希算法進行編碼,則由於用戶登錄請求的持續時間與密碼中編碼的用戶的持續時間之間存在差異,因此您可能容易受到用戶枚舉定時攻擊的攻擊。非默認算法以及不存在的用戶(運行默認哈希器)的登錄請求的持續時間。您可以通過升級較舊的密碼哈希來減輕這種情況。
無需登錄即可升級密碼
如果您現有的數據庫具有較舊的弱哈希(例如MD5或SHA1),則可能需要自己升級這些哈希,而不是等待用戶登錄時進行升級(如果用戶未登錄則可能永遠不會發生)返回您的網站)。在這種情況下,您可以使用“包裝的”密碼哈希器。
在此示例中,我們將遷移SHA1哈希的集合以使用PBKDF2(SHA1(password))並添加相應的密碼哈希,以檢查用戶在登錄時輸入的密碼是否正確。我們假設我們正在使用內置User
模型,並且我們的項目有一個accounts
應用程序。您可以修改模式以與任何算法或自定義用戶模型一起使用。
首先,我們將添加自定義哈希器:
賬戶/ hashers.py
from django.contrib.auth.hashers import (
PBKDF2PasswordHasher, SHA1PasswordHasher,
)
class PBKDF2WrappedSHA1PasswordHasher(PBKDF2PasswordHasher):
algorithm = 'pbkdf2_wrapped_sha1'
def encode_sha1_hash(self, sha1_hash, salt, iterations=None):
return super().encode(sha1_hash, salt, iterations)
def encode(self, password, salt, iterations=None):
_, _, sha1_hash = SHA1PasswordHasher().encode(password, salt).split('$', 2)
return self.encode_sha1_hash(sha1_hash, salt, iterations)
數據遷移可能類似於:
賬戶/遷移/ 0002_migrate_sha1_passwords.py
from django.db import migrations
from ..hashers import PBKDF2WrappedSHA1PasswordHasher
def forwards_func(apps, schema_editor):
User = apps.get_model('auth', 'User')
users = User.objects.filter(password__startswith='sha1$')
hasher = PBKDF2WrappedSHA1PasswordHasher()
for user in users:
algorithm, salt, sha1_hash = user.password.split('$', 2)
user.password = hasher.encode_sha1_hash(sha1_hash, salt)
user.save(update_fields=['password'])
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
# replace this with the latest migration in contrib.auth
('auth', '####_migration_name'),
]
operations = [
migrations.RunPython(forwards_func),
]
請注意,根據硬件的速度,數千名用戶的遷移過程需要幾分鍾的時間。
最后,我們將添加一個PASSWORD_HASHERS
設置:
mysite的/的settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'accounts.hashers.PBKDF2WrappedSHA1PasswordHasher',
]
在此列表中包括您的站點使用的任何其他哈希。
包含的哈希器
Django中包含的哈希表的完整列表為:
[
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
]
相應的算法名稱為:
pbkdf2_sha256
pbkdf2_sha1
argon2
bcrypt_sha256
bcrypt
sha1
md5
unsalted_sha1
unsalted_md5
crypt
編寫自己的哈希器
如果編寫包含工作因子(例如多次迭代)的自己的密碼哈希器,則應實現一種 方法,以彌合密碼中提供的工作因子與哈希器的默認工作因子之間的運行時差距。這樣可以防止由於使用較舊迭代次數編碼密碼的用戶的登錄請求與不存在的用戶(運行默認哈希器的默認迭代次數)之間的登錄請求不同而導致的用戶枚舉計時攻擊。harden_runtime(self, password, encoded)``encoded
以PBKDF2為例,如果encoded
包含20,000次迭代,並且哈希器的默認iterations
值為30,000,則該方法應再運行password
10,000次PBKDF2迭代。
如果您的哈希器沒有工作因素,則將該方法實現為無操作(pass
)。
手動管理用戶密碼
該django.contrib.auth.hashers
模塊提供了一組用於創建和驗證哈希密碼的功能。您可以獨立於User
模型使用它們。
-
如果您想通過將純文本密碼與數據庫中的哈希密碼進行比較來手動驗證用戶身份,請使用便捷功能
check_password()
。它有兩個參數:要檢查的純文本密碼和要檢查的用戶password
字段在數據庫中的完整值,True
如果不匹配False
則返回。 -
make_password
(password,salt = None,hasher ='default')[源代碼] ¶以此應用程序使用的格式創建哈希密碼。它有一個強制性參數:純文本密碼。(可選)如果不想使用默認值(
PASSWORD_HASHERS
設置的第一項),則可以提供使用salt和哈希算法的方法。有關每個哈希器的算法名稱,請參見 隨附的哈希器。如果password參數為None
,則返回一個不可用的密碼(永遠不會被接受check_password()
)。 -
is_password_usable
(encode_password)[源代碼] ¶返回
False
密碼是否為的結果User.set_unusable_password()
。在Django 2.1中進行了更改:在較舊的版本中,False
如果密碼為None
或空字符串,或者密碼使用的密碼不在PASSWORD_HASHERS
設置中,則會返回此值。該行為被認為是一個錯誤,因為它會阻止使用這種密碼的用戶請求重置密碼。