【任务8】注册与登录:设计一个加密算法
打卡要求:
1、在工具类定义一个静态方法,这个方法就是用来加密。
2、请给出你的设计思路,也可以给出具体加密算法的代码,不过必须要先给出大致思路。
下面我提供一些加密思路,然后你们可以参考:
先说一下对方一般是怎么破解密码的:比如对方随便拿一个密码也进行MD5,然后把结果和你加密后的密码对比一下,如果一样,说明就找到你的原始密码了,反正对方就是暴力一个一个密码测试,当然,他们有个测试表,毕竟很多人的密码都是手机号啥的。
1、加密算法应该不可逆,之所以不可逆,主要担心你不仅密码泄漏了,加密算法的代码也泄漏了,如果可逆,那么别人可以还原。
2、大部分人都采用了 MD5 算法,MD5不可逆,我们也可以采用这个,虽然不是最好的,但是方便。
3、通过加盐值来提供破解难度,比如有些人的密码设置很随意,就是 123456,那这样别人用MD5,一下子就测试到了,你加盐之后可以提高难度。
4、不仅加盐,我还要加随机盐。
基于这些思考,你可以设计出更好的方案,当然,你也可以直接一个 MD5 加密就行,到时候记得去看一看面试连环炮哦
本问题对应的项目链接:从0设计一个web项目
共以下 7 个回答
加密方式:MD5,随机加盐
public class Md5Memory {
}
加密方式:MD5、加随机盐
采用bcrypt算法进行加密,导入依赖
工具类
一开始,我是仿照前面的同学的做法,使用 MD5(SHA1(password) + salt) 的方法实现加密,数据库存盐和密码,后来意识到数据泄露,盐被拿到,撞表是很容易的事情,所以去了解了PBKFD2、bcrypt、scrypt、最终选择了比较新的,比较牛的argon2来做加密,它是基于AES实现的一种不可逆加密,原理就是提高破解成本,需要耗时耗空间才有破解可能,且破解几率概率很低(现阶段已经几乎不存在不可破解的密码)。
最后,引入的是argon2的依赖包来实现加密。
代码在项目的utils包下,测试类有相关样例
github地址:https://github.com/lucca-suen/FromZerotoExpert/tree/v2.3
3.可以通过加pepper,就是加胡椒,来进一步增加破解难度。可以加一个公共pepper,通过一个安全的API来访问,或者用一个哈希密码对称加密作为自己的pepper,或者维护一个pepper表,通过pepperID来与password对应。pepper应该拼接在密码的前缀。
参考博客:https://www.ujcms.com/knowledge/509.html#:~:text=%E5%AF%86%E7%A0%81%E5%8A%A0%E5%AF%86%E5%AD%98%E5%82%A8%E6%8A%80%E6%9C%AF%E8%AF%A6%E8%A7%A3%EF%BC%88Password%20Storage%20Cheat%20Sheet%EF%BC%89%201%20%E4%BD%BF%E7%94%A8%20Bcrypt%20%E3%80%82,3%20%E4%BD%BF%E7%94%A8%E7%9B%90%20Salt%20%EF%BC%88%E7%8E%B0%E4%BB%A3%E7%AE%97%E6%B3%95%E4%BC%9A%E8%87%AA%E5%8A%A8%E5%B8%AE%E4%BD%A0%E8%BF%99%E4%B9%88%E5%81%9A%EF%BC%89%E3%80%82%204%20%E8%80%83%E8%99%91%E4%BD%BF%E7%94%A8%E8%83%A1%E6%A4%92%20Pepper%20%E6%9D%A5%E6%8F%90%E4%BE%9B%E9%A2%9D%E5%A4%96%E7%9A%84%E9%98%B2%E5%BE%A1%E6%B7%B1%E5%BA%A6%EF%BC%88%E5%B0%BD%E7%AE%A1%E5%8D%95%E7%8B%AC%E4%BD%BF%E7%94%A8%E5%AE%83%E6%97%A0%E6%B3%95%E6%8F%90%E4%BE%9B%E9%A2%9D%E5%A4%96%E7%9A%84%E5%AE%89%E5%85%A8%E7%89%B9%E6%80%A7%EF%BC%89%E3%80%82
我是采用现代加密算法PBKDF2来对密码进行加密。
GitHub地址:https://github.com/shuaishuaixi03/FromZerotoExpert/tree/dev
用雪花算法生成盐值(存储在数据库中),加密是 先利用用户输入原始密码长度作为种子,置乱盐值生成新盐值,再利用新盐值与原始密码进行两次MD5加密
测试: {"success":true,"message":"用户添加成功","content":{"id":null,"username":"test","password":"2429ff95c99e6ae6382f5620e1916e9b","email":null,"comment":null,"salt":"95862563429941248"}}
服务器链接:http://124.223.184.187:8088/FromZerotoExpert
Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev
首先简单的说一下我的思路,最开始我的想法是这样的,当用户进行注册时,我们利用生成UUID的方式生成一个随机盐值并保存在数据库中(这里用的是Hutool 工具包,用IdUtil.simpleUUID()方法生成唯一的32位字符串,原理类似于雪花算法,是基于时间戳生成的唯一字符串,高并发下基本保证不重复),然后将用户输入的密码与盐值进行字符串拼接,并将拼接好的字符串进行MD5加密,然后将生成的加密后的字符串保存在数据库的password字段中。但是,这样的做法存在一些问题,在这里我假设我的数据库表已经被人窃取,那么我的盐值很容易被窃取者看到(因为已经保存在数据库中),那么他就可能猜到我数据库中的密码是密码+盐值拼接的方式进行MD5加密而生成的,于是他就可以暴力的尝试他输入的密码+我数据库中的盐值进行破解,一旦猜对,密码就被人猜到,比较危险。所以我进行了第一步优化,就是生成盐的方式进行更改,如下代码所示。
我在生成盐值的时候,生成一个真盐realSalt,一个假盐fakeSalt,并将真盐和假盐混合在一起(比如真盐是"123",假盐是"abc",那么混合盐则是"1a2b3c"),然后存在数据库中的是混合盐,而用来和密码进行拼接加密的是真盐,这样的话即使数据库被窃取,如果窃取方不知道获取真盐的方式,那就很难破解出真正的密码。但是,这里还有一个小小的逻辑漏洞,就是我把真盐和假盐混合了之后,盐的长度也随之增长,原来用UUID方式生成的盐只有32位,而混合后的盐有64位,比较长,那么破解方是有可能怀疑到我的盐是混合而成的,而如果混合方式一旦被猜到(或者我的开发团队有人暴露了混合盐的方式),那么他仍旧可以提取出真盐假盐并用暴力猜密码的方式进行破解。所以我再次进行了优化,就是利用假盐进行二次加密,流程是先让用户输入的密码+真盐进行字符串拼接并使用MD5进行一次加密,然后将加密后的字符串与假盐拼接用MD5进行二次加密,将最后加密生成的字符串存储到user表中的password字段。但是呢,这种方式其实只是将假盐重复利用了一次而已,和上次优化相比,破解方如果能提取出真盐和假盐,说不定也能想到真盐一次加密,假盐二次加密的做法。因此,我又一次进行了优化,就是把假盐进行随机乱序排列,具体如下代码所示。
利用随机数种子,进行假盐字符串的置乱,当种子一样时,置乱生成的字符串就一定一样(在这里我把种子写死了,用静态常量进行表示的,但其实可以写在别处动态获取,而且这个随机数种子其实也可以用真盐或者假盐的hashCode()或者类似的方法生成,这样就能尽可能的保证每个用户的随机种子都是不同的并且只能从盐中获得),所以我们用密码+真盐进行一次加密生成的字符串,与置乱后的假盐进行拼接并完成MD5的二次加密,将二次加密生成的字符串当作最终的password,这样的话破解方如果想要知道我们的密码,首先他要知道我的盐是混合盐,并且怎么提取出真盐和假盐,其次他要知道我设置的随机数种子是多少,并且将假盐置乱的方式也要和我一样,然后他才能开始一次次的暴力破解,以上任何一步出现了问题,都无法破解真正密码。这种方式是我优化后的最终加密的方式,具体代码如下所示。
总的来说就是加了三层保护,分别是二次加密,随机种子的假盐置乱,混合盐。以上是我的思路及想法,可能有存在问题的地方,还请大家多多指教。
其中最终数据库存入的数据如下图所示:
gitHub地址:https://github.com/Second6s9/FromZerotoExpert/tree/dev
gitHub防覆盖地址:https://github.com/Second6s9/FromZerotoExpert/tree/day08
服务器址:http://123.57.142.33:8088/FromZerotoExpert/
哈哈,很6啊,加密这么多次,盗取者怕是直接放弃,,,都是很不错的想法,后面我在综合大家的想法,整理一下。
哈哈哈,自己看了几篇加密算法的博客然后瞎琢磨的,把自己当成一个无赖的攻击者,然后想着怎么打败自己,就加密了这么多次。。。