2012/03/11

小谈网站用户验证系统的设计

请甄别文章的时效性。

这篇文章最后更新于 10 年前 ,其中所记录的信息可能已经不再合理或有效。如有任何建议,欢迎与我联系

现在几乎每个 Web2.0 网站都有会员管理模块,那怎样设计用户验证系统才能保证用户的账户安全呢?

本文主要涉及:

  • 数据库中怎样保存密码
  • 记住密码功能的实现方法

前段时间以 CSDN 为首的几个大型网站纷纷爆出严重的密码泄露事件,更令人惊讶的是泄露的都是明文密码。明文密码有木有!我整个人都斯巴达了!

咳咳…

从小语文老师就教导我们,密码一定要经过散列处理才能存进数据库中,以免入侵者取得数据库后直接用明文密码登录用户账户。考虑到很多人都是“一码通”(即所有网站用的密码都一样),明文存储密码带来的危害就更严重了。

选择哪种散列函数?

常用的散列函数有 MD5, SHA1, SHA256, SHA512, SHA-3 等,至于哪个最常用不得而知,但我接触过的几个项目(不是我做的)多数使用了 MD5。这些函数的比较这里就不细说了,感兴趣的话可以参考这篇文章后面的链接。就目前计算机的运行速度来看,MD5、SHA1 这样的散列函数应当尽量避免使用,因为如果使用穷举法来破解一个一般复杂的密码基本不用花很久时间,甚至甚至!连穷举都不用,破解者只要查阅彩虹表就有可能直接得到散列前的密码原文。试试把自己的密码 MD5 加密后放到 http://cmd5.com/ 里面,看看能不能查到原文,如果能,那么所有使用 MD5 做散列函数的网站对你而言和使用明文密码是没有区别的。 就我来看,SHA512 是一个很适合存储密码的散列算法,目前我做的项目都采用此算法,不过 也有人强烈要求用 bcrypt 算法来保证安全性 。一句话概括 bcrypt 的特点就是:很安全、很慢。如果你的客户不是极重要的、极易招惹攻击者的,或者除非你有足够多的 CPU 资源,SHA512 已经足够了。

哦,对了,忘了说盐(SALT)。盐,就是作料,在进行散列处理时,先把密码原文和盐放在一起(甚至搅拌搅拌)再散列。比如你的密码是 123456,盐是 Q2E4T6Y7U,那么处理的过程就是 HASH(‘123456’ + ‘Q2E4T6Y7U’),你的密码瞬间变成 123456Q2E4T6Y7U 这样的健壮密码有木有!要注意的是盐的字符空间一定要大以保证安全,每个用户的盐应该是不同的,所以说盐要存储在数据库里,每次登录时要取出来验证。当然更安全的做法是盐和散列后的密码分开存放。使用盐的好处就是即使整个数据库都泄露了(包括散列后的密码和盐),破解者也不得不为每个用户单独破译,这样的成本极高。

怎样设计保存密码

HTTP 是无状态的协议,想保存用户登录状态必须使用 cookie 或 session,session 一般都是通过 cookie 实现的(对不支持 cookie 的浏览器——如老版本的 UCWEB 等——有些网站也直接把 session id 放到网址上)。

下面我来简短而形象解释一下 session:每个用户登录后,服务器都把用户的资料(如用户 id)保存在单独的保险箱中,然后把保险箱的密码(session id)存储在 cookie 中。 大多数 session 的实现都是让记录 session id 的 cookie 随浏览器关闭而销毁,这样就浏览器关闭再打开后用户又变成未登录了。

那怎么实现记住密码呢?

  • 我曾经看到一种实现是把记录 session id 的 cookie 的生命周期设为很长的时间,如两周,这样就可以实现记住密码了。然而,不安全啊!你想啊,别人把这个 cookie 原封拷进自己的电脑,然后就成功完成冒名顶替了。
  • 也可以在 cookie 中存用户 id 和一个记录在数据库中的随机值,然后系统查询数据库判断用户 id 和这个随机值是否符合,如果符合则登录成功。当然这个方法和前面那个存在一样的问题。

我目前用的方法其实很类似上面第二个方法,只不过在 cookie 中记录三个值:用户 id、一个记录在数据库中的随机值(token)、另一个记录在数据库中的随机值(series)。

流程如下:

  1. 用户第一次用密码登录时,程序生成两个随机值,即 token 和 series,和用户 id 一起存到数据库中。
  2. 用户再次来访时(不考虑 session),系统在数据库中查询用户 cookie 中的 3 个值,查到了即登录成功。登录成功后要再生成一个随机值,把 token 的值设置为这个随机值,同时存入数据库和 cookie 中。
  3. 如果用户来访时,系统在数据库中查到 cookie 中用户 id 与 series 都与数据库中的数据符合,但 token 不一样。这意味着有人拷走了用户的 cookie 并用该 cookie 成功登录了,这时应该警示用户这个信息,并强烈要求用户更改密码。

这个方法已经可以“解决”前两个方法的问题了,然而需要注意的是:此方法仍然无法避免别人通过拷走 cookie 冒名顶替登录,只不过能在这种不好的事情发生后通知一下受害者而已,所以在执行重要操作时(现金交易、修改密码等)一定需要用户提供密码才能继续操作。

以上是我对网站用户验证系统的一些想法,欢迎留言吐槽!

参考链接