Django中的密码的加密和核对

使用Django作为框架搭建一个web应用是很高效的一件事。

现在我就在逐渐的熟悉这个框架,遇到了这样的问题:我们从前端获取的一些数据,例如密码都是明文,但是在数据库中存储的是对应的密文,当我们想注册或者修改密码的时候,怎样将密码等字符串转换成密文呢?当我们进行登录操作的时候,在前端表单中得到的也是明文,怎样将这些数据和后端的密文进行核对呢?

Django提供了原生的对明文加密的模块:make_password和check_password,现在就详细地梳理下。

先看看两个模块的源代码吧:

make_password
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def make_password(password, salt=None, hasher='default'):
"""
Turn a plain-text password into a hash for database storage
Same as encode() but generates a new random salt.
If password is None then a concatenation of
UNUSABLE_PASSWORD_PREFIX and a random string will be returned
which disallows logins. Additional random string reduces chances
of gaining access to staff or superuser accounts.
See ticket #20079 for more info.
"""
if password is None:
return UNUSABLE_PASSWORD_PREFIX + get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH)
hasher = get_hasher(hasher)
if not salt:
salt = hasher.salt()
return hasher.encode(password, salt)
check_password
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def check_password(password, encoded, setter=None, preferred='default'):
"""
Returns a boolean of whether the raw password matches the three
part encoded digest.
If setter is specified, it'll be called when you need to
regenerate the password.
"""
if password is None or not is_password_usable(encoded):
return False
preferred = get_hasher(preferred)
hasher = identify_hasher(encoded)
must_update = hasher.algorithm != preferred.algorithm
if not must_update:
must_update = preferred.must_update(encoded)
is_correct = hasher.verify(password, encoded)
if setter and is_correct and must_update:
setter(password)
return is_correct

首先将这些模块import进来:

1
from django.contrib.auth.hashers import make_password, check_password

然后使用make_password生成密码:

1
2
make_password("www.111cn.net", None, 'pbkdf2_sha256')
u'pbkdf2_sha2561200012000H6HRZD4DDiKg$RXBGBTiFWADyw+J9O7114vxKvysBVP+lz7oSYxkoic0='

这样生成的密码有一个特点,就是相同的字符串每一次生成的密码都不一样。

1
2
3
4
5
6
7
8
9
>>> make_password("www.111cn.net", None, 'pbkdf2_sha256')
'pbkdf2_sha256$24000$uaJAtjM4bkdS$Hbg5pPm7wjEkzHvwSm+jQmebTfES6nWuL0scrYvH9BU='
>>> make_password("www.111cn.net", None, 'pbkdf2_sha256')
'pbkdf2_sha256$24000$vu1Wc6TCEdLX$mBlcqgfMkVS19fX4RR7M38Gvhpr4rRjMqdZitfBQzDI='
>>> make_password("www.111cn.net", None, 'pbkdf2_sha256')
'pbkdf2_sha256$24000$fZTFHM5jIVyU$6eK0SjSZLdVdDgkzrDyNUcJLMG2LaZVoUYDf1zkY6uQ='
>>> make_password("www.111cn.net", None, 'pbkdf2_sha256') == make_password("www.111cn.net", None,'pbkdf2_sha256')
False

那么怎样去核对和匹配呢?就用到了checkpassword模块。

每次使用这个模块,会返回一个布尔类型的值,True表示匹配成功,False表示匹配不成功。

1
2
3
4
5
6
>>> text = "www.111cn.net"
>>> passwd = make_password(text, None, 'pbkdf2_sha256')
>>> print passwd
pbkdf2_sha2561200012000xzMLhCNvQbb8$i1XDnJIpb/cRRGRX2x7Ym74RNfPRCUp5pbU6Sn+V3J0=
>>> print check_password(text, passwd)
True


如果不像每次都生成不一样的密文,可以把make_password中的第二个参数也就是salt给一个固定的字符串,分析源代码可以知道如果不给传入这个参数的话,Django会自动随机生成一段密文,所以每次都不一样。举个例子:

1
2
3
4
5
6
7
8
9
10
11
>>> make_password("123456abcde", "a", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$a$3DWROrfU8opmd+/8sZETvcKvxdY+1ghn74VPt3Zuv+M='
>>> make_password("123456abcde", "a", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$a$3DWROrfU8opmd+/8sZETvcKvxdY+1ghn74VPt3Zuv+M='
>>> make_password("123456abcde", "a", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$a$3DWROrfU8opmd+/8sZETvcKvxdY+1ghn74VPt3Zuv+M='

这样做每次生成的密文都是一样的。

其实第二个参数随便传进来一个字符串都可以,但是不能为空。为空会怎样呢?

1
2
3
4
5
6
>>> make_password("123456abcde", "", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$UAPZchni6kV3$Q+s4/jcR9p/p0YlpW59QLusd9BGofBya3thI9AC9b+o='
>>> make_password("123456abcde", "", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$DnXQMTh6iYOK$o3Hel+bnmZvoRcvCrRY9AgDQs5YEvAh7Yc1hMlz7iZg='
>>> make_password("123456abcde", "", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$7Ya1Bno3T0I6$hgNAiJ10cZk3/TmitCYSz8Bfa+0W7DZYyNu9TlU3t+g='

但是如果中间参数不写的话,后面的参数就自动成了第二个参数,这样就默认成第三个参数是空的,这样的生成的密文都是一样的。

1
2
3
4
5
>>> make_password("123456abcde", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$pbkdf2_sha256$XAceUSzrCuPrhCG3MJkgwwuQn2pIzHC6n7C1b/GSzdc='
>>> make_password("123456abcde", 'pbkdf2_sha256')
'pbkdf2_sha256$24000$pbkdf2_sha256$XAceUSzrCuPrhCG3MJkgwwuQn2pIzHC6n7C1b/GSzdc='

那第三个参数是干什么的呢?其实指定生成密文的类型。具体我也不是很清楚。常见的有这么几种。

1
2
3
4
5
6
7
pbkdf2_sha256
pbkdf2_sha1
bcrypt_sha256
bcrypt
sha1
unsalted_md5
crypt