很多时候我们会遇到需要密码校验的场景,之前写过一些对身份验证不是特别严格的系统,只是直接的把密码进行传输。
比如账户Admin的密码是123,那么客户端就传123给服务端,服务端从数据库中查找用户名为admin的家伙,然后抽出其密码123和传过来的123进行比对,发现一致,则返回成功的信息给客户端。
这么做有一些隐患,密码123是明文格式,如果有人从网络上进行截获,则一眼就破解了。
想到这里,我就把123进行了加密,比如加密的结果是D6GF8=,这样看来安全多了,即使被截获,也是一堆乱码。可是黑客如果被这个难住就不叫黑客了,对于123这么短的密码,字典遍历不出一小时就能枚举出结果。
那这样的话我就把密码变成1353ASDeh@%$这类复杂的密码,即使采用字典枚举恐怕很难攻破。确实,这么复杂的密码字典攻击恐怕得用个半年,这期间我们又可以通过强制用户更新密码来杜绝破解。
可我们的客户似乎不太喜欢这样,他们更喜欢123456这样的密码,而且也不想总改密码。
上面的这种策略可以简单的看做,A加密得到B,B解密得到A,这种转化是一对一的。
面对这个问题,人们想出了下面的这种方法:
我们对A不再加密,而是抽取其特征,举例来说,如果密码是123的话,特征可能是“一串数字,相互连续,三位数”,这样一来,符合这个特征的会有很多密码,比如456,789等等。如果特征的粒度细到一定程度,可以看作是完全表现了一个密码,但不是绝对的一对一。
哈希算法Hash用来操作这一过程,粗略的可以把哈希理解为将一个内容散列到一个数轴的某个区域的一个点。
不过即使是取其特征,在字典攻击面前仍然是O(n)的复杂度,为了解决这个问题我们引入Salt,我不知该怎么翻译,大概是撒点儿盐的意思吧,这样再嘴灵的厨子也尝不出放了什么作料了吧。原本的密码+Salt后再取Hash,这样字典攻击的复杂度一下子变成了指数级别。加入Salt的同时增加了Hash的不重复性,我是这么理解的。
孔子曰:什么样的密码系统都是能破解的。
他说的对,就算你加了盐再散列比特征,还是有可能被破解的,只是破解难度比最初的校验方法增加了多倍。我们可以再辅助一些约束,比如密码复杂度,密码强制更新,密码历史记录等等方法,基本上我们可以认为这样的系统是绝对安全的。
微软在Enterprise lib中提供了下面的库
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography
我们也可以通过以下命名空间来编码实现
System.Security.Cryptography
最后一点好处,不过貌似是废话,天然的防御了SQL注入。