看到大家评论较多,就把方案加强了下
旧的系统密码存储的是md5(password),理论上md5在有限的时间空间内是不可逆的(实际上已经有人可以了)
一般使用md5(password),加加密后的密码进行保存
但这会有一个比较严重的问题,比如密码是123456的用户,存储的密码值都是e10adc3949ba59abbe56e057f20f883e
这样就会导致,如果被暴库后,可以拿已知的字典表来直接匹配了(所有e10adc3949ba59abbe56e057f20f883e的用户密码都是123456,这不是废话么)
一般的处理方式是加盐(salt),也就是md5(相对不变的内容+密码),这样即使密码一样,存储的内容也不一样,“相对不变的内容”一般会是用户名
这样的话,存储的是md5(userName+password)
这样就会好很多了,但是如果你的系统以前使用的md5(password),那么,你本身并没有存储password,要升级到这个方案,基本是不可行的。
md5(userName+md5(password)),这样的方案就优雅些了,也加了盐,也可以从原有的系统进行平滑升级
更好的方案:md5(userName+md5(password)+固定的二十位以上的字符串)
这样的话,密码的安全不再依赖于密码本身的简单或者复杂了,更多的是依赖于这二十位的字符串了,只要固定的字符串足够强壮,以至于目前的密码库都不存在此内容,那么及时用户使用了弱口令,也不影响安全。
贴一点代码吧,附件有完整的代码和jar包
package info.frady;
import org.apache.commons.codec.digest.DigestUtils;
public class CommonTest {
public static void main(String[] args) {
String userName="frady";
String password="123456";
System.out.println(DigestUtils.shaHex(password));//sha(password),很少这么干
System.out.println(DigestUtils.md5Hex(password));//md5(password),通用的做法
System.out.println(DigestUtils.md5Hex(userName+password));//md5(userName+password),加强的做法,加了用户名做salt
System.out.println(DigestUtils.md5Hex(userName+DigestUtils.md5Hex(password)));//md5(userName+md5(password)),兼容旧md5(password)升级的安全做法,用用户名做salt
}
}
没想到回复的人还很多,补充下:
1.此方案适合密码存储的是md5(password)旧系统的改造,一般这样的系统,没有机会再生成md5(password+salt)了,因为旧的系统中根本没有password!(如果你的系统中还有password,那你的代码我就不好评价了)
2.基于function的隐藏一般来讲意义不大,你所知道的function大家都能知道。我们需要的是告诉他function,但是他确逆不回来的,或者逆向难度非常大。md5就具备这样的特征,function大家都知道,但是逆向的成本非常大。
3.不通过逆向,用字典表直接查找匹配是md5目前最大的安全问题,但此方案使用的是md5(userName+md5(password)),如果userName至少是4位的话(注册的时候一般都有这个要求),那么userName+md5(password)至少得是36位了,即使我的userName是abcd,密码是abc123,好吧假设你是个有心的黑客,而且你有个强大的多的字典,以至于你的字典表里竟然有md5(abcde99a18c428cb38d5f260853678922e03),然后我确实得承认你胜利了。
这意味着即使username是四位,你的密码库得比你本来的库庞大(26个字母+10个数字)*(36)*36*36=1679616倍。167万倍的密码库,哥们,再说磁盘非常廉价,你也有点搞笑了。