我有一个django项目,我想搬到烧瓶。 问题是以与django相同的方式加密和解密密码。 这是可能的,实现相同的加密和解密,如在Django 1.10。那就是我想在烧瓶中以相同的方式创建和验证密码,就像使用django一样。谷歌搜索给了我passlib,但文档不清楚django版本(1.10)。谢谢。django 1.10密码与烧瓶配合使用
回答
(Passlib开发商在这里)
Passlib绝对应该能够处理这种情况下,让我知道你的文档的部分不清晰,我可以尝试清除它们! (最新文档是http://passlib.readthedocs.io/en/stable/)
这应该有助于开始(假设passlib> = 1.7)。
处理事情的最简单方法是创建一个CryptContext实例,并使用数据库中的所有哈希格式进行配置。它会照顾从那里验证哈希&。
Django的1.10,你可能要像下面这样:
>>> from passlib.context import CryptContext
>>> pwd_context = CryptContext([
... default="django_pbkdf2_sha256",
... schemes=["django_argon2", "django_bcrypt", "django_bcrypt_sha256",
... "django_pbkdf2_sha256", "django_pbkdf2_sha1",
... "django_disabled"])
您可以调整“默认”上面你想新的哈希使用的任何方案 - 即使插入非Django的哈希格式像“bcrypt”进入列表。您还可以删除数据库中不存在的任何内容。
一旦上下文对象存在,只需要调用.hash()来散列密码,W /自动盐代:
>>> hash = context.hash("foo")
>>> hash
'pbkdf2_sha256$29000$uzyeK0HKJIBR$XQtpjc9nfTdteF1fpk1Jk7FCePwB7S2JLuggiE8UBE4='
然后将下面来验证散列:
>>> context.verify("foo", hash)
True
>>> context.verify("bar", hash)
False
如果需要,passlib的CryptContext tutorial中有更多的细节。
让我们深入一点:
django/contrib/auth/base_user.py
:
class AbstractBaseUser(models.Model):
...
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
def check_password(self, raw_password):
"""
Return a boolean of whether the raw_password was correct. Handles
hashing formats behind the scenes.
"""
def setter(raw_password):
self.set_password(raw_password)
# Password hash upgrades shouldn't be considered password changes.
self._password = None
self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter)
基本上,我们需要检查如何make_password
和check_password
的作品,让我们做到这一点:
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)
和校验密码:
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)
hasher_changed = hasher.algorithm != preferred.algorithm
must_update = hasher_changed or preferred.must_update(encoded)
is_correct = hasher.verify(password, encoded)
# If the hasher didn't change (we don't protect against enumeration if it
# does) and the password should get updated, try to close the timing gap
# between the work factor of the current encoded password and the default
# work factor.
if not is_correct and not hasher_changed and must_update:
hasher.harden_runtime(password, encoded)
if setter and is_correct and must_update:
setter(password)
return is_correct
Aaand这只是太多:)让我们专注于haser!
Django的默认散列器是:django.contrib.auth.hashers.PBKDF2PasswordHasher
- 如果你的代码不使用默认的,你可以找到所有的人都在django/conf/global_settings.py
下PASSWORD_HASHERS
让我们检查什么.verify
和.encode
做散列器对象。
def verify(self, password, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == self.algorithm
encoded_2 = self.encode(password, salt, int(iterations))
return constant_time_compare(encoded, encoded_2)
而这基本上是raw
密码核对,编码是一个字符串(在这种格式的数据库存储的密码:pbkdf2_sha256 $$$(不记得确切此)
反正这里发生了什么 - 。 Django的创建的新编码的密码(从原始密码),并检查结果是一样提供一个
def encode(self, password, salt, iterations=None):
assert password is not None
assert salt and '$' not in salt
if not iterations:
iterations = self.iterations
hash = pbkdf2(password, salt, iterations, digest=self.digest)
hash = base64.b64encode(hash).decode('ascii').strip()
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
这是从一个原始密码创建密码的方法。基本上你只需要执行pbkdf2
它可以在django/utils/crypto.py
中找到,据我所知它只使用标准的hashlib库。由于Django是开源的 - 你可以,因为它是:)借这个代码(可能))
所以总结以上所有:
import hashlib
import hmac
import struct
import binascii
import base64
def _long_to_bin(x, hex_format_string):
"""
Convert a long integer into a binary string.
hex_format_string is like "%020x" for padding 10 characters.
"""
return binascii.unhexlify((hex_format_string % x).encode('ascii'))
def _bin_to_long(x):
"""
Convert a binary string into a long integer
This is a clever optimization for fast xor vector math
"""
return int(binascii.hexlify(x), 16)
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
"""
Implements PBKDF2 as defined in RFC 2898, section 5.2
HMAC+SHA256 is used as the default pseudo random function.
As of 2014, 100,000 iterations was the recommended default which took
100ms on a 2.7Ghz Intel i7 with an optimized implementation. This is
probably the bare minimum for security given 1000 iterations was
recommended in 2001. This code is very well optimized for CPython and
is about five times slower than OpenSSL's implementation. Look in
django.contrib.auth.hashers for the present default, it is lower than
the recommended 100,000 because of the performance difference between
this and an optimized implementation.
"""
assert iterations > 0
if not digest:
digest = hashlib.sha256
password = password
salt = salt
hlen = digest().digest_size
if not dklen:
dklen = hlen
if dklen > (2 ** 32 - 1) * hlen:
raise OverflowError('dklen too big')
l = -(-dklen // hlen)
r = dklen - (l - 1) * hlen
hex_format_string = "%%0%ix" % (hlen * 2)
inner, outer = digest(), digest()
if len(password) > inner.block_size:
password = digest(password).digest()
password += b'\x00' * (inner.block_size - len(password))
inner.update(password.translate(hmac.trans_36))
outer.update(password.translate(hmac.trans_5C))
def F(i):
u = salt + struct.pack(b'>I', i)
result = 0
for j in range(int(iterations)):
dig1, dig2 = inner.copy(), outer.copy()
dig1.update(u)
dig2.update(dig1.digest())
u = dig2.digest()
result ^= _bin_to_long(u)
return _long_to_bin(result, hex_format_string)
T = [F(x) for x in range(1, l)]
return b''.join(T) + F(l)[:r]
def make_password(password, salt, iterations=2, digest=hashlib.sha256):
hash = pbkdf2(password=password, salt=salt, iterations=iterations, digest=digest)
hash = base64.b64encode(hash).decode('ascii').strip()
return "%s$%d$%s$%s" % ('pbkdf2_sha256', iterations, salt, hash)
def check_password(raw_password, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
encoded_2 = make_password(raw_password, salt, int(iterations))
return encoded_2 == encoded
pwd = make_password(password='test', salt='salt', iterations=2, digest=hashlib.sha256)
# pbkdf2_sha256$2$salt$paft68X11fyh4GG9uMnHtk6pY9QFojoiDckOvLG6GoI=
print(check_password('test1', pwd))
# False
print(check_password('test', pwd))
# True
顺便说一句,请记住,做一个你的密码时盐应是随机的。你自己检查.salt
方法。 ;) 快乐编码!
- 1. 烧瓶Secuirty禁用密码确认
- 2. 使用MySQL与烧瓶
- 3. 使用烧瓶Oauthlib
- 4. 使用使用烧瓶mongoengine
- 5. 在烧瓶中使用与app.add_url_rule默认
- 6. 烧瓶可配置蓝图
- 7. 的Python:js代码在烧瓶
- 8. 烧瓶 - 正确使用jsonify
- 9. 如何使用烧瓶WTF
- 10. 使用烧瓶+ couchdb + couchbase lite
- 11. 使用凉亭烧瓶
- 12. 开始使用烧瓶
- 13. 如何使用烧瓶安全保护烧瓶管理面板
- 14. 引导选择与烧瓶
- 15. 烧瓶不宁从烧瓶SQLAlchemy的
- 16. 配置MySQL与Django配合使用
- 17. 用烧瓶宁静
- 18. Python烧瓶wt形成呼吸机检查,如果密码匹配数据库
- 19. 在烧瓶中使用工厂模式时如何在烧瓶扩展中使用配置?
- 20. Django 1.10使用csrf标记
- 21. 在Django中使用Ajax(1.10)
- 22. 如何在使用烧瓶邮件时保持邮件密码安全
- 23. 使用PyCharm调试烧瓶应用程序下的烧瓶应用程序
- 24. 烧瓶virtualenv
- 25. 创建烧瓶
- 26. 烧瓶,blue_print,current_app
- 27. web.py和烧瓶
- 28. 烧瓶 - jinja2.exceptions.UndefinedError
- 29. API在烧瓶
- 30. 烧瓶render_template
谢谢@ @ @欢呼! –