Django 密码与安全
密码是一种用来混淆的技术(把用公开的、标准的信息编码表示的信息通过一种变换手段,将其变为除通信
双方以外其他人所不能读懂的信息编码)。就是将正常的(可识别的)信息转变为无法识别的信息。
但这种无法识别的信息部分是破解(密码泄漏)。
密码的设计需遵守 柯克霍夫原则
:
即使密码系统的任何细节已为人悉知,只要密匙(key,又称密钥或秘钥)未泄漏,它也应是安全的。
Django 如何存储密码?
User 对象的 password 属性是如下这种格式:
<algorithm>$<iterations>$<salt>$<hash>
包括:哈希算法 $ 算法迭代次数(在哈希上运行的次数)$ 随机 Salt $ 密码哈希值
Django 使用PASSWORD_HASHERS
的设置来选择算法,PASSWORD_HASHERS是settings配置文件中的一个配置项,这是一个 Django 支持的哈希算法类列表,
第一个条目将被用来存储密码。如果你想使用不同算法,你需要修改 PASSWORD_HASHERS ,
在列表中首选列出你的算法
注:Django 1.3版本及之前使用sha1算法加密用户密码, 存储格式为(
<algorithm>$<salt>$<hash>
):
1.4及以后版本默认选择settings.PASSWORD_HASHERS[0]
作为加密用户密码的算法(默认为PBKDF2PasswordHasher(pbkdf2_sha256)), NIST (美国国家标准与技术研究院 ) 推荐的机制。存储格式为(<algorithm>$<iterations>$<salt>$<hash>
):
什么是PBKDF2?
PBKDF2算法通过多次hash来对密码进行加密。原理是通过password和salt进行hash,然后将结果作为salt在
与password进行hash,多次重复此过程,生成最终的密文。盐值的添加也会增加“彩虹表”攻击的难度,
此过程可能达到上千次,逆向破解的难度太大,破解一个密码的时间可能需要几百年,所以PBKDF2算法是安全的.**
Hasher该如何选择?
Django提供了很多的PASSWORD_HASHERS,Django自带的Hasher之间的对比如下:
但并非所有都是推荐的,目前最为推荐的是默认的PBKDF2PasswordHasher,因为它足够安全。
同时对于一些易破解的加密算法,如:md5,sha1(因为可以通过 预计算哈希链集、彩虹表 破解)所以对于一些安全程度较低的加密算法,我们应该去做对应的升级。
Hasher 有哪些方法?
hasher提供的方法如下:
-
密码加密 encode
-
密码校验 verify
-
更改迭代次数(iterations)的判断
Hasher配置iterations次数与数据库密码的中的iterations次数做比较
-
额外进行hash次数
check_password之巧妙设计
1. 获取所需的hasher
2. 获取老密码的hasher
3. 是否需要强化算法,增加复杂度
4. 加密算法是否改变判断
5. 密码校验verify
7. 额外进行hash次数
(应用:比如想提高迭代次数提高密码安全性,
更改了Hasher的iterations数量,当检测到与数据库中的数量不一致,
则会额外进行iterations - int(数据库iterations) 次迭代)
6. 更改老的加密算法
(应用:用户登陆时,密码自动升级,只需调整PASSWORD_HASHERS顺序,如:sha1 -> pbkdf2_sha1)
如何做系统的密码升级
如果公司要你升级密码系统要如何做?比如之前使用的sha1
用户登陆时,密码自动升级
只需调整PASSWORD_HASHERS顺序,如:sha1 -> pbkdf2_sha1,当用户之前的密码校验通过后
用户为登陆时,密码升级
如果数据库拥有老旧低效的哈希算法,比如 MD5 或 SHA1,那么你也许希望自己升级哈希而不是等待用户登录后
升级(如果一个用户不再登录站点,数据库一直是老密码,密码就不会升级了)
步骤:
1. 取出数据库中用户密码,把此时加密的密码再进行一次加密,生成pbkdf2_wrapped_sha1的马甲密码, 此时数据库中的马甲密码格式为:pbkdf2_wrapped_sha256$216000$4YfO8YTUgTO9$xxxxxxx
2. PASSWORD_HASHERS 中添加处理中间 PBKDF2WrappedSHA1PasswordHasher 类
3. 当用户登陆时,走到PBKDF2WrappedSHA1PasswordHasher的encode方法,把密码也加密成马甲密码
4. 然后执行密码校验,两次马甲密码一致,通过密码校验
5. 触发登陆更改密码更改老的加密算法条件。密码改成:pbkdf2_sha256$216000$GS9f0OKOxxQE$xxxxxx
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0EqSNDn-1624274177083)(http://share-images.chunyu.mobi//donghao/20210621190611.png)]
参考以下代码:
from django.contrib.auth import get_user_model
from django.core.management import BaseCommand
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运算,然后进行转换成pbkdf2_sha256 hash密码
"""
_, _, sha1_hash = SHA1PasswordHasher().encode(password, salt).split('$', 2)
return self.encode_sha1_hash(sha1_hash, salt, iterations)
class Command(BaseCommand):
def handle(self, *args, **options):
"""
sha1密码升级 -> pbkdf2_sha1
"""
User = get_user_model()
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'])
Django 如何做密码校验?
内置的密码校验器
Django提供了可插入的密码验证。可以同时配置多个密码验证器。Django中包含一些验证器,也可非常简单的实现自定义验证器。
配置于在settings文件中的AUTH_PASSWORD_VALIDATORS。只用于 Django Admin、command指令创建、修改用户的密码
提供的功能分别为:
- CommonPasswordValidator:通过与定义的一些普通的密码校验。common-passwords.txt 定义了近2w个普通密码。
- NumericPasswordValidator:isdigit 进行数字判断
- MinimumLengthValidator:len 进行长度比较
- UserAttributeSimilarityValidator: 与需要对比的用户属性(如:username, email等)进行相似度检查, 使用的python的标准库 Difflib来做文本相似度检查。
三方包 django-password-validators
- PasswordCharacterValidator
指定至少包含的数字、字母、大小写数量等,提高密码安全性
- UniquePasswordsValidator
验证用户是否曾经使用过密码, 不能设置成前面设置过的密码n次(n>=0)
自定义密码校验器
需实现以下方法:
1.AUTH_PASSWORD_VALIDATORS 添加校验器Name(导入路径), OPTIONS (__init__方法参数)
2.类实现validate、get_help_text 方法
3.可以添加 password_changed 方法,会在密码修改成功后调用