锁定老帖子 主题:一种简单的给MD5加盐算法
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (3)
|
|
---|---|
作者 | 正文 |
发表时间:2012-10-22
mfkvfn 写道 如果“能拿到用户密码的MD5摘要”的人能拿到密码摘要形成的算法。
那么根据算法能很快就从最终的48位中得到16位随机数和32位MD5字符串(具体是1,4,7...为随机数,0,2,3,5,6,8,...为真正的32位MD5)。然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。 如果“能拿到用户密码的MD5摘要”的人拿不到密码摘要形成的算法。那么随便一种办法就可以了,只要对原始密码进行任意处理就可以了(甚至根本用不着MD5,简单的字符替换就可以了)。别人一样得不到密码。 好久没来,偶尔上来发个帖,发现好累呀,得说多少遍呀。 “然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。” 你要么是没理解什么是加盐,要么就是不知道什么是MD5反查库。MD5反查库还能反查出“16位随机数+真正的密码”? |
|
返回顶楼 | |
发表时间:2012-10-22
wyuch 写道 mfkvfn 写道 如果“能拿到用户密码的MD5摘要”的人能拿到密码摘要形成的算法。
那么根据算法能很快就从最终的48位中得到16位随机数和32位MD5字符串(具体是1,4,7...为随机数,0,2,3,5,6,8,...为真正的32位MD5)。然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。 如果“能拿到用户密码的MD5摘要”的人拿不到密码摘要形成的算法。那么随便一种办法就可以了,只要对原始密码进行任意处理就可以了(甚至根本用不着MD5,简单的字符替换就可以了)。别人一样得不到密码。 好久没来,偶尔上来发个帖,发现好累呀,得说多少遍呀。 “然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。” 你要么是没理解什么是加盐,要么就是不知道什么是MD5反查库。MD5反查库还能反查出“16位随机数+真正的密码”? 我脚的..你的加密假设在..反查库..能查出任何密码的..基础上..可以认为是md5可逆的...如果真那样...还不如自己写一个加密规则呢...多次加密其实已经能满足一般的需求了... |
|
返回顶楼 | |
发表时间:2012-10-22
如果非要说多次加密的缺点..那就是碰到密码是123456的bt用户...这应该是...创建密码的时候加以限制..不应该为了这点功能在数据库加个字段..你可以用用户的注册时间+用户名+id+...多了去了..没必要是吧...
|
|
返回顶楼 | |
发表时间:2012-10-22
rox 写道 原来有个朋友介绍的一种方法,不算很保险,但也没太大问题。
同样也是UserName+PassWord再MD5后,截取固定长度。 就是不保存原始的MD5,不影响验证,也最简单。 这种情况不修改用户名还好,如果可以修改用户名怎么弄呢? |
|
返回顶楼 | |
发表时间:2012-10-22
最后修改:2012-10-22
ansjsun 写道 wyuch 写道 mfkvfn 写道 如果“能拿到用户密码的MD5摘要”的人能拿到密码摘要形成的算法。
那么根据算法能很快就从最终的48位中得到16位随机数和32位MD5字符串(具体是1,4,7...为随机数,0,2,3,5,6,8,...为真正的32位MD5)。然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。 如果“能拿到用户密码的MD5摘要”的人拿不到密码摘要形成的算法。那么随便一种办法就可以了,只要对原始密码进行任意处理就可以了(甚至根本用不着MD5,简单的字符替换就可以了)。别人一样得不到密码。 好久没来,偶尔上来发个帖,发现好累呀,得说多少遍呀。 “然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。” 你要么是没理解什么是加盐,要么就是不知道什么是MD5反查库。MD5反查库还能反查出“16位随机数+真正的密码”? 我脚的..你的加密假设在..反查库..能查出任何密码的..基础上..可以认为是md5可逆的...如果真那样...还不如自己写一个加密规则呢...多次加密其实已经能满足一般的需求了... 无语问苍天呀,同学,知道什么是反问吗? |
|
返回顶楼 | |
发表时间:2012-10-22
python实现
#!/usr/bin/env python #coding:utf-8 import random import hashlib DIGEST_LEN = 16 def generate(password): salt = '%0*d%0*d' % (8, random.randint(0, 99999999), 8, random.randint(0, 99999999)) password = hashlib.md5(str(password)+salt).hexdigest() cs = [] # size 48 for i in range(DIGEST_LEN): cs.append(password[i*2]) cs.append(salt[i]) cs.append(password[i*2+1]) return "".join(cs) def verify(password, md5): salt = [] md5_password = [] for i in range(DIGEST_LEN): salt.append(md5[i * 3 + 1]) md5_password.append(md5[i * 3]) md5_password.append(md5[i * 3 + 2]) return hashlib.md5(str(password) + "".join(salt)).hexdigest() == "".join(md5_password) if __name__ == '__main__': password = generate("123456") print verify("123456", password) # print "True" |
|
返回顶楼 | |
发表时间:2012-10-22
这个对付MD5数据库可以了
|
|
返回顶楼 | |
发表时间:2012-10-22
多此一举,你的用户表pk都能修改的话,说明你的设计很ugly;如果不可修改的话,直接拿pk和密码做md5就ok了。
|
|
返回顶楼 | |
发表时间:2012-10-23
wyuch 写道 lonelybug 写道 作为一个加密函数,你这程序写的性能未免有点低。
首先你Salt补零的过程用循环就会是一个问题。你为什么不初始化一个用16个‘0’的字符串初始化一个StringBuffer,然后再作后面的工作就可以了。 对于后面48位密码产生也是同样的道理。 还有基本上你这种加密没什么意义,因为48位密码的混合顺序是固定的(人为的),而不是consistent的随机产生。 原来是有工具方法StringUtil.leftPad来补齐的,随手改成了不依赖于别的工具函数的写法了。 你要用StringBuffer,为什么不用StringBuilder呢?其实,你将这个函数改成StringBuffer实现还是StringBuilder实现,还是用+号,性能差异都可以忽略不计。 这也不是加密,只是在摘要中加入干扰项,增加反查的难度的。我前面已经说过了,只要干扰算法是公开的,人家都可以用一样的方式重新构造MD5库再反查。 我对你的“心宽”表达非常“敬仰”。 String salt = String.valueOf(i1) + String.valueOf(i2); if (salt.length() < 16) { for (int i = 0; i < 16 - salt.length(); i++) { salt += "0"; } } 这段代码,你知道如果你用+的话会产生多少次String的拷贝么? 读读thinking in java你就不会这么心宽的人为可以忽略不计。 另外,我没明白你要说啥,如果干扰算法公开都可以重构md5库反差,那你做的是什么?如果salt公开了那还叫作salt?我实在没搞懂你的逻辑。 |
|
返回顶楼 | |
发表时间:2012-10-23
lonelybug 写道 wyuch 写道 lonelybug 写道 作为一个加密函数,你这程序写的性能未免有点低。
首先你Salt补零的过程用循环就会是一个问题。你为什么不初始化一个用16个‘0’的字符串初始化一个StringBuffer,然后再作后面的工作就可以了。 对于后面48位密码产生也是同样的道理。 还有基本上你这种加密没什么意义,因为48位密码的混合顺序是固定的(人为的),而不是consistent的随机产生。 原来是有工具方法StringUtil.leftPad来补齐的,随手改成了不依赖于别的工具函数的写法了。 你要用StringBuffer,为什么不用StringBuilder呢?其实,你将这个函数改成StringBuffer实现还是StringBuilder实现,还是用+号,性能差异都可以忽略不计。 这也不是加密,只是在摘要中加入干扰项,增加反查的难度的。我前面已经说过了,只要干扰算法是公开的,人家都可以用一样的方式重新构造MD5库再反查。 我对你的“心宽”表达非常“敬仰”。 String salt = String.valueOf(i1) + String.valueOf(i2); if (salt.length() < 16) { for (int i = 0; i < 16 - salt.length(); i++) { salt += "0"; } } 这段代码,你知道如果你用+的话会产生多少次String的拷贝么? 读读thinking in java你就不会这么心宽的人为可以忽略不计。 另外,我没明白你要说啥,如果干扰算法公开都可以重构md5库反差,那你做的是什么?如果salt公开了那还叫作salt?我实在没搞懂你的逻辑。 public static String generate1(String password) { int i1 = new Random().nextInt(99999999); int i2 = new Random().nextInt(99999999); String salt = String.valueOf(i1) + String.valueOf(i2); int len = salt.length(); if (len < 16) { for (int i = 0; i < 16 - len; i++) { salt += "0"; } } password = md5Hex(password + salt); char[] cs1 = password.toCharArray(); char[] cs2 = salt.toCharArray(); char[] cs = new char[48]; for (int i = 0; i < 48; i += 3) { cs[i] = cs1[i / 3 * 2]; char c = cs2[i / 3]; cs[i + 1] = c; cs[i + 2] = cs1[i / 3 * 2 + 1]; } return new String(cs); } public static String generate2(String password) { int i1 = new Random().nextInt(99999999); int i2 = new Random().nextInt(99999999); String salt = String.valueOf(i1) + String.valueOf(i2); int len = salt.length(); if (len < 16) { StringBuffer sb = new StringBuffer(salt); for (int i = 0; i < 16 - len; i++) { sb.append("0"); } salt = sb.toString(); } password = md5Hex(password + salt); char[] cs1 = password.toCharArray(); char[] cs2 = salt.toCharArray(); char[] cs = new char[48]; for (int i = 0; i < 48; i += 3) { cs[i] = cs1[i / 3 * 2]; cs[i + 1] = cs2[i / 3]; cs[i + 2] = cs1[i / 3 * 2 + 1]; } return new String(cs); } public static String generate3(String password) { int i1 = new Random().nextInt(99999999); int i2 = new Random().nextInt(99999999); String salt = String.valueOf(i1) + String.valueOf(i2); int len = salt.length(); if (len < 16) { StringBuilder sb = new StringBuilder(salt); for (int i = 0; i < 16 - len; i++) { sb.append("0"); } salt = sb.toString(); } password = md5Hex(password + salt); char[] cs1 = password.toCharArray(); char[] cs2 = salt.toCharArray(); char[] cs = new char[48]; for (int i = 0; i < 48; i += 3) { cs[i] = cs1[i / 3 * 2]; cs[i + 1] = cs2[i / 3]; cs[i + 2] = cs1[i / 3 * 2 + 1]; } return new String(cs); } /** * 获取十六进制字符串形式的MD5摘要 */ public static String md5Hex(String src) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] bs = md5.digest(src.getBytes()); return new String(new Hex().encode(bs)); } catch (Exception e) { return null; } } public static void main(String[] args) { long t = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { generate1("admin"); } System.out.println("使用加号:\t" + (System.currentTimeMillis() - t)); t = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { generate2("admin"); } System.out.println("使用StringBuilder:\t" + (System.currentTimeMillis() - t)); t = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { generate3("admin"); } System.out.println("使用StringBuffer:\t" + (System.currentTimeMillis() - t)); } 关于加号: 说了随手改的,不是每个人都有洁癖见不得加号,而且你有洁癖也拜托实事求是公允一点嘛。最受不了的是,你还不仔细读人家的帖子就开始批评了,说半天都白说了。关于加号性能,我说可以忽略不计是有根据的,就比如上面这个程序里。我实际测试了三次,得分都相差无几。如下所示: 第一次: 使用加号: 11203 StringBuilder: 11422 StringBuffer: 11406 第二次: 使用加号: 11203 StringBuilder: 11391 StringBuffer: 11453 第三次 使用加号: 11203 StringBuilder: 11312 StringBuffer: 11485 原因何在?一是因为主要是运算是MD5,字符串运算所占比较非常小。二是因为加号不是每次都执行(90%的情况下随机出来的结果都已经是2个8位数了),而StringBuffer每次都new了实例,导致自身的性能优势被抵消甚至处于劣势了。额,细心的同学肯定发现了,我太“宽心”地让new StringBuffer每次都执行,可以改造成根据需要才new嘛,好吧,那样StringBuffer会相对于加号取得2%左右的性能优势。 那么对于大量的字符串连接,StringBuffer到底有多大优势呢,我也写了个测试例子如下: public static void main(String[] args) { main1(); main2(); main3(); } public static void main1() { long t = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { String str = ""; for (int j = 0; j < 16; j++) { str += "0"; } } System.out.println("使用加号:\t" + (System.currentTimeMillis() - t)); } public static void main2() { long t = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { StringBuilder sb = new StringBuilder(16); for (int j = 0; j < 16; j++) { sb.append("0"); } } System.out.println("StringBuilder:\t" + (System.currentTimeMillis() - t)); } public static void main3() { long t = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { StringBuffer sb = new StringBuffer(16); for (int j = 0; j < 16; j++) { sb.append("0"); } sb.toString(); } System.out.println("StringBuffer:\t" + (System.currentTimeMillis() - t)); } 第一次: 使用加号: 3484 StringBuilder: 578 StringBuffer: 1422 第二次: 使用加号: 3532 StringBuilder: 562 StringBuffer: 1406 第三次: 使用加号: 3578 StringBuilder: 562 StringBuffer: 1407 根据测试结果,每个方法每次测试都执行了100万*16=1600万次的字符串连接操作,最慢的加号费时不过3.5秒,StringBuffer要快一些,只需要1.5秒,StringBuilder性能最好,只需要不到0.6秒。 总结陈词如下: 1、lonelybug同学说的对,对于大量的字符串连接操作StringBuffer相对加号有性能优势。 2、不过也得分场合,对于少量的字符串连接,您也用不着担心,确实是可以忽略不计的,说不定您不小心没把new语句放到if时面时StringBuffer反而处于劣势了。 3、一个应用每天就算有一千万次登录,PasswordUtil里面的加号运算所费CPU时间全部加一起都不超过1秒。 4、对于局部变量,StringBuffer已经不推荐使用了,JDK1.5以后建议使用StringBuilder,因为StringBuilder不需要同步,所以相对需要同步的StringBuffer有性能优势。lonelybug用不着把StringBuffer当个宝了。 5、建议lonelybug同学您先动手再发言,不要看了几本书,就想着四处找人毛病,因为别人用了个加句就犯了你什么大忌似的,寻章摘句老雕虫不好玩。 |
|
返回顶楼 | |