怎樣的 Hash 演算法能對抗硬體破解
前言
用過暴力破解工具 hashcat 的都知道,這款軟體的強大之處在於它能充分利用 GPU 計算,比起 CPU 要快很多。所以在破解諸如 WiFi 握手包、資料庫中的口令 Hash 值時,能大幅提高計算效率。
當然 GPU 仍屬於通用硬體,顯然還不是最優化的。要是為特定的演算法打造特定的硬體,效率更是高出幾個量級。比特幣礦機就是很好的例子。
硬體的仍在不斷進步,系統安全等級若不提高,暴力破解將會越來越容易。因此,一種能抵抗「硬體破解」的 Hash 演算法,顯得很有必要。
時間成本
在探討如何對抗硬體之前,先來講解過去是如何對抗「暴力破解」的。
一些經典的 Hash 演算法,例如 MD5、SHA256 等,計算速度是非常快的。如果口令 Hash 用了這類函式,將來攻擊者跑字典時,可達到非常高的速度。那些強度不高的口令,很容易被破解。
為了緩解這種狀況,密碼學家引入了「拉伸」的概念:反覆 Hash 多次,從而增加計算時間。
例如 PBKDF2 演算法就運用了這種思想。它的原理很簡單,對指定函式 F 反覆進行 N 次:
function PBKDF2(F, ..., N)
...
for i = 0 to N
...
x = F(x, ...)
...
...
return x
這樣就能靈活設定 Hash 的時間成本了。例如設定 10000,對開發者來說,只是多了幾十毫秒的計算;但對於攻擊者,破解速度就降低了一萬倍!
時間成本侷限性
PBKDF2 確實有很大的效果,但對於硬體破解,卻無任何對抗措施。
因為 PBKDF2 只是對原函式簡單封裝,多執行幾次而已。如果原函式不能對抗硬體,那麼套一層 PBKDF2 同樣也不能。
例如 WiFi 的 WPA2 協議,就是讓 HMAC-SHA1
重複執行 4096 次:
DK = PBKDF2(HMAC−SHA1, Password, SSID, 4096, ...)
雖然相比單次 Hash 要慢上幾千倍,但這並不妨礙硬體破解。
硬體依然可發揮其「高併發」優勢,讓每個執行緒分別計算不同口令的 PBKDF2:
執行緒 | 計算 |
---|---|
1 | PBKDF2(..., "12345678", 4096, ...) == KEY |
2 | PBKDF2(..., "00000000", 4096, ...) == KEY |
... | ... |
100 | PBKDF2(..., "88888888", 4096, ...) == KEY |
雖然耗時確實增加了很多倍,但並沒有影響到硬體的發揮。同樣的破解,效率仍然遠高於 CPU。
所以,時間成本並不能抵抗硬體破解。
空間成本
單論計算效能,硬體是非常逆天的,但再綜合一些其他因素,或許就未必那麼強大了。
假如某個硬體可開啟 100 個執行緒同時破解,但總記憶體卻只有 100M —— 這顯然是個很大的短板。
如果有種 PBKDF 演算法空間複雜度為 2M,那將會有一半的執行緒,因記憶體不足而無法執行!
若再極端些,將空間複雜度提高到 100M,那麼整個硬體只能開啟 1 個執行緒,99% 的算力都無法得到發揮!
這樣,即使硬體的計算效能再強勁,也終將卡在記憶體這個瓶頸上。
不過,怎樣才能讓演算法消耗這麼多記憶體,同時又不能被輕易繞過?這裡舉個簡單的例子:
function MemoryHard(..., M)
int space[M]
for i = 0 .. 10000
x = Hash(x, ...)
space[int(x) % M] ^= int(x)
return Hash(space)
當然這個例子是隨意寫的,並不嚴謹。但主要思想是:
引入了空間成本 M,並申請相應的記憶體
利用經典 Hash 函式的結果,作為陣列索引,對記憶體進行讀寫
每次記憶體讀寫,都會影響到最終結果
由於 Hash 函式的結果是不可預測的,因此事先無法知道哪些位置會被訪問。只有準備充足的記憶體,才能達到 O(1) 的訪問速度。
攻擊者要想達到同樣的速度,就不得不花費同樣多的記憶體!
時空權衡
通常硬體的「計算資源」要比「儲存資源」充足得多,因此可考慮「時間換空間」的策略 —— 使用更復雜的儲存管理機制,從而減少空間分配,這樣就能開啟更多的執行緒。
比如犧牲 40% 的速度,換取 50% 的空間:
方案 | 可用記憶體 | 空間分配 | 可用執行緒 | 單執行緒速度 | 總速度 |
---|---|---|---|---|---|
A | 1000M | 100M | 10 / 100 | 10 hash/s | 100 hash/s |
B | 1000M | 50M | 20 / 100 | 6 hash/s | 120 hash/s |
由於空間成本是之前的一半,因此可多啟動一倍的執行緒。算上折損,最終速度仍增加了 20%。
當然,如果 效能折損比例 > 空間壓縮比例,這個方案就沒有意義了。
訪問瓶頸
事實上,記憶體除了容量外,訪問頻率也是有限制的。
就記憶體本身而言,每秒讀寫次數是有上限的。其次,計算單元和記憶體之間的互動,更是一大瓶頸。
像 MD5、SHA256 這類 Hash 函式,空間複雜度非常低。硬體破解時,每個計算單元光靠自身的暫存器以及快取記憶體,就差不多夠用了,很少需要訪問記憶體。
但對於 Memory-Hard 函式,就沒那麼順利了。它不僅很佔記憶體,而且還十分頻繁地「隨機訪問」記憶體,因此很難命中快取記憶體。這使得每次訪問,幾乎都會和記憶體進行互動,從而佔用大量頻寬。
如果有多個計算單元頻繁訪問,那麼記憶體頻寬就會成為瓶頸。這樣,也能起到抑制併發的效果!
例如 bcrypt 演算法就運用了類似思想,它在計算過程中頻繁訪問 4KB 的記憶體空間,從而消耗頻寬資源。
不過隨著硬體發展,bcrypt 的優勢也在逐漸降低。為了能更靈活地設定記憶體大小,scrypt 演算法出現了 —— 它既有時間成本,還有空間成本,這樣就能更持久地對抗。
當然,空間成本也不是絕對有效的。如果攻擊者不惜代價,製造出儲存「容量」和「頻寬」都很充足的硬體裝置,那麼仍能高效地進行破解。
並行維度
十幾年來,記憶體容量翻了好幾翻,但 CPU 主頻卻沒有很大提升。由於受到物理因素的制約,主頻已很難提升,只能朝著多核發展。
然而像 PBKDF2 這樣的演算法,卻只能使用單執行緒計算 —— 因為它每次 Hash 都依賴上一次的 Hash 結果。這種序列的模式,是無法拆解成多個任務的,也就無法享受多執行緒的優勢。
這就意味著 —— 時間成本,終將達到一個瓶頸!
對此,多執行緒真的無能為力嗎?
儘管單次 PBKDF 不能被拆解,但可以要求多次 PBKDF,並且互相沒有依賴。這樣多執行緒就能派上用場了。
例如我們對 PBKDF 進行封裝,要求執行 4 次完全獨立的計算,最後再將結果融合到一起:
function Parall(Password, Salt, ...)
-- 該部分可被並行 --
for i = 0 .. 4
DK[i] = PBKDF(Password, Salt + i, ...)
------------------
return Hash(DK)
這樣,我們即可開啟 4 個執行緒,同時計算這 4 個 PBKDF。
現在就能用 1 秒的時間,獲得之前 4 秒的強度!攻擊者破解時,成本就增加了 4 倍。
如今主流的口令 Hash 函式都支援「並行維度」。例如 scrypt 以及更先進的 argon2,都可通過引數 p 設定。
執行緒開銷
現實中,「執行緒數」未必要和「並行維度」一樣多,因為還得考慮「空間成本」。
假設上述的 PBKDF 空間成本有 512MB,如果開啟 4 個執行緒,就得佔用 2GB 的記憶體!若使用者只有 1.5 GB 的空閒記憶體,還不如只開 2 個執行緒,反而會更順暢。
當然,也可以開 3 個執行緒,但這樣會更快嗎?顯然不會!
因為 4 個任務分給 3 個執行緒,總有一個執行緒得做兩份,所以最終用時並沒有縮短。反而增加了執行緒建立、記憶體申請等開銷。
大家可體會下 時空成本(N)、並行維度(P)、執行緒數(Thread)對計算的影響。
小結
到此,我們講解了 3 個對抗破解的因素:
時間成本(迭代次數)
空間成本(記憶體容量、頻寬)
並行維度(多執行緒資源)
或許你已感悟到這其中的理念 —— 讓 Hash 演算法牽涉更多的硬體能力。這樣,只有綜合性能高的硬體,才能順利執行;專為某個功能打造的硬體,就會出現瓶頸!
照這個思路,我們也可發揮想象:假如有個演算法使用了不少條件分支指令,而 CPU 正好擁有強大的分支預測功能。這樣該演算法在 CPU 上執行時,就能獲得很高的效能;而在其他精簡過的硬體上,就沒有這麼好的效果了。
當然這裡純屬想象,自創密碼學演算法是不推薦的。現實中還是得用更權威的演算法,例如 argon2、scrypt 等。
應用
本文提到的對抗方案,都是從硬體消耗上進行的。不過,這樣傷敵一千也會自損八百。
假如伺服器每 Hash 一次口令,就得花 1 秒時間加 1GB 記憶體,那麼一旦有幾十個人同時訪問,系統可能就支撐不住了。
有什麼辦法,既能使用高成本的 Hash,又不耗費伺服器資源?事實上,口令 Hash 完全可以在客戶端計算:
DK = Client_PBKDF(Password, Username, Cost ...)
因為口令與 DK 的對應關係是唯一的。賬號註冊時,提交的就是 DK;登入時,如果提交的 DK 相同,也就證明口令是相同的。
所以客戶端無需提供原始口令,服務端也能認證。使用這種方案,還能進一步減少口令洩露的環節,例如網路被竊聽、服務端惡意程式等。
當然,服務端收到 DK 後,還不能立即儲存。因為萬一 DK 洩露了,攻擊者還是能用它登上使用者的賬號,儘管不知道口令。
因此,服務端需對 DK 再進行 Hash 處理。
不過這一次,只需快速的 Hash 函式即可。因為 DK 是無規律的資料(熵很高),無法通過跑字典還原,所以用簡單的 Hash 就能保護。
這樣,伺服器只需極小的計算開銷,就能實現高強度的口令安全了!
將來即使被拖庫,攻擊者也只能使用如下 Hash 函式跑字典:
f(x) => server_hash( client_hash(x) )
因為其中用到了 client_hash,所以這個最終函式同樣能對抗硬體破解!
用以模擬被入侵的場景。大家可嘗試破解其中弱口令,看看需要多少時間。
相關推薦
怎樣的 Hash 演算法能對抗硬體破解
前言 用過暴力破解工具 hashcat 的都知道,這款軟體的強大之處在於它能充分利用 GPU 計算,比起 CPU 要快很多。所以在破解諸如 WiFi 握手包、資料庫中的口令 Hash 值時,能大幅提高計算效率。 當然 GPU 仍屬於通用硬體,顯然還不是最優化的。要是為特定的演算法打造特定的硬體,效率更是高出幾
Python演算法筆試題目,破解Hash值,N進製法
Find the string whichhas this hash: 25267566250558 The string has length8. Characters can befrom: c,e,i,a,r,w,u,s,p The hash functionworks like
2018最新淘寶面試出爐:分散式鎖+叢集+一致Hash演算法+底層技術原理
是需要掌握牢固,重點會問HashMap等集合類,以及多執行緒、執行緒池等。 原文連結:https://blog.csdn.net/SpringJavaMyBatis/article/details/83415696 &n
關於什麼是一致性hash演算法
當需要分散式快取的時候,通過key的hash值分散資料儲存hash(n)%快取伺服器臺數,同時也可以快速查詢資料而不用遍歷所有的伺服器。如下圖: 但是這樣,當業務拓展想要增加一臺伺服器的話,要麼快取伺服器資料全部需要重新計算儲存 -----hash(n)%5 。 要麼需要遍歷所有快取伺服器。不夠靈活。
Hash演算法的講解
散列表,又叫雜湊表,它是基於快速存取的角度設計的,也是一種典型的“空間換時間”的做法。顧名思義,該資料結構可以理解為一個線性表,但是其中的元素不是緊密排列的,而是可能存在空隙。 散列表
一致性Hash演算法的深入理解
總結: 1、使用一致性Hash演算法,儘管增強了系統的伸縮性,但是也有可能導致負載分佈不均勻,解決辦法就是使用虛擬節點代替真實節點, 2、Hash演算法的選擇上,首先我們考慮簡單的String.HashCode()方法,這個演算法的缺點是,相似的字串如N1(10.0.
Java中的HashCode 1 之hash演算法基本原理
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
學習筆記(六):使用隨機森林演算法檢測FTP暴力破解
1.資料蒐集: 載入ADFA-LD中正常資料 定義遍歷目錄下檔案 從攻擊資料中篩選出和FTP暴力破解有關的資料 &n
學習筆記(五):使用決策樹演算法檢測POP3暴力破解
1.資料蒐集 載入KDD 99中的資料: def load_kdd99(filename): x=[] with open(filename) asf: for line in f: line=line.st
logging日誌模組,re正則表示式模組,hashlib hash演算法相關的庫,
logging: 功能完善的日誌模組 import logging #日誌的級別 logging.debug("這是個除錯資訊")#級別10 #常規資訊 logging.info("常規資訊")#20 #警告資訊 logging.warning("警告
memcache客戶端實現叢集之一致性hash演算法
一致性雜湊的演算法把取餘演算法的等於號來選擇mem伺服器變成了大於號來選擇mem伺服器,這應該是才是關鍵,可以使一個鍵的mem伺服器落點變成是動態選擇(一個伺服器down掉然後選擇crc32(key)後大於這個伺服器的落點....) 新增虛擬節點,虛擬節點其實還是原來那幾臺伺服器,每個虛擬節
linux核心netfilter連線跟蹤的hash演算法
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
分散式演算法(一致性Hash演算法)
一、分散式演算法 在做伺服器負載均衡時候可供選擇的負載均衡的演算法有很多,包括: 輪循演算法(Round Robin)、雜湊演算法(HASH)、最少連線演算法(Least Connection)、響應速度演算法(Response Time)、加權法
【轉】【java原始碼分析】Map中的hash演算法分析
全網把Map中的hash()分析的最透徹的文章,別無二家。 2018年05月09日 09:08:08 閱讀數:957 你知道HashMap中hash方法的具體實現嗎?你知道HashTable、ConcurrentHashMap中hash方法
HDU 1686 Oulipo (hash演算法)
Oulipo Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2553
一致性hash演算法程式碼實現
什麼是一致性hash 一致性雜湊演算法(Consistent Hashing Algorithm)是一種分散式演算法,常用於負載均衡。Memcached client也選擇這種演算法,解決將key-value均勻分配到眾多Memcached server上的問題。它可以取代傳統的取模操作
HashMap中的hash演算法總結
前言 演算法一直是我的弱項,然而面試中基本是必考的專案,剛好上次看到一個HashMap的面試題,今天也來學習下 HashMap中的hash演算法是如何實現的。 數學知識回顧 << : 左移運算子,num << 1,相當於num乘以2 低位補0 舉例:3 <<
如果目標資料是一段記憶體區的話,該使用什麼HASH演算法比較合適?
如果目標資料是一段記憶體區的話,該使用什麼HASH演算法比較合適? 演算法的選擇標準: 儘量在不同的輸入的情況下產生在合法區間的離散,不重複的輸出。 有兩種計算方法比較容易產生離散的輸出: 1.異或運算 2.區域性移位運算
Java架構/一致性Hash演算法在資料庫分表中的實踐
最近有一個專案,其中某個功能單表資料在可預估的未來達到了億級,初步估算在90億左右。與同事詳細討論後,決定採用一致性Hash演算法來完成資料庫的自動擴容和資料遷移。整個程式細節由我同事完成,我只是將其理解併成文,供有相同問題的同行參考。 參看此文的兄弟,預設各位已經熟悉一致性hash
一致性Hash演算法在資料庫分表中的實踐
最近有一個專案,其中某個功能單表資料在可預估的未來達到了億級,初步估算在90億左右。與同事詳細討論後,決定採用一致性Hash演算法來完成資料庫的自動擴容和資料遷移。整個程式細節由我同事完成,我只是將其理解併成文,供有相同問題的同行參考。 參看此文的兄弟,預設各位已經熟悉一致性hash演算法了。此文僅僅闡述程式