iOS逆向攻防之用户密码加密

1,387 阅读7分钟

在互联网时代,用户信息对于一个企业的重要性就比如血液,可以说一个公司最核心,最重要的就是用户的信息,用户的信息全部都存在服务器中,并通过用户的账号关联着,所以该账号对应的密码安全性非常重要!所以我们今天来研究一下用户密码加密的问题,先来还原一下业务场景:

企业的产品必须要用户注册该企业的平台账号,
用户在注册的时候,会输入账号和密码
作为以后用户在该平台上所有操作的依据。

现在的需求就是尽可能的保证该用户账号/密码的安全。我们来一步步的讨论不同方案的可行性

一、尝试各种加密方案~


把用户真实的账号密码保存在服务器中可不可行呢?

互联网初始阶段,也确实这样做过,把用户的真实密码保存在服务器中,但是非常不安全,也非常不可取。

如果用户的真实密码保存在服务器中,一旦服务器出了问题,比如数据库密码泄漏,那么服务器中存的所有的用户密码都会泄漏。这对一个互联网公司是致命的!所以现在都是只有重置密码功能,并不会有找回旧密码的功能。因为服务器根本就没存旧密码~

既然不能直接保存用户真实密码,那么这时候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位的十六进制字符

2019-10-09 23:31:01.898337+0800 PasswordTestProject[10311:1544249] 现在的密码是:e10adc3949ba59abbe56e057f20f883e

但是Hash有个特点,相同的数据运算得到的Hash值是相同的,并且由于Hash有散列碰撞现象,有一个专门的网站https://www.cmd5.com,是专门用来做Hash散列碰撞的,然后就得到了原密码~

www.cmd5.com网站截图.png

这个时候你还认为简单的Hash还很安全吗?

还有小伙伴们用过的,密码加盐后再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

把这个结果粘贴到网站中,点击查询,傻眼了吧~ 直接被查出来~,截图如下:

加盐之后,再次查询.png

先来看看这个网站,正如简短的网站介绍说的,专门针对md5、sha1等全球通用公开的加密算法进行反向查询,通过穷举字符组合的方式,以key-value的形式(例如key为123456,value为123456的Hash值e10adc3949ba59abbe56e057f20f883e)创建了明文密文对应查询数据库,创建的记录约90万亿条,占用硬盘超过500TB~

www.cmd5.com选项.png

仔细点一下这个不起眼的网站中间的选项部分,你会发现这个网站可不仅仅像它原本的UI界面这么简单,它还可以选择多次Hash嵌套,还有你能想到的加盐,只要你能拿到盐,那么破解的难度就会大幅度降低,密码破解中的说法,如果你需要10年才能穷举出来的破解,如果现在1个小时就能做到,就算破解成功!所以说单纯的加盐还不够安全!

可能小伙伴在质疑,把盐弄得复杂一点,不就好了么~

当然不行!加盐也存在隐患,盐可能会泄漏;就算生成复杂盐,如果泄露了规则,也不够安全。

下面就来提供一下这个问题的解决办法:

二、解决办法:HMAC加密方案


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加密后,cmd5网站截图.png

再去网站上找,就找不到了,而且根本没有HMAC选项,被告知需要等一段时间,是查不出来的,想都不用想~

那么看下图HMAC业务分析流程:

HMAC加密流程图.png

那么问题又来了,在另一台设备上是不是就没有这个key了?所以真正的业务流程应该是这样子的

1、登录时,用户输入账号account
2、客户端通过账号在本地钥匙串中查询是否有保存的key
3、如果本地没有,就向服务器获取
4、服务器把key发给客户端,客户端用HMAC加密,发送给服务器保存

三、HMAC加密方案最终优化

通过HMAC加密后,密码确实不容易破解了,但是大家能发现一个问题,无论你用什么样的方式加密,你发给服务器验证的永远都是加密后的一串数字,如果黑客通过https拦截,拿到这一串数字,照样可以拿到登录权限!

那么我们如何防护呢?

只需要加一点点处理:在HMAC加密之前,向服务器获取当前的时间戳(201910100105 这个时间戳精确到分钟),然后将HMAC得到的Hash值再拼接上当前的时间戳,再进行Hash,得到一个最终的Hash值字符串发送给服务器,服务器在接收到最终的字符串后,使用同样的算法,拿当前的时间戳进行验证,如果当前分钟时间戳不满足,就取上一分钟的时间戳,两次如果校验失败,那么验证失败。这样,隔两分钟,客户端发给服务器的字符串就不一样,黑客就算拿到这个字符串了,如果在2分钟内不能碰撞出密码,那么就一直登录失败,就算碰撞出来密码,也只是破解了一个用户的账号,每个账号都是独立的,所以强行破解收益超级低!这样就做到了相对安全! 心疼黑客一下~

最后整理一下优化后的HMAC加密流程图:

HMAC加密方案优化.png