密碼管理在非必要情況下一般不會(huì)重新發(fā)明,Django致力于提供一套安全、靈活的工具集來(lái)管理用戶(hù)密碼。本文檔描述Django存儲(chǔ)密碼和hash存儲(chǔ)方法配置的方式,以及使用hash密碼的一些實(shí)例。
另見(jiàn)
即使用戶(hù)可能會(huì)使用強(qiáng)密碼,攻擊者也可能竊聽(tīng)到他們的連接。使用HTTPS來(lái)避免在HTTP連接上發(fā)送密碼(或者任何敏感的數(shù)據(jù)),因?yàn)榉駝t密碼又被嗅探的風(fēng)險(xiǎn)。
Django通常使用PBKDF2來(lái)提供靈活的密碼儲(chǔ)存系統(tǒng)。
User
對(duì)象的password
屬性是一個(gè)這種格式的字符串:
<algorithm>$<iterations>$<salt>$<hash>
那些就是用于儲(chǔ)存用戶(hù)密碼的部分,以美元字符分分隔。它們由哈希算法、算法迭代次數(shù)(工作因數(shù))、隨機(jī)的salt、以及生成的密碼哈希值組成。算法是Django可以使用的,單向哈?;蛘呙艽a儲(chǔ)存算法之一,請(qǐng)見(jiàn)下文。迭代描述了算法在哈希上執(zhí)行的次數(shù)。salt是隨機(jī)的種子值,哈希值是這個(gè)單向函數(shù)的結(jié)果。
通常,Django以SHA256的哈希值使用PBKDF2算法,由NIST推薦的一種密碼伸縮機(jī)制。這對(duì)于大多數(shù)用戶(hù)都很有效:它非常安全,需要大量的計(jì)算來(lái)破解。
然而,取決于你的需求,你可以選擇一個(gè)不同的算法,或者甚至使用自定義的算法來(lái)滿(mǎn)足你的特定的安全環(huán)境。不過(guò),大多數(shù)用戶(hù)并不需要這樣做 -- 如果你不確定,最好不要這樣。如果你打算這樣做,請(qǐng)繼續(xù)閱讀:
DJango通過(guò)訪(fǎng)問(wèn)PASSWORD_HASHERS
設(shè)置來(lái)選擇要使用的算法。這里有一個(gè)列表,列出了Django支持的哈希算法類(lèi)。列表的第一個(gè)元素 (即settings.
PASSWORD_HASHERS[0]) 會(huì)用于儲(chǔ)存密碼, 所有其它元素都是用于驗(yàn)證的哈希值,它們可以用于檢查現(xiàn)有的密碼。意思是如果你打算使用不同的算法,你需要修改PASSWORD_HASHERS
,來(lái)將你最喜歡的算法在列表中放在首位。
PASSWORD_HASHERS
默認(rèn)為:
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)
這意味著,Django會(huì)使用 PBKDF2 儲(chǔ)存所有密碼,但是支持使用 PBKDF2SHA1, bcrypt, SHA1等等算法來(lái)檢查儲(chǔ)存的密碼。下一節(jié)會(huì)描述一些通用的方法,高級(jí)用戶(hù)可能想通過(guò)它來(lái)修改這個(gè)設(shè)置。
Bcrypt是一種流行的密碼儲(chǔ)存算法,它特意被設(shè)計(jì)用于長(zhǎng)期的密碼儲(chǔ)存。Django并沒(méi)有默認(rèn)使用它,由于它需要使用三方的庫(kù),但是由于很多人都想使用它,Django會(huì)以最小的努力來(lái)支持。
執(zhí)行以下步驟來(lái)作為你的默認(rèn)儲(chǔ)存算法來(lái)使用Bcrypt:
安裝bcrypt 庫(kù)。這可以通過(guò)運(yùn)行pip install django[bcrypt]
,,或者下載并運(yùn)行 python setup.py install
來(lái)實(shí)現(xiàn)。
修改 PASSWORD_HASHERS
,將 BCryptSHA256PasswordHasher
放在首位。也就是說(shuō),在你的設(shè)置文件中應(yīng)該:
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)
(你應(yīng)該將其它元素留在列表中,否則Django不能升級(jí)密碼;見(jiàn)下文)。
配置完畢 -- 現(xiàn)在Django會(huì)使用Bcrypt作為默認(rèn)的儲(chǔ)存算法。
BCryptPasswordHasher的密碼截?cái)?/p>
bcrypt的設(shè)計(jì)者會(huì)在72個(gè)字符處截?cái)嗨械拿艽a,這意味著bcrypt(password_with_100_chars) == bcrypt(password_with_100_chars[:72])
。原生的 BCryptPasswordHasher
并不會(huì)做任何的特殊處理, 所以它也會(huì)受到這一隱藏密碼長(zhǎng)度限制的約束。BCryptSHA256PasswordHasher
通過(guò)事先使用 sha256生成哈希來(lái)解決這一問(wèn)題。這樣就可以防止密碼截?cái)嗔耍阅氵€是應(yīng)該優(yōu)先考慮BCryptPasswordHasher
。這個(gè)截?cái)鄮?lái)的實(shí)際效果很微不足道,因?yàn)榇蠖鄶?shù)用戶(hù)不會(huì)使用長(zhǎng)度超過(guò)72的密碼,并且即使在72個(gè)字符處截?cái)?,破解brypt所需的計(jì)算能力依然是天文數(shù)字。雖然如此,我們還是推薦使用BCryptSHA256PasswordHasher
,根據(jù) “有備無(wú)患”的原則。
其它 bcrypt 的實(shí)現(xiàn)
有一些其它的bcrypt 實(shí)現(xiàn),可以讓你在Django中使用它。Django的bcrypt 支持并不直接兼容這些實(shí)現(xiàn)。你需要修改數(shù)據(jù)庫(kù)中的哈希值,改為 bcrypt$(raw bcrypt output)
的形式,來(lái)升級(jí)它們。例如: bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy
。
PBKDF2 和bcrypt 算法使用大量的哈希迭代或循環(huán)。這會(huì)有意拖慢攻擊者,使對(duì)哈希密碼的攻擊更難以進(jìn)行。然而,隨著計(jì)算機(jī)能力的不斷增加,迭代的次數(shù)也需要增加。我們選了一個(gè)合理的默認(rèn)值(并且在Django的每個(gè)發(fā)行版會(huì)不斷增加),但是你可能想要調(diào)高或者調(diào)低它,取決于你的安全需求和計(jì)算能力。要想這樣做,你可以繼承相應(yīng)的算法,并且覆寫(xiě)iterations
參數(shù)。例如,增加PBKDF2算法默認(rèn)使用的迭代次數(shù):
創(chuàng)建django.contrib.auth.hashers.PBKDF2PasswordHasher
的子類(lèi):
from django.contrib.auth.hashers import PBKDF2PasswordHasher
class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
"""
A subclass of PBKDF2PasswordHasher that uses 100 times more iterations.
"""
iterations = PBKDF2PasswordHasher.iterations * 100
把它保存在項(xiàng)目中的某個(gè)位置。例如,把它放在類(lèi)似于myproject/hashers.py
的文件中。
將你的新的hasher作為第一個(gè)元素添加到PASSWORD_HASHERS
:
PASSWORD_HASHERS = (
'myproject.hashers.MyPBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)
配置完畢 -- 現(xiàn)在DJango在儲(chǔ)存使用PBKDF2的密碼時(shí)會(huì)使用更多的迭代次數(shù)。
用戶(hù)登錄之后,如果他們的密碼沒(méi)有以首選的密碼算法來(lái)儲(chǔ)存,Django會(huì)自動(dòng)將算法升級(jí)為首選的那個(gè)。這意味著Django中舊的安裝會(huì)在用戶(hù)登錄時(shí)自動(dòng)變得更加安全,并且你可以隨意在新的(或者更好的)儲(chǔ)存算法發(fā)明之后切換到它們。
然而,Django只會(huì)升級(jí)在 PASSWORD_HASHERS
中出現(xiàn)的算法,所以升級(jí)到新系統(tǒng)時(shí),你應(yīng)該確保不要 _移除_列表中的元素。如果你移除了,使用列表中沒(méi)有的算法的用戶(hù)不會(huì)被升級(jí)。修改PBKDF2迭代次數(shù)之后,密碼也會(huì)被升級(jí)。
django.contrib.auth.hashers
模塊提供了一系列的函數(shù)來(lái)創(chuàng)建和驗(yàn)證哈希密碼。你可以獨(dú)立于User
模型之外使用它們。
check_password
(password, encoded)[source]
如果你打算通過(guò)比較純文本密碼和數(shù)據(jù)庫(kù)中哈希后的密碼來(lái)手動(dòng)驗(yàn)證用戶(hù),要使用check_password()
這一便捷的函數(shù)。它接收兩個(gè)參數(shù):要檢查的純文本密碼,和數(shù)據(jù)庫(kù)中用戶(hù)的password
字段的完整值。如果二者匹配,返回True
,否則返回False
。
make_password
(password, salt=None, hasher='default')[source]
以當(dāng)前應(yīng)用所使用的格式創(chuàng)建哈希密碼。它接受一個(gè)必需參數(shù):純文本密碼。如果你不想使用默認(rèn)值(PASSWORD_HASHERS
設(shè)置的首選項(xiàng)),你可以提供salt值和要使用的哈希算法,它們是可選的。當(dāng)前支持的算法是: 'pbkdf2_sha256'
, 'pbkdf2_sha1'
, 'bcrypt_sha256'
(參見(jiàn)在 Django中使用Bcrypt), 'bcrypt'
, 'sha1'
, 'md5'
, 'unsalted_md5'
(僅僅用于向后兼容) 和 'crypt'
(如果你安裝了 crypt
庫(kù))。如果password參數(shù)是None
,會(huì)返回一個(gè)不可用的密碼(它永遠(yuǎn)不會(huì)被check_password()
接受)。
is_password_usable
(_encodedpassword)[source]
檢查提供的字符串是否是可以用check_password()
驗(yàn)證的哈希密碼。
譯者:Django 文檔協(xié)作翻譯小組,原文:Password management。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。