在互联网时代,用户信息对于一个企业的重要性就好比血液,能够说一个公司最核心,最重要的就是用户的信息,用户的信息所有都存在服务器中,并经过用户的帐号关联着,因此该帐号对应的密码安全性很是重要!因此咱们今天来研究一下用户密码加密的问题,先来还原一下业务场景:算法
企业的产品必需要用户注册该企业的平台帐号,
用户在注册的时候,会输入帐号和密码
做为之后用户在该平台上全部操做的依据。
复制代码
如今的需求就是尽量的保证该用户帐号/密码的安全。咱们来一步步的讨论不一样方案的可行性数据库
把用户真实的帐号密码保存在服务器中可不可行呢?安全
互联网初始阶段,也确实这样作过,把用户的真实密码保存在服务器中,可是很是不安全,也很是不可取。bash
若是用户的真实密码保存在服务器中,一旦服务器出了问题,好比数据库密码泄漏,那么服务器中存的全部的用户密码都会泄漏。这对一个互联网公司是致命的!因此如今都是只有重置密码功能,并不会有找回旧密码的功能。由于服务器根本就没存旧密码~服务器
既然不能直接保存用户真实密码,那么这时候Hash算法就派上用场了,由于Hash算法的特性,单向不可逆,能够用来作识别,咱们能够在用户注册帐号的时候把用户密码的Hash值保存到服务器,这样用户之后每次登录,将用户密码的Hash值传给服务器,和服务器中存的Hash进行对比,也能够验证用户信息登录。优化
代码演示部分: 好比用户密码是’123456‘,用户在注册的时候,将密码进行一次Hash算法,并打印网站
//
// ViewController.m
// PasswordTestProject
// 用户密码加密
// Created by battleMage on 2019/10/9.
// Copyright © 2019 battleMage. All rights reserved.
//
#import "ViewController.h"
#import "NSString+Hash.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//用户登陆
NSString * pwd = @"123456";
pwd = pwd.md5String;
NSLog(@"如今的密码是:%@", pwd);
}
@end
复制代码
Run运行一下,点击屏幕,获得打印台的结果为'e10adc3949ba59abbe56e057f20f883e'这么一串128位的十六进制字符ui
2019-10-09 23:31:01.898337+0800 PasswordTestProject[10311:1544249] 如今的密码是:e10adc3949ba59abbe56e057f20f883e
复制代码
可是Hash有个特色,相同的数据运算获得的Hash值是相同的,而且因为Hash有散列碰撞现象,有一个专门的网站https://www.cmd5.com,是专门用来作Hash散列碰撞的,而后就获得了原密码~加密
这个时候你还认为简单的Hash还很安全吗?spa
还有小伙伴们用过的,密码加盐后再Hash。 加盐操做,顾名思义就是初始密码拼接上一串奇怪的字符串,这个字符串能够很长,从而保证加密的安全性。
下面咱们来加盐进行代码演示一下:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//用户登陆
NSString * pwd = @"123456";
//y加盐
NSString * salt = @"abcde";
pwd = [pwd stringByAppendingString:salt];
pwd = pwd.md5String;
NSLog(@"如今的密码是:%@", pwd);
}
复制代码
再次运行,点击模拟器,打印结果以下,获得一个Hash串 ‘600c849881fb596aacc5717f29f6081a’
2019-10-09 23:50:29.314117+0800 PasswordTestProject[10627:1563651] 如今的密码是:600c849881fb596aacc5717f29f6081a
复制代码
把这个结果粘贴到网站中,点击查询,傻眼了吧~ 直接被查出来~,截图以下:
先来看看这个网站,正如简短的网站介绍说的,专门针对md五、sha1等全球通用公开的加密算法进行反向查询,经过穷举字符组合的方式,以key-value的形式(例如key为123456,value为123456的Hash值e10adc3949ba59abbe56e057f20f883e)建立了明文密文对应查询数据库,建立的记录约90万亿条,占用硬盘超过500TB~
仔细点一下这个不起眼的网站中间的选项部分,你会发现这个网站可不只仅像它本来的UI界面这么简单,它还能够选择屡次Hash嵌套,还有你能想到的加盐,只要你能拿到盐,那么破解的难度就会大幅度下降,密码破解中的说法,若是你须要10年才能穷举出来的破解,若是如今1个小时就能作到,就算破解成功!因此说单纯的加盐还不够安全!
可能小伙伴在质疑,把盐弄得复杂一点,不就行了么~
固然不行!加盐也存在隐患,盐可能会泄漏;就算生成复杂盐,若是泄露了规则,也不够安全。
下面就来提供一下这个问题的解决办法:
一、HMAC加密方案介绍
HMAC加密方案是使用一个密钥加密,而且作了两次散列,在实际开发中,密钥来自服务器.通常是根据当前服务器时间相关的随机值
复制代码
代码演示部分:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//用户登陆
NSString * pwd = @"123456";
// //y加盐
// NSString * salt = @"abcde";
// pwd = [pwd stringByAppendingString:salt];
// pwd = pwd.md5String;
pwd = [pwd hmacMD5StringWithKey:@"BattleMage"];
NSLog(@"如今的密码是:%@", pwd);
}
复制代码
运行点击,打印结果
2019-10-10 00:15:31.523575+0800 PasswordTestProject[10979:1585298] 如今的密码是:0a763947ac5811b8c8222a1458b1d28f
复制代码
再去网站上找,就找不到了,并且根本没有HMAC选项,被告知须要等一段时间,是查不出来的,想都不用想~
那么看下图HMAC业务分析流程:
那么问题又来了,在另外一台设备上是否是就没有这个key了?因此真正的业务流程应该是这样子的
一、登陆时,用户输入帐号account
二、客户端经过帐号在本地钥匙串中查询是否有保存的key
三、若是本地没有,就向服务器获取
四、服务器把key发给客户端,客户端用HMAC加密,发送给服务器保存
复制代码
经过HMAC加密后,密码确实不容易破解了,可是你们能发现一个问题,不管你用什么样的方式加密,你发给服务器验证的永远都是加密后的一串数字,若是黑客经过https拦截,拿到这一串数字,照样能够拿到登陆权限!
那么咱们如何防御呢?
只须要加一点点处理:在HMAC加密以前,向服务器获取当前的时间戳(201910100105 这个时间戳精确到分钟),而后将HMAC获得的Hash值再拼接上当前的时间戳,再进行Hash,获得一个最终的Hash值字符串发送给服务器,服务器在接收到最终的字符串后,使用一样的算法,拿当前的时间戳进行验证,若是当前分钟时间戳不知足,就取上一分钟的时间戳,两次若是校验失败,那么验证失败。这样,隔两分钟,客户端发给服务器的字符串就不同,黑客就算拿到这个字符串了,若是在2分钟内不能碰撞出密码,那么就一直登陆失败,就算碰撞出来密码,也只是破解了一个用户的帐号,每一个帐号都是独立的,因此强行破解收益超级低!这样就作到了相对安全! 心疼黑客一下~
最后整理一下优化后的HMAC加密流程图: