在互聯網時代,用戶信息對於一個企業的重要性就好比血液,能夠說一個公司最核心,最重要的就是用戶的信息,用戶的信息所有都存在服務器中,並經過用戶的帳號關聯着,因此該帳號對應的密碼安全性很是重要!因此咱們今天來研究一下用戶密碼加密的問題,先來還原一下業務場景:算法
企業的產品必需要用戶註冊該企業的平臺帳號,
用戶在註冊的時候,會輸入帳號和密碼
做爲之後用戶在該平臺上全部操做的依據。
複製代碼
如今的需求就是儘量的保證該用戶帳號/密碼的安全。咱們來一步步的討論不一樣方案的可行性數據庫
把用戶真實的帳號密碼保存在服務器中可不可行呢?安全
互聯網初始階段,也確實這樣作過,把用戶的真實密碼保存在服務器中,可是很是不安全,也很是不可取。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加密流程圖: