Flask教程的密码哈希
虽然我们已经将密码哈希加入我们的注册页面,我想花一些时间来讨论实际发生的事情。也许你最终使用另一种语言,或者也许passlib不支持你在未来使用的Python版本。正因为如此,你应该至少在高层了解它是如何工作的。
它不仅对安全实践很重要,它也只是很酷的工作原理!
首先,你可能会理解为什么对密码进行加密很重要。如果您的数据库存储纯文本密码,至少您将自己查看密码,因此任何有权访问您的服务器的人都可以看到密码。在一个完美的世界中,没有人会侵犯用户的隐私,但这个世界并不完美。不仅如果你使用虚拟专用服务器,或者共享主机,那么为你工作的人可能窃取用户密码,黑客可能,甚至是你的服务器的主机。
那么,我们如何掩盖密码?遮掩原始文本很容易,我们可以对一个随机的算法做到这一点。问题是,使用密码,我们实际上需要能够验证用户将来输入作为原始密码。
采取的更原始的措施之一是简单的密码哈希。这是一个哈希函数应用于用户输入的内容,该哈希是作为密码存储的内容。
这里有一个简单的哈希脚本来说明这一点,你可以运行:
import hashlib
password = 'pa$$w0rd'
h = hashlib.md5(password.encode())
print(h.hexdigest())
导入hashlib,设置示例密码,创建哈希对象,打印哈希:
6c9b8b27dea1ddb845f96aa2567c6754
所以工作相当不错。如果你刚刚在数据库中看到了哈希,你不知道这是什么意思。但是,出现以下问题:运行脚本两次,或五次。你会发现每次的输出是相同的。最初,考虑到验证,你可能认为这不是一个要求吗?我们还能如何实现验证?
这里的问题是人们创建了大量的哈希表,特别是称为哈希查找表,在那里你可以搜索哈希,然后找到相应的明文密码。你也可以自己创建一个,只需为字符组合生成哈希。生成表需要更长的时间。这些表是大的,但不是太大,不能存储在你的笔记本电脑或上网本。
我们需要的是一种生成唯一哈希的方法,但是通过询问两个哈希是否来自同一个输入,尽管它们是非常不同的哈希,仍然找到一种验证哈希的方法。
然而,在到达之前,人们提出了一个更简单的解决方案:为什么不放置一个秘密模式的文本到每个输入的密码,只有我们的服务器知道。这就是所谓的“盐析”。
盐渍,虽然仍然使用,最初开始很简单。这里有一个例子说明如何盐化工作,建立我们最后的例子:
import hashlib
user_entered_password = 'pa$$w0rd'
salt = "5gz"
db_password = user_entered_password+salt
h = hashlib.md5(db_password.encode())
print(h.hexdigest())
在这里,唯一的主要区别是我们只有一个盐,我们追加到最后。然后,任何时候用户输入他们的密码,我们添加salt,哈希,然后比较这些哈希。 de6e389819bdaa9e0ca60bb52cabccae
现在,盐可以在任何地方添加。也许是在中间的输入,也许在开始,也许在结束。你可以在开始时有一个盐,另一个在密码的中间,另一个在最后甚至。
这是很好,但有固有的风险,仍然,这里是为什么:
对于相同的密码,哈希始终相同。这意味着如果有人破解你生成的盐,那么他们现在通过生成哈希表破解了所有密码。这又可以进行大量的处理,但这绝不是今天的标准所无法达到的。
加密的一句话是,你不能依赖于安全的保密性。对加密的一个很好的测试是问自己:“如果有人发现我的加密方法,我的安全性是否受损?在许多情况下,例如用密码,对此的答案是“是的!这是一个问题。考虑有人访问您的数据库的许多原因也意味着他们有权访问您的源代码。这意味着有人可以找到你的盐。从这里,打破加密密码的整个数据库是相对快速的工作。
我们想要的是一种生成唯一哈希的方法,它们的源可以容易地验证,但是暴力强制将需要每个密码的强制强制,而不是整个数据库的暴力强制。让我们带上passlib的大枪。
如果你没有passlib,你可能不会,因为它不是标准库的一部分,做一个快速:
pip install passlib
or… sudo apt-get install python-passlib
Once you have passlib, let’s play!
from passlib.hash import sha256_crypt
password = sha256_crypt.encrypt("password")
password2 = sha256_crypt.encrypt("password")
print(password)
print(password2)
print(sha256_crypt.verify("password", password))
这里我们引入passlib的哈希能力,并使用SHA256作为算法。SHA256本质上优于md5,但是在上面的例子中你可以用“sha256”替换“md5”,看到输出的哈希值保持不变,只是稍长一些。
接下来,我们显示我们使用sha256_crypt从passlib哈希“密码”两次。一次到密码的变量,再一次到password2。
然后我们输出两者的哈希,注意他们是不同的。
最后,我们验证两个单独的哈希值来自同一个源。
果然,布尔环响了,我们有一个匹配!
现在,我们有一个很好的方法来保护用户密码,同时仍然能够在用户登录时验证用户。
现在,考虑黑客违反我们的服务器并获得对我们的源代码和我们的数据库的要求。他们可以看到一切,但现在什么?
现在,他们将不得不通过暴力破解密码,与以前一样,只是现在它是一次一个密码。Yikes。他们可以做的是获取他们的密码字典(通常是可能的密码的大量列表),生成一个散列,然后尝试验证这个散列对数据库中的所有密码,通过迭代每个并运行sha256_crypt.verify他们真/假反应。然而,这个过程是非常麻烦的,并且结果是缓慢的。这将需要很长时间,并且没有办法预先准备这里。你可能会想,他们不能通过生成SHA256哈希在高级准备?不,因为sha256_crypt也使用独特的盐。
在我写这篇文章的时候,这个方法没有已知的弱点。
现在,我想强调使用上面的“方法”。在方法和方法的应用之间存在主要区别。
另一个加密和安全性的格言通常如下:
“你可以在地球上拥有最坚固,最不可穿透的加固门保护一个房间,但如果墙仍然薄弱,那就没有好处。
忘记墙壁,天花板,甚至地面都很简单。
考虑你写了一个程序的逻辑多少次,认为它是坚实的,然后遇到一个错误,并“当然!你会不断犯错误,你可能知道你做了很多。有安全性,这些错误通常是未经检查,未经测试。尝试你最好的想像一个黑客,但永远记住每个系统,连接到万维网,是可以攻击。只接受它,并在这个前提下工作。
接受passlib可能有缺陷,有一天,或者已经有人知道SHA256的缺陷。此外,大量的密码加密系统被黑客与服务器访问极其简单:
如果黑客获得对你的服务器的访问,并发现你的数据库被安全加密,他们可以做一些简单的事情,在登录表单上创建一个日志记录功能,它只是简单地保存用户键入的字段,到文本文件,或在别处传输数据。这显然不像一次获得整个数据库那么大,但是这种事情发生了。
很多人也对2FA(双因素身份验证)等事情给予了很大的信任。我讨厌爆破你的泡沫,但是,虽然这种方法使很多道理,这种方法的应用程序,你,客户端和你使用的网站非常重要。
作为一个开发人员,我已经设置了2FA几次。有很多选项,您可以选择安装2FA时,可以增加或阻碍安全。一个特别的,非常受欢迎的比特币钱包网站,例如,重新使用公共密钥为您的2FA。我发现这个,当我换手机。其结果是,有人可以暂时访问您的手机,访问您的帐户,重新验证2FA,您会发现在您的设备上没有任何更改。他们正在回收生成代码的公钥。现在他们只是等待你存了一大笔钱,然后他们带你。你永远不会知道你甚至是脆弱的。如果黑客也可以伪造你的会话cookie,这是另一种方式,他们可以做到这一点,他们甚至不需要你的代码。虚拟会话很难,但仍然可能。这就是为什么网站通常要求您在对帐户进行安全更改时重新输入密码。这个流行的比特币钱包网站?不,不需要重新输入您的密码。
尼斯安全的门,但薄弱的墙壁。
2FA错误的另一个很好的例子是当人们通过像Google一样使用2FA。很好,但如果你有你的2FA设置与谷歌验证器的Gmail帐户也不受2FA保护,你拧紧了。
大门,薄弱的墙壁。
最后,在让你感到脆弱之前,我将解决所有企业和服务器的最薄弱点:
运行他们的人。
最弱的环节总是人民。无论是因为他们犯错误,还是因为他们可以很容易地进行社交工程,人们通常是主要目标,或者至少是漏洞的原因。
我甚至不能计算我看到有多少网站被黑客入侵,因为有人提出了一个管理员,并能够获得访问。这听起来很蠢,但这个骗局很容易跌倒,特别是考虑到我们生活在今天的世界,开发人员分散,通常不是所有的本地。我个人是成功版本的受害者,我有网站的开发人员是黑客,我有无尽的尝试。这是黑客做的,他们黑客。他们不断尝试,最终他们可以通过。你的工作是使它尽可能具有挑战性。
这就像大多数犯罪。大多数犯罪是机会犯罪,你的工作不是最慢,最胖,最可怕的孩子从熊跑。