密码安全:存储/传输/安全问题设计

存储设计

设计原则: 密码存储必须杜绝获取明文之可能

用户的密码通常具有一定规律,一般用户会对若干不同账户使用相同密码,不论以何种方式导致明文泄漏,就意味着用户的其他一系列账户也受到同样威胁. 在一些国家,对于用户密码存储有着相关规定.

1. 密码不以明文存储

2. 密码不以简单hash方式存储

hash方式包括hash算法选型、混淆手段及迭代次数。

算法选型

AES256是较安全的算法, MD5和SHA128属于较简单的算法, 一方面是因为它们的安全空间相对于如今的计算能力而言, 应对生日攻击(Birthdate Attack)的安全性已经变弱. 另一方面, 它们存在的年代已久, 已经有了较大规模的hash数据库, 可能通过简单的数据库查询就可以找到对应明文

生日攻击: 如果一个房间裡有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50% 以MD5为例,理论上讲它存在2^64中可能性,但其实只需尝试2^32种可能就有很大可能找到碰撞,而借助一些方法还可以将尝试次数降低到2^16以内

混淆手段

直接对明文,或者是简单加上固定的一段字符串进行hash运算并存储是不明智的选择, 这带来两个问题:一方面, 攻击者获取到hash值后, 可以自行破解/查询数据库直接获取到明文, 另一方面, 如果你的数据库中有两个用户碰巧使用相同的密码,那么攻击者就会意识到这可能是一个弱密码(如123456). 获取到数据库的攻击者就很容易找到它,并进行集中破解. 比较可靠的办法是对每个用户使用一段随机的字符串(通常称为salt),在运算hash时将其与明文连接运算.

迭代次数

如果只进行一次hash,以MD5为例,它存在2^64中可能,某个数据库中可能就存在着它的明文. 如果使用两次,那么就进一步缩小了攻击者直接通过hash找到明文的可能性. 当然,这并不能阻止攻击者通过字典攻击找出诸如"abcd1234"这样的简单密码.

密码传输

密码传输存在于两个阶段: 注册和认证.

1. 注册

Django和很多网站在注册期间密码都是通过明文传输的, 某些网站甚至没有使用HTTPS保护这些密码明文.

防范措施:

服务端存储 K = hash( password, reg_salt )

2. 认证

不论服务器上以和何种方式存储了用户密码, 用户和服务器之间总要通过一种方式进行验证. 这中间存在几种可能的攻击:

认证过程:

  1. 服务端告知客户端注册时的盐值及本次盐值: reg_salt, challenge_salt , 并运算: server_k = hash( K, challenge_salt )
  2. 客户端进行运算,并发送给服务端 K = hash( password, reg_salt ) client_k = hash( K, challenge_salt )
  3. 服务端比较 client_k与server_k

重置密码安全问题设计

现在比较流行的方法是使用手机或安全邮箱进行密码重置. 某些还会提供一种通过安全问题进行重置.

考虑到密码的两种主要泄漏方式:偷看(shoulder surfing)和社会工程(social engineering), 查户口式的"安全问题"并不安全. 例如这些问题:

在某些情况下, 这些所谓的安全问题只需要简单的调查就可以得到答案. 有趣的是, 早年的网易邮箱就曾经使用过最后一个问题"你喜欢的运动是什么", 然后我轻而易举的差点重置了临班某同学的邮箱密码

安全问题设计上应当注意以下方面: