您好,登錄后才能下訂單哦!
本篇內容介紹了“數字指紋作用有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在大文件上傳的場景中,為了提高大文件上傳的用戶體驗,我們會支持斷點續傳。在上傳過程中,我們會對大文件進行分片處理,然后使用 md5 算法計算分片的哈希值,再把該分片的內容和其對應的哈希值一起提交到服務器。
當服務器接收到分片對應的哈希值時,會先查詢該哈希值是否已經存在。如果存在,則表示該分片已經上傳過。這時可以返回大文件已上傳的字節數,從而讓客戶端可以繼續上傳剩余的內容。
其實分片對應的哈希值也可以被稱為分片的 “數字指紋”,那么什么是 “數字指紋” 呢?要理解 “數字指紋”,我們需要先來了解一下什么是消息摘要算法。
一、什么是消息摘要算法
消息摘要算法是密碼學算法中非常重要的一個分支,它通過對所有數據提取指紋信息以實現數據簽名、數據完整性校驗等功能,由于其不可逆性,有時候會被用做敏感信息的加密。消息摘要算法也被稱為哈希(Hash)算法或散列算法。
任何消息經過散列函數處理后,都會獲得唯一的散列值,這一過程稱為 “消息摘要”,其散列值稱為 “數字指紋”,其算法自然就是 “消息摘要算法” 了。換句話說,如果其數字指紋一致,就說明其消息是一致的。
(圖片來源 —— https://zh.wikipedia.org/wiki/散列函數)
消息摘要算法不存在密鑰的管理與分發問題,適合于分布式網絡上使用。消息摘要算法主要應用在 “數字簽名” 領域,作為對明文的摘要算法。著名的摘要算法有 RSA 公司的 MD5 算法和 SHA-1 算法及其大量的變體。
1.1 消息摘要算法的特點
無論輸入的消息有多長,計算出來的消息摘要的長度總是固定的。例如應用 MD5 算法摘要的消息有 128 個比特位,用 SHA-1 算法摘要的消息最終有 160 個比特位的輸出,SHA-1的變體可以產生 192 個比特位和 256 個比特位的消息摘要。一般認為,摘要的最終輸出越長,該摘要算法就越安全。
消息摘要看起來是 “隨機的”。這些比特看上去是胡亂的雜湊在一起的,可以用大量的輸入來檢驗其輸出是否相同,一般,不同的輸入會有不同的輸出,而且輸出的摘要消息可以通過隨機性檢驗。一般地,只要輸入的消息不同,對其進行摘要以后產生的摘要消息也必不相同;但相同的輸入必會產生相同的輸出。
消息摘要函數是單向函數,即只能進行正向的信息摘要,而無法從摘要中恢復出任何的消息,甚至根本就找不到任何與原信息相關的信息。
好的摘要算法,沒有人能從中找到 “碰撞” 或者說極度難找到,雖然 “碰撞” 是肯定存在的(碰撞即不同的內容產生相同的摘要)。
1.2 消息摘要算法的家譜
消息摘要算法主要分為三大類:MD(Message Digest,消息摘要算法)、SHA-1(Secure Hash Algorithm,安全散列算法)和 MAC(Message Authentication Code,消息認證碼算法)。
MD 系列算法包括 MD2、MD4 和 MD5 共 3 種算法;SHA 算法主要包括其代表算法 SHA-1 和 SHA-1 算法的變種 SHA-2 系列算法(包含 SHA-224、SHA-256、SHA-384 和 SHA-512);MAC 算法綜合了上述兩種算法,主要包括 HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384 和 HmacSHA512 算法。
盡管上述內容列舉了各種消息摘要算法,但仍不能滿足應用需要。基于這些消息摘要算法,又衍生出了 RipeMD 系列(包含 RipeMD128、RipeMD160、RipeMD256、RipeMD320)、Tiger、GOST3411 和 Whirlpool 算法。
對于大多數前端開發者來說,接觸得比較多的應該是 MD5 算法。所以,接下來阿寶哥將重點介紹 MD5 算法。
二、什么是 MD5 算法
MD5(Message Digest Algorithm 5,消息摘要算法版本5),它由 MD2、MD3、MD4 發展而來,由 Ron Rivest(RSA 公司)在 1992 年提出,目前被廣泛應用于數據完整性校驗、數據(消息)摘要、數據簽名等。MD2、MD4、MD5 都產生 16 字節(128 位)的校驗值,一般用 32 位十六進制數表示。MD2 的算法較慢但相對安全,MD4 速度很快,但安全性下降,MD5 比 MD4 更安全、速度更快。
隨著計算機技術的發展和計算水平的不斷提高,MD5 算法暴露出來的漏洞也越來越多。1996 年后被證實存在弱點,可以被加以破解,對于需要高度安全性的數據,專家一般建議改用其他算法,如 SHA-2。2004 年,證實 MD5 算法無法防止碰撞(collision),因此不適用于安全性認證,如 SSL 公開密鑰認證或是數字簽名等用途。
2.1 MD5 特點
穩定、運算速度快。
壓縮性:輸入任意長度的數據,輸出長度固定(128 比特位)。
運算不可逆:已知運算結果的情況下,無法通過通過逆運算得到原始字符串。
高度離散:輸入的微小變化,可導致運算結果差異巨大。
2.2 MD5 散列
128 位的 MD5 散列在大多數情況下會被表示為 32 位十六進制數字。以下是一個 43 位長的僅 ASCII 字母列的 MD5 散列:
MD5("The quick brown fox jumps over the lazy dog") = 9e107d9d372bb6826bd81d3542a419d6
即使在原文中作一個小變化(比如把 dog 改為 cog,只改變一個字符)其散列也會發生巨大的變化:
MD5("The quick brown fox jumps over the lazy cog") = 1055d3e698d289f2af8663725127bd4b
接著我們再來舉幾個 MD5 散列的例子:
MD5("") -> d41d8cd98f00b204e9800998ecf8427e MD5("semlinker") -> 688881f1c8aa6ffd3fcec471e0391e4d MD5("kakuqo") -> e18c3c4dd05aef020946e6afbf9e04ef
三、MD5 算法的用途
3.1 防止被篡改
3.1.1 文件分發防篡改
在互聯網上分發軟件安裝包時,出于安全性考慮,為了防止軟件被篡改,比如在軟件安裝程序中添加木馬程序。軟件開發者通常會使用消息摘要算法,比如 MD5 算法產生一個與文件匹配的數字指紋,這樣接收者在接收到文件后,就可以利用一些現成的工具來檢查文件完整性。
(圖片來源 —— https://en.wikipedia.org/wiki/MD5)
這里我們來舉一個實際的例子,下圖是 MySQL Community Server 8.0.19 版本的下載頁,該下載頁通過 MD5 算法分別計算出不同軟件包的數字指紋,具體如下圖所示:
(圖片來源 —— https://dev.mysql.com/downloads/mysql/)
當用戶從官網上下載到對應的安裝包之后,可以利用一些 MD5 校驗工具對已下載的文件進行校驗,然后比對最終的 MD5 數字指紋。若結果與官網公布的數字指紋一致,則表示該安裝包未經過任何修改是安全的,基本可以放心安裝。
3.1.2 消息傳輸防篡改
假設在網絡上你需要發送電子文檔給你的朋友,在文件發送前,先對文檔的內容進行 MD5 運算,得出該電子文檔的 “數字指紋”,并把該 “數字指紋” 隨電子文檔一同發送給對方。當對方接收到電子文檔之后,也使用 MD5 算法對文檔的內容進行哈希運算,在運算完成后也會得到一個對應 “數字指紋”,當該指紋與你所發送文檔的 “數字指紋” 一致時,表示文檔在傳輸過程中未被篡改。
3.2 信息保密
在互聯網初期很多網站在數據庫中以明文的形式存儲用戶的密碼,這存在很大的安全隱患,比如數據庫被黑客入侵,從而導致網站用戶信息的泄露。針對這個問題,一種解決方案是在保存用戶密碼時,不再使用明文,而是使用消息摘要算法,比如 MD5 算法對明文密碼進行哈希運算,然后把運算的結果保存到數據庫中。使用上述方案,避免了在數據庫中以明文方式保存密碼,提高了系統的安全性,不過這種方案并不安全,后面我們會詳細分析。
當用戶登錄時,登錄系統對用戶輸入的密碼執行 MD5 哈希運算,然后再使用用戶 ID 和密碼對應的 MD5 “數字指紋” 進行用戶認證。若認證通過,則當前的用戶可以正常登錄系統。用戶密碼經過 MD5 哈希運算后存儲的方案至少有兩個好處:
防內部攻擊:因為在數據庫中不會以明文的方式保存密碼,因此可以避免系統中用戶的密碼被具有系統管理員權限的人員知道。
防外部攻擊:網站數據庫被黑客入侵,黑客只能獲取經過 MD5 運算后的密碼,而不是用戶的明文密碼。
四、MD5 算法使用示例
在 Node.js 環境中,我們可以使用 crypto 原生模塊提供的 md5 實現,當然也可以使用主流的 MD5 第三方庫,比如 md5 這個可以同時運行在服務端和客戶端的第三方庫。在介紹具體使用前,我們需要提前安裝 md5 這個第三方庫,具體安裝方式如下:
$ npm install md5 --save
4.1 crypto 模塊使用示例
const crypto = require("crypto"); const msg = "阿寶哥"; function md5(data) { const hash = crypto.createHash("md5"); return hash.update(data).digest("hex"); } console.log("Node.js Crypto MD5: " + msg + " -> " + md5(msg));
4.2 MD5 庫使用示例
const md5 = require('md5'); const msg = "阿寶哥"; console.log("MD5 Lib MD5: " + msg + " -> " + md5(msg));
以上示例代碼正常運行后,在控制臺中會輸出以下結果:
Node.js Crypto MD5: 阿寶哥 -> 8eec7fcf817f7340b791b32ecdbed570 MD5 Lib MD5: 阿寶哥 -> 8eec7fcf817f7340b791b32ecdbed570
五、MD5 算法的缺陷
哈希碰撞是指不同的輸入卻產生了相同的輸出,好的哈希算法,應該沒有人能從中找到 “碰撞” 或者說極度難找到,雖然 “碰撞” 是肯定存在的。
2005 年山東大學的王小云教授發布算法可以輕易構造 MD5 碰撞實例,此后 2007 年,有國外學者在王小云教授算法的基礎上,提出了更進一步的 MD5 前綴碰撞構造算法 “chosen prefix collision”,此后還有專家陸續提供了MD5 碰撞構造的開源的庫。
2009 年,中國科學院的謝濤和馮登國僅用了 220.96 的碰撞算法復雜度,破解了 MD5 的碰撞抵抗,該攻擊在普通計算機上運行只需要數秒鐘。
MD5 碰撞很容易構造,基于 MD5 來驗證數據完整性已不可靠,考慮到近期谷歌已成功構造了 SHA-1(英語:Secure Hash Algorithm 1,中文名:安全散列算法1)的碰撞實例,對于數據完整性,應使用 SHA256 或更強的算法代替。
5.1 MD5 碰撞樣本
下面我們先來看一下 MD5 碰撞的樣本:
5.1.1 HEX(十六進制)樣本 A1
4dc968ff0ee35c209572d4777b721587 d36fa7b21bdc56b74a3dc0783e7b9518 afbfa200a8284bf36e8e4b55b35f4275 93d849676da0d1555d8360fb5f07fea2
5.1.2 HEX(十六進制)樣本 A2
4dc968ff0ee35c209572d4777b721587 d36fa7b21bdc56b74a3dc0783e7b9518 afbfa202a8284bf36e8e4b55b35f4275 93d849676da0d1d55d8360fb5f07fea2
兩個樣本之間的差異如下圖所示:
5.2 驗證 MD5 碰撞
下面我們來使用 Node.js 實際驗證一下樣本 A1 和樣本 A2 經過 MD5 運算后輸出的結果是否一致:
5.2.1 設置樣本數據
let sample1 = ` 4dc968ff0ee35c209572d4777b721587 d36fa7b21bdc56b74a3dc0783e7b9518 afbfa200a8284bf36e8e4b55b35f4275 93d849676da0d1555d8360fb5f07fea2`; let sample2 = ` 4dc968ff0ee35c209572d4777b721587 d36fa7b21bdc56b74a3dc0783e7b9518 afbfa202a8284bf36e8e4b55b35f4275 93d849676da0d1d55d8360fb5f07fea2 `;
5.2.2 定義 getHashResult 方法
function getHashResult(hexString) { const hash = crypto.createHash("md5"); const buffer = Buffer.from(hexString.replace(/\s/g, ""), "hex"); return hash.update(buffer).digest("hex"); }
5.2.3 執行碰撞檢測
let sample1Md5 = getHashResult(sample1); let sample2Md5 = getHashResult(sample2); if (sample1Md5 === sample2Md5) { console.log(`出現 MD5 碰撞: ${sample1Md5}`); } else { console.log(`未出現 MD5 碰撞`); }
以上代碼成功運行后,在控制臺中會輸出以下結果:
出現 MD5 碰撞: 008ee33a9d58b51cfeb425b0959121c9
如果你對其它 MD5 碰撞的樣本感興趣,可以查看 MD5碰撞的一些例子 這篇文章。由于基于 MD5 來驗證數據完整性不太可靠,所以 Node.js 使用了 SHA256 算法來確保數據的完整性。
(圖片來源 —— https://nodejs.org/download/release/v15.6.0/SHASUMS256.txt.asc)
六、MD5 密碼安全性
6.1 MD5 密文反向查詢
前面我們已經提到通過對用戶密碼進行 MD5 運算可以提高系統的安全性。但實際上,這樣的安全性還是不高。為什么呢?因為只要輸入相同就會產生相同的輸出。接下來我們來舉一個示例,字符串 123456789 是一個很常用的密碼,它經過 MD5 運算后會生成一個對應的哈希值:
MD5("123456789") -> 25f9e794323b453885f5181f1b624d0b
由于輸入相同就會產生相同的結果,因此攻擊者就可以根據哈希結果反推輸入。其中一種常見的破解方式就是使用彩虹表。彩虹表是一個用于加密散列函數逆運算的預先計算好的表,常用于破解加密過的密碼散列。 查找表常常用于包含有限字符固定長度純文本密碼的加密。這是以空間換時間的典型實踐,在每一次嘗試都計算的暴力破解中使用更少的計算能力和更多的儲存空間,但卻比簡單的每個輸入一條散列的翻查表使用更少的儲存空間和更多的計算性能。
目前網上某些站點,比如 cmd5.com 已經為我們提供了 MD5 密文的反向查詢服務,我們以 MD5("123456789") 生成的結果,做個簡單的驗證,具體如下圖所示:
因為 123456789 是很常見的密碼,因此該網站能夠反向得出正確結果那就不足為奇了。以下是 cmd5 網站的站點說明,大家可以參考一下,感興趣的小伙伴可以親自驗證一下。
現在我們已經知道如果用戶的密碼相同 MD5 的值就會一樣,通過一些 MD5 密文的反向查詢網站,密碼大概率會被解析出來,這樣使用相同密碼的用戶就會受到影響。那么該問題如何解決呢?答案是密碼加鹽。
6.2 密碼加鹽
鹽(Salt),在密碼學中,是指在散列之前將散列內容(例如:密碼)的任意固定位置插入特定的字符串。這個在散列中加入字符串的方式稱為 “加鹽”。其作用是讓加鹽后的散列結果和沒有加鹽的結果不相同,在不同的應用情景中,這個處理可以增加額外的安全性。
在大部分情況,鹽是不需要保密的。鹽可以是隨機產生的字符串,其插入的位置可以也是隨意而定。如果這個散列結果在將來需要進行驗證(例如:驗證用戶輸入的密碼),則需要將已使用的鹽記錄下來。為了便于理解,我們來舉個簡單的示例。
6.2.1 Node.js MD5 加鹽示例
const crypto = require("crypto"); function cryptPwd(password, salt) { const saltPassword = password + ":" + salt; console.log("原始密碼:%s", password); console.log("加鹽后的密碼:%s", saltPassword); const md5 = crypto.createHash("md5"); const result = md5.update(saltPassword).digest("hex"); console.log("加鹽密碼的md5值:%s", result); } cryptPwd("123456789","exe"); cryptPwd("123456789","eft");
以上示例代碼正常運行后,在控制臺中會輸出以下結果:
原始密碼:123456789 加鹽后的密碼:123456789:exe 加鹽密碼的md5值:3328003d9f786897e0749f349af490ca 原始密碼:123456789 加鹽后的密碼:123456789:eft 加鹽密碼的md5值:3c45dd21ba03e8216d56dce8fe5ebabf
通過觀察以上結果,我們發現原始密碼一致,但使用的鹽值不一樣,最終生成的 MD5 哈希值差異也比較大。此外為了提高破解的難度,我們可以隨機生成鹽值并且提高鹽值的長度。
6.3 bcrypt
哈希加鹽的方式確實能夠增加攻擊者的成本,但是今天來看還遠遠不夠,我們需要一種更加安全的方式來存儲用戶的密碼,這也就是今天被廣泛使用的 bcrypt。
bcrypt 是一個由 Niels Provos 以及 David Mazières 根據 Blowfish 加密算法所設計的密碼散列函數,于 1999 年在 USENIX 中展示。bcrypt 這一算法就是為哈希密碼而專門設計的,所以它是一個執行相對較慢的算法,這也就能夠減少攻擊者每秒能夠處理的密碼數量,從而避免攻擊者的字典攻擊。實現中 bcrypt 會使用一個加鹽的流程以防御彩虹表攻擊,同時 bcrypt 還是適應性函數,它可以借由增加迭代之次數來抵御日益增進的電腦運算能力透過暴力法破解。
由 bcrypt 加密的文件可在所有支持的操作系統和處理器上進行轉移。它的口令必須是 8 至 56 個字符,并將在內部被轉化為 448 位的密鑰。然而,所提供的所有字符都具有十分重要的意義。密碼越強大,您的數據就越安全。
下面我們以 Node.js 平臺的 bcryptjs 為例,介紹一下如何使用 bcrypt 算法來處理用戶的密碼。首先我們需要先安裝 bcryptjs:
$ npm install bcryptjs --save
6.3.1 使用 bcryptjs 處理密碼
const bcrypt = require("bcryptjs"); const password = "123456789"; const saltRounds = 10; async function bcryptHash(str, saltRounds) { let hashedResult; try { const salt = await bcrypt.genSalt(saltRounds); hashedResult = await bcrypt.hash(str, salt); } catch (error) { throw error; } return hashedResult; } bcryptHash(password, saltRounds).then(console.log);
以上示例代碼正常運行后,在控制臺中會輸出以下結果:
$2a$10$O1SrEy3KsgN0NQdQjaSU6OxjxDo0jf.j/e2goSwSEu4esz9i58dRm
很明顯密碼 123456789 經過 bcrypt 的哈希運算后,得到了一串讀不懂的 “亂碼”。這里我們已經完成第一步,即用戶登錄密碼的加密。下一步我們要實現登錄密碼的比對,即要保證用戶輸入正確的密碼后,能正常登錄系統。
6.3.2 使用 bcryptjs 校驗密碼
async function bcryptCompare(str, hashed) { let isMatch; try { isMatc = await bcrypt.compare(str, hashed); } catch (error) { throw error; } return isMatch; } bcryptCompare( "123456789", "$2a$10$O1SrEy3KsgN0NQdQjaSU6OxjxDo0jf.j/e2goSwSEu4esz9i58dRm" ).then(console.log); bcryptCompare( "123456", "$2a$10$O1SrEy3KsgN0NQdQjaSU6OxjxDo0jf.j/e2goSwSEu4esz9i58dRm" ).then(console.log);
以上示例代碼正常運行后,在控制臺中會輸出以下結果:
true false
因為我們的原始密碼是 123456789,很明顯與 123456 并不匹配,所以會輸出以上的匹配結果。
“數字指紋作用有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。