本文摘錄自《Nodejs學習筆記》,更多章節及更新,請訪問 github主頁地址。歡迎加羣交流,羣號 197339705。javascript
MD5(Message-Digest Algorithm)是計算機安全領域普遍使用的散列函數(又稱哈希算法、摘要算法),主要用來確保消息的完整和一致性。常見的應用場景有密碼保護、下載文件校驗等。java
本文先對MD5的特色與應用進行簡要概述,接着重點介紹MD5在密碼保護場景下的應用,最後經過例子對MD5碰撞進行簡單介紹。node
jquery.js
求md5值,57254個字符,耗時1.907ms在nodejs中,crypto
模塊封裝了一系列密碼學相關的功能,包括摘要運算。基礎例子以下,很是簡單:jquery
var crypto = require('crypto');
var md5 = crypto.createHash('md5');
var result = md5.update('a').digest('hex');
// 輸出:0cc175b9c0f1b6a831c399e269772661
console.log(result);複製代碼
前面提到,將明文密碼保存到數據庫是很不安全的,最不濟也要進行md5後進行保存。好比用戶密碼是123456
,md5運行後,獲得輸出:e10adc3949ba59abbe56e057f20f883e
。git
這樣至少有兩個好處:github
示例代碼以下:算法
var crypto = require('crypto');
function cryptPwd(password) {
var md5 = crypto.createHash('md5');
return md5.update(password).digest('hex');
}
var password = '123456';
var cryptedPassword = cryptPwd(password);
console.log(cryptedPassword);
// 輸出:e10adc3949ba59abbe56e057f20f883e複製代碼
前面提到,經過對用戶密碼進行md5運算來提升安全性。但實際上,這樣的安全性是不好的,爲何呢?數據庫
稍微修改下上面的例子,可能你就明白了。相同的明文密碼,md5值也是相同的。數組
var crypto = require('crypto');
function cryptPwd(password) {
var md5 = crypto.createHash('md5');
return md5.update(password).digest('hex');
}
var password = '123456';
console.log( cryptPwd(password) );
// 輸出:e10adc3949ba59abbe56e057f20f883e
console.log( cryptPwd(password) );
// 輸出:e10adc3949ba59abbe56e057f20f883e複製代碼
也就是說,當攻擊者知道算法是md5,且數據庫裏存儲的密碼值爲e10adc3949ba59abbe56e057f20f883e
時,理論上能夠能夠猜到,用戶的明文密碼就是123456
。安全
事實上,彩虹表就是這麼進行暴力破解的:事先將常見明文密碼的md5值運算好存起來,而後跟網站數據庫裏存儲的密碼進行匹配,就可以快速找到用戶的明文密碼。(這裏不探究具體細節)
那麼,有什麼辦法能夠進一步提高安全性呢?答案是:密碼加鹽。
「加鹽」這個詞看上去很玄乎,其實原理很簡單,就是在密碼特定位置插入特定字符串後,再對修改後的字符串進行md5運算。
例子以下。一樣的密碼,當「鹽」值不同時,md5值的差別很是大。經過密碼加鹽,能夠防止最初級的暴力破解,若是攻擊者事先不知道」鹽「值,破解的難度就會很是大。
var crypto = require('crypto');
function cryptPwd(password, salt) {
// 密碼「加鹽」
var saltPassword = password + ':' + salt;
console.log('原始密碼:%s', password);
console.log('加鹽後的密碼:%s', saltPassword);
// 加鹽密碼的md5值
var md5 = crypto.createHash('md5');
var result = md5.update(saltPassword).digest('hex');
console.log('加鹽密碼的md5值:%s', result);
}
cryptPwd('123456', 'abc');
// 輸出:
// 原始密碼:123456
// 加鹽後的密碼:123456:abc
// 加鹽密碼的md5值:51011af1892f59e74baf61f3d4389092
cryptPwd('123456', 'bcd');
// 輸出:
// 原始密碼:123456
// 加鹽後的密碼:123456:bcd
// 加鹽密碼的md5值:55a95bcb6bfbaef6906dbbd264ab4531複製代碼
經過密碼加鹽,密碼的安全性已經提升了很多。但其實上面的例子存在很多問題。
假設字符串拼接算法、鹽值已外泄,上面的代碼至少存在下面問題:
短鹽值自沒必要說,應該避免。對於爲何不該該使用固定鹽值,這裏須要多解釋一下。不少時候,咱們的鹽值是硬編碼到咱們的代碼裏的(好比配置文件),一旦壞人經過某種手段獲知了鹽值,那麼,只須要針對這串固定的鹽值進行暴力窮舉就好了。
好比上面的代碼,當你知道鹽值是abc
時,馬上就能猜到51011af1892f59e74baf61f3d4389092
對應的明文密碼是123456
。
那麼,該怎麼優化呢?答案是:隨機鹽值。
示例代碼以下。能夠看到,密碼一樣是123456,因爲採用了隨機鹽值,先後運算得出的結果是不一樣的。這樣帶來的好處是,多個用戶,一樣的密碼,攻擊者須要進行屢次運算纔可以徹底破解。一樣是純數字3位短鹽值,隨機鹽值破解所需的運算量,是固定鹽值的1000倍。
var crypto = require('crypto');
function getRandomSalt(){
return Math.random().toString().slice(2, 5);
}
function cryptPwd(password, salt) {
// 密碼「加鹽」
var saltPassword = password + ':' + salt;
console.log('原始密碼:%s', password);
console.log('加鹽後的密碼:%s', saltPassword);
// 加鹽密碼的md5值
var md5 = crypto.createHash('md5');
var result = md5.update(saltPassword).digest('hex');
console.log('加鹽密碼的md5值:%s', result);
}
var password = '123456';
cryptPwd('123456', getRandomSalt());
// 輸出:
// 原始密碼:123456
// 加鹽後的密碼:123456:498
// 加鹽密碼的md5值:af3b7d32cc2a254a6bf1ebdcfd700115
cryptPwd('123456', getRandomSalt());
// 輸出:
// 原始密碼:123456
// 加鹽後的密碼:123456:287
// 加鹽密碼的md5值:65d7dd044c2db64c5e658d947578d759複製代碼
簡單的說,就是兩段不一樣的字符串,通過MD5運算後,得出相同的結果。
網上有很多例子,這裏就不贅述,直接上例子,參考(這裏)[www.mscs.dal.ca/~selinger/m…]
function getHashResult(hexString){
// 轉成16進制,好比 0x4d 0xc9 ...
hexString = hexString.replace(/(\w{2,2})/g, '0x$1 ').trim();
// 轉成16進制數組,如 [0x4d, 0xc9, ...]
var arr = hexString.split(' ');
// 轉成對應的buffer,如:<Buffer 4d c9 ...>
var buff = Buffer.from(arr);
var crypto = require('crypto');
var hash = crypto.createHash('md5');
// 計算md5值
var result = hash.update(buff).digest('hex');
return result;
}
var str1 = 'd131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70';
var str2 = 'd131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70';
var result1 = getHashResult(str1);
var result2 = getHashResult(str2);
if(result1 === result2) {
console.log(`Got the same md5 result: ${result1}`);
}else{
console.log(`Not the same md5 result`);
}複製代碼
若有錯漏,敬請指出,歡迎多交流 :)
MD5碰撞的一些例子
www.jianshu.com/p/c9089fd5b…
MD5 Collision Demo
www.mscs.dal.ca/~selinger/m…
Free Password Hash Cracker
crackstation.net/