Python 从数据库中存储和检索密码的最安全方法

Posted

技术标签:

【中文标题】Python 从数据库中存储和检索密码的最安全方法【英文标题】:Python's safest method to store and retrieve passwords from a database 【发布时间】:2011-02-04 01:15:13 【问题描述】:

希望将用户名和密码存储在数据库中,并且想知道这样做最安全的方法是什么。我知道我必须在某处使用盐,但不确定如何安全地生成它或如何应用它来加密密码。一些示例 Python 代码将不胜感激。谢谢。

【问题讨论】:

这里有很多信息:***.com/questions/1183161/… 【参考方案1】:

将密码+盐存储为哈希和盐。看看 Django 是如何做到的:basic docs 和 source。 在数据库中,他们将<type of hash>$<salt>$<hash> 存储在单个字符字段中。您还可以将这三个部分存储在单独的字段中。

设置密码的功能:

def set_password(self, raw_password):
    import random
    algo = 'sha1'
    salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
    hsh = get_hexdigest(algo, salt, raw_password)
    self.password = '%s$%s$%s' % (algo, salt, hsh)

get_hexdigest 只是一些散列算法的薄包装。您可以为此使用 hashlib。类似hashlib.sha1('%s%s' % (salt, hash)).hexdigest()

以及检查密码的功能:

def check_password(raw_password, enc_password):
    """
    Returns a boolean of whether the raw_password was correct. Handles
    encryption formats behind the scenes.
    """
    algo, salt, hsh = enc_password.split('$')
    return hsh == get_hexdigest(algo, salt, raw_password)

【讨论】:

这真的很有帮助。非常感谢。 可能会更好 - 您不使用恒定时间字符串比较(这里不是很重要,但如果用户进行散列,例如安全 cookie,那么您必须使用恒定时间字符串比较) , 并且 sha1 可以被强制用于可预测的(即大多数人的)密码。 这些函数是类函数,但是OP没有要求类函数。 请注意,此答案中的代码使用 random.random 作为盐的来源,根据文档docs.python.org/3/library/random.html,出于安全相关目的,盐被认为是不安全的。此外,它使用单轮 sha1,这对于密码散列是不安全的,并且根据 Django 文档,默认情况下 Django 不再这样做。正如我在回答中提到的,最好使用来自 docs.python.org/3/library/hashlib.html#key-derivation 的 PBKDF2 功能。 PBKDF2也是目前Django使用的默认算法。【参考方案2】:

我认为最好为此使用专门用于散列密码的函数。我在这里解释了一些原因:https://***.com/a/10948614/893857 现在标准库有一个dedicated section in the documentation for hashing password。它甚至提到你应该从像os.urandom() 这样的加密安全随机源获取你的盐。

【讨论】:

谢谢! passlib - 快速简单。 :-)【参考方案3】:

我在这里回答了这个问题:https://***.com/a/18488878/1661689,@Koffie 也是如此。

我不知道如何强调接受的答案并不安全。它比纯文本更好,也比未加盐的哈希更好,但它仍然极易受到字典甚至暴力攻击。相反,请使用 SLOW KDF,例如 bcrypt(或至少 PBKDF2 迭代 10,000 次)

【讨论】:

您是否建议存储正确的加盐、散列密码是一种极易受到攻击的做法?您不能对加盐哈希使用字典攻击。 没有。我建议 SHA-1 的单次迭代,如上面的代码所示,不是“正确加盐和散列”。您可以对加盐散列使用字典攻击,散列越快,每个 pw 的尝试速度就越快。您希望攻击成功需要 1 天还是 64,000 天? SHA-x 散列在自定义硬件 (ASIC) 上非常快。 owasp.org/index.php/…***.com/questions/13545677/… @rz 说“看看 Django 是如何做到的”,然后显示完全不同的代码。 Django 确实 做对了:github.com/django/django/blob/master/django/contrib/auth/…【参考方案4】:

如果您对应用程序的两个端点都有足够的控制权,那么绝对最好的方法是使用PAK-Z+。

(编辑:原版推荐SRP,但PAK-Z+有安全证明。)

【讨论】:

【参考方案5】:

对于烧瓶应用程序或任何 python 应用程序,您可以使用 werkzeug WSGI Web 应用程序库,它为您提供了使用盐和不同类型的算法生成和解码密码的选项,格式如下:method$salt$hash

默认盐长度为 16,使用的算法为 sha256

例如。

from werkzeug.security import generate_password_hash, check_password_hash

raw_pwd = 'mySecurePassword'

# genreates the encrypted password
hashed_pwd = generate_password_hash(raw_pwd)

# to verify the password
print(check_password_hash(hashed_pwd, raw_pwd)) # return boolean value after validating password

您可以在此处阅读有关 werkzeug 安全性的更多信息:https://werkzeug.palletsprojects.com/en/2.0.x/utils/#module-werkzeug.security

【讨论】:

【参考方案6】:

这是一种更简单的方法(取自 effbot),只要长度大于 8 的密码不会有问题*:

import crypt

import random, string

def getsalt(chars = string.letters + string.digits):
    # generate a random 2-character 'salt'
    return random.choice(chars) + random.choice(chars)

用于生成密码:

crypt.crypt("password", getsalt())

*:长度大于 8 的密码从右到下被剥离为 8 个字符长

【讨论】:

crypt 我认为加密超过 8 个字符的密码有问题

以上是关于Python 从数据库中存储和检索密码的最安全方法的主要内容,如果未能解决你的问题,请参考以下文章

在缓存中安全地存储密码哈希

Python - 如何在文本文件中制作字典?

在 Raspberry Pi 上使用 Python 从传感器存储数据的最有效方法

双向加密:我需要存储可以检索的密码

双向加密:我需要存储可以检索的密码

在不对所有记录执行循环的情况下从数据库中检索特定数据的最有效方法是啥?