你知道什么事情最捉急么?慢的网站。
不幸的是,验证过程中某些特定部分就应该是缓慢的。这似乎违反直觉,但是验证过程中的慢速是保证安全的很大一部分。
本文介绍 Python 中的验证过程(不仅仅是散列法),还有如何在不降低安全性的条件下提高 Python 的验证速度。
我将带你读一下 Python 伪代码,并教你如何保证验证系统尽可能快而需要掌握的知识。
密码散列法很慢
当用户在网站上注册时,给了密码——最好的方法是在存储之前去散列用户密码。这意味着你可以用一个散列算法,比如 bcrypt 或 scrypt 这样,可以把一个密码字符串翻译为不能被反向破解的胡言乱语。
一旦用了散列法,就没有办法回复原始密码了。
有个重要的点:强大的散列函数,比如 bcrypt 和 scrypt ,是命中注定般的慢啊!
散列法需要更多的 CPU、RAM 和时间用来计算,这意味着一个攻击者会需要花费更长的时间来暴力破解一个密码。
现在,假设你已经黑入了数据库,还有可以访问全部用户密码的散列。
也可以说是有10个 bcrypt 散列。
如果想要找到密码,你只能去:暴力破解它们。
想做到这点,你会写一些代码来遍历所有可能的密码组合(在下面的例子中我用 PyPI 中的brute库):
1 2 3 4 5 6 7 8 9 10 11 |
$ pip install brute # crack.py from brute import brute HASH_TO_CRACK = 'xxx' for pw in brute(length=8): if HASH_TO_CRACK == bcrypt(pw): print 'Password is:', pw break |
上面的例子中,我们会遍历所有8个字母或以下的可能密码,并试图去暴力破解它。
每次新生成一个可能的密码,通过 bcrypt
函数运行,得到散列,将它和你已经黑了的密码散列比较。如果你得到了一对儿,意味着你已经成功暴力破解了用户密码!
但是有个重点:bcrypt 和 scrypt 会花很长时间去计算,占用很多资源。
因为bcrypt和scrypt在数学意义上计算很慢,攻击者会因为占用太多计算机资源而很艰难地花费时间来暴力破解。
因此,我们现在理解了散列的原理,以及为什么时间密集——我们来讨论一下验证。
注:如果你对于了解密码安全有兴趣,你也许会想要去读篇写了有点时间的文章对于密码安全的正确方法——是个好的阅读材料。如果你想更加了解,看看这个.
如何进行验证工作
当用使用传统的方法注册或登录网站时,你需要散列他们的密码,然后存储到数据库或与数据库中的值进行比较——然后发生了什么?你要记住,用户不是有一个会话ID就是有某种API秘钥。
看看这些伪代码:
1 2 3 4 5 6 |
# register.py user = User('r@rdegges.com', 'hithere!123') user.save() # save this user to the database # Create a new session cookie in the browser, which holds the user ID. session.create('session', user.id) |
这个办法的思路是用户 ID 存储到用户的浏览器缓存中,下一次用户请求你网站时,用户浏览器就发送缓存中的用户 ID 给你的服务器,并允许你浏览用户账号下的信息,而不需要再次输入邮件地址或密码。
再看看这些伪代码:
1 2 |
# views.py user = User.find(id=session) |
正如你料想的——通过 ID 查询用户账号是非常快的(密码不散列是必要的)。
因此——这就意味着初始创建账户和登录密码的过程较慢——剩下的就都很快了!
我们继续。
优化速度
一旦用户数据请求网站的每个页面,数据就要接收的非常频繁。
如过你使用了 Postgres 或 MySQL 之类的数据库,这就意味着你的网站如果有100个用户,那你每秒要查询数据库的users表
数百次。
如果你的网站还需要做其他事,你可能忍不了这么慢的网页加载。
你可以做什么来提速呢?缓存!
缓存是速度和性能问题的解决方案——提升访问你网站的速度时,让用户数据快速加载速度的有效方法之一。
这种方法超简单:存储键值对到持久化存储中,用户ID作为键,用户的账户数据作为散列值。
这很有用,因为下次用户请求你网页时,就可以发送会话缓存,而不是通过数据库查询账户,你也可以直接从内存数据库缓存查询这些信息。
对于缓存,你也许更想要存储数据到 memcached 或 redis 之类的缓存系统中,令人开心的是,python都有对应的库
在伪代码里,你可以这么做:
1 2 3 4 5 6 |
if session: user = cache.get(session) # If no user was found in the cache, try querying the database directly. if not user: user = User.get(id=session) |
实现
如果你用了 Django之类的web框架,那用(框架)内置的校验系统就可以很容易的完成这篇文章提及的所有内容。
如果你用了另一个框架/工具,你也许想到google搜寻一下相关库。不管你用什么工具,本文的几个典型选项都能帮到你。
最后,如果你在用 Python / Flask / Django,并且想要得到关于用户存储和安全最全、最好的实践,你可以看看我们的开发者服务 Stormpath。
我们的服务为你存储用户账户和用户数据,负责处理密码散列、加密、数据安全、最好的实践和其他的所有。
可以免费用于大多数应用,也有轻松整合到Python、Flask 和 Django 应用。
我们最新发布的 Python 库中包含了内建的支持内存数据库、memcached 和 redis 缓存,以确保总是尽可能快速访问你的网站。
如果你想用Stormpath,你可以从下面地址得到我们的库: