密碼密鑰硬編碼檢查
摘要:本文重點講述通過靜態檢查工具有效的防止密碼密鑰的泄露。
本文分享自華為雲社區《密碼密鑰硬編碼檢查》,作者: Uncle_Tom。
Verizon《2022數據泄露調查報告(DBIR)》指出,61%的數據泄露涉及憑證數據,憑證是犯罪分子最喜歡的數據類型,就像披著羊皮的狼一樣,它們的行為在攻擊之前顯得無害。憑證的泄露是資訊泄露的主要途徑,內部員工操作不規範、沒有養成良好的工作行為習慣以及疏忽大意等已成為多起嚴重網路安全事件發生的根本原因。本文重點講述通過靜態檢查工具有效的防止密碼密鑰的泄露。
1. 密鑰的重要性
1.1. 看風
我們還是先說故事。話說2006年有一個柳雲龍的電視連續劇《暗算》分為三部曲《聽風》、《看風》、《捕風》。很有意思的是三個故事裡都有個「風」字。你看過風嗎?我沒看到,只看見樹葉飄動,才知道風來過。風,來無影,去無蹤,無孔不入,又無處不在。三部曲分別對應:偵聽、破譯和諜報。故事描述了老一輩情報人員(一群與風打交道的人),與敵鬥智斗勇的故事,信仰的力量讓他們無所畏懼、勇往直前,看得人蕩氣迴腸,催人淚下。
由於平時很少看電視,去年正在為一個演算法發愁,偶然看到這個劇,感同身受,便一口氣看完了,裡面一些對加密演算法邏輯的一些看法,還是給了當時的我很大的啟發,讓我完成了當時的演算法,還特意發了微博紀念。 個人比較喜歡《看風》破譯密碼這個章節,裡面有句經典的台詞:風是看不見的,破譯密碼就是看見了風,密碼是兵器,是兵器中的暗器。
1.2. 保密通訊模型
- 保密通訊模型
密碼學有一個重要的產物——密鑰。密鑰作為資訊在傳播時用來加密的工具起著非常重要的作用。主流的六種密碼技術,就是圍繞密鑰展開的:對稱密碼、非對稱密碼、單向散列函數、消息認證碼、數字簽名、偽隨機數生成器。
1.3. 密鑰的重要性
- 古典密碼學(1949年之前)
數據的安全主要是基於演算法的保密。
送你一首小詩:
我畫藍江水悠悠,
愛晚亭上楓葉愁。
秋月溶溶照佛寺,
香煙裊裊繞經樓。
如果不告訴你這是首藏頭詩,你還真的很難猜到唐伯虎對秋香的表白:「我愛秋香」。 藏頭詩就是加密演算法的一種。
由於西方文字是由字母組成,字母又是有序的字符集。所以在對文字加密時,密碼演算法很容易想到採用:替代密碼、置換密碼或替代與置換密碼的組合,來完成完成資訊的加密。公元前1世紀古羅馬的凱撒密碼,就是對文字中的字母,採用它在字母表中之後的第k個字母來代換。從而變成密文。在解密時,反向再移動k個字母進行還原。
這個時代將密碼發展到巔峰的,要算二戰時期德軍用機械實現的轉輪機(RotorMachine)ENIGMA密碼,很多的諜戰片都是圍繞這個機器展開的。
這個時期的密碼存在很多的問題:
- 不適合大規模生產
- 不適合較大的或者人員變動較大的組織
- 用戶無法了解演算法的安全性
奧古斯特·柯克霍夫在19世紀提出了密碼學上的柯克霍夫原則(Kerckhoffs』s principle)由:即使密碼系統的任何細節已為人悉知,只要密鑰(key)未泄漏,它也應是安全的。 這個原則指導了以後的密碼學演算法的發展。
- 近代密碼學(1949-1975)
數據的安全基於密鑰,而不是演算法的保密。
香農在20世紀40年代末發表的一系列論文,特別是1949年的《保密系統通訊理論》,把密碼學推向了基於資訊理論的科學軌道。
這階段的發展主要是對稱加密演算法。對稱加密是發送方使用某種公開的演算法使用密鑰對明文進行加密,接收方使用之前發送方給予的密鑰對密文進行解密得到明文。近代密碼發展中一個重要突破是「數據加密標準」(DES)的出現。
- 現代密碼學(1976-)
公鑰密碼使得發送端和接收端無密鑰傳輸的保密通訊成為可能。
1976 年 Diffie 和 Hellman 的公鑰密碼的思想提出,標誌著現代密碼學的誕生。這是密碼學發展史上具有里程碑意義的大事件,自此提出了許多種公鑰密碼體制 ,如基於分解大整數的困難性的密碼體制——RSA 密碼體制及其變種、基於離散對數問題的公鑰密碼體制。
1.4. 密鑰泄露的危害
影響密碼系統安全性的基本因素:密碼演算法複雜度、密鑰機密性、密鑰長度。其中密鑰機密性與主要與密鑰的管理相關。如何保護好密鑰的安全就成了資訊安全的非常重要的一個部分。
在現在的應用系統中,密碼、密鑰往往被作為一種訪問密鑰(access key)或憑證(Credentials),用於系統之間建立鏈接,避免了用戶密碼的明文傳輸。很多時候訪問密鑰等同於密碼。
例如我們熟悉的GitHub的訪問密鑰。當我們獲得Github某個庫的地址訪問密鑰,就可以在Windows的憑證管理或本地Git的憑證管理里添加這個訪問密鑰,本地git和遠端的訪問庫地址就建立了信任鏈接,不再需要每次都輸入密碼。
但密鑰本地化,也會導緻密鑰分散在程式碼、配置文件中。一旦缺乏對密鑰的統一管理, 就容易導緻密鑰泄露。員工不慎將密鑰泄漏到開源網站導致重要數據丟失事件時有發生。
2018年某酒店集團共140G約5億條個人資訊遭泄露,被發現泄露的資訊在境外黑市中售賣。究其原因,是該集團對員工的安全意識缺乏足夠的教育培訓,導致開發人員在無意識的情況下將公司測試平台的帳號密碼發到 GitHub上,使得黑客下載了整個數據。
我們從Verizon(美國最大的有線通訊和語音通訊提供商),每年發布的《數據泄露調查報告(Data Breach Investigations Report(DBIR))》,來看下密碼密鑰在資訊泄露中被黑客利用的情況。
-
《2020數據泄露調查報告(DBIR)》
使用偷竊的信用憑證、利用員工誤發送、員工誤配置是數據泄露的主要威脅。內部員工操作不規範、沒有養成良好的工作行為習慣以及疏忽大意等已成為多起嚴重網路安全事件發生的根本原因。 -
《2021數據泄露調查報告(DBIR)》
61%的數據泄露涉及憑證數據,憑證的泄露是資訊泄露的主要途徑,防止憑證泄露對資訊保護有著重要的作用。 -
《2022數據泄露調查報告(DBIR)》
-
憑證是發起攻擊的最重要的手段。
-
憑證和個人數據是黑客最喜歡獲取的兩類數據
報告指出:我們長期以來一直認為,憑證是犯罪分子最喜歡的數據類型,因為它們對於偽裝成系統上的合法用戶非常有用。就像諺語中披著羊皮的狼一樣,它們的行為在攻擊之前顯得無害。
-
2. 密碼密鑰硬編碼的檢查
接下來我們看下如何防範密碼密鑰在帶碼中由於硬編碼導致的泄露。
先來看些如何鑒別密碼密鑰。
2.1. 香農熵(Shannon entropy)
密鑰的長度決定了密鑰空間(keyspace),通常以位為單位。密鑰空間越大,密鑰被攻破的難度就越大。
密鑰是由密鑰空間的隨機值構成。對於任意一個隨機變數 X,它的熵定義如下:
變數的不確定性越大,熵也就越大,把它搞清楚所需要的資訊量也就越大。
- P(x_i)P(xi) : 指的是單個樣本變數所屬的變數種類的個數佔據所有變數個數的比例。
舉例:數據data有六個值,分為別為:[a,b,c,a,b,a];
樣本總個數是6,變數種類數3;分別為[a:3, b:2, c:1]
所以對應的pi分別為[a:1/2, b:1/3, c:1/6]
公式計算log以2為底數的pi的對數值,然後再乘以pi的負數,再計算其加和,得到的便是香農熵的值:1.4591479170272448。
/** * Base on shannon entropy return bits of entropy represented in string. * * @param str input string * @return entropy */ public static double getShannonEntropy(String str) { int num = 0; Map<Character, Integer> pi = new HashMap<Character, Integer>(); // count char in string char cx; for (int l = 0; l < str.length(); ++l) { cx = str.charAt(l); if (pi.containsKey(cx)) { pi.put(cx, pi.get(cx) + 1); } else { pi.put(cx, 1); } num = num + 1; } double entropy = 0.0; for (Map.Entry<Character, Integer> entry : pi.entrySet()) { cx = entry.getKey(); double p = (double) entry.getValue() / num; entropy = entropy + p * (Math.log(p) / Math.log(2)); } return -entropy; }
- 同等長度的字元串,通常密鑰的熵值更高
密鑰為避免彩虹攻擊,在取值上更加的離散,會盡量採用不重複的字元。就像我們為了增加密碼的複雜性,要求長度不小於8,必須包含大小寫、特殊字元、以及數字一樣的道理。所以密鑰的熵值會比一般的文本要高的多。我們就是利用這點來識別字元串是否是密鑰。
2.2. 工具的檢查邏輯
對於密碼密鑰的硬編碼檢查可以採用靜態分析工具來完成。工具的檢查過程通常包含四個過程:輸入文件準備、檢查、過濾和報告輸出。
2.2.1. 輸入文件轉換
- 輸入文件分類
我們需要檢查的文本文件進行分類,通常包括以下幾種類型:- 程式語言:C、C++、Java、Python、Go、Js等;
- 有統一格式的文件:屬性文件、yaml、csv、json、xml等;
- 文本文件:沒有固定格式的文本文件。
分類的目的是為了更好的識別文件中的字元串常量,充分利用字元串常量的上下文關聯,以便在分析中最大程度的減少誤報。
- 輸入文件轉換
- 程式語言:通過各語言的語法解析器,解析成抽象語法樹,提取語法樹中的等於字元串的常量,以及對應的變數名;
- 有統一格式的文件:照格式轉換成變數名和字元串常量值;
- 文本文件:採用token的方式分割成一個個的token,變成一個各的字元串常量。
2.2.2. 密碼密鑰檢查
在我們得到大量的變數名和字元串常量後,主要通過正則表達式匹配的方式完成目標的篩選。
由於檢查密碼密鑰的種類和類型不同,可以通過配置文件來提高檢查能力的可擴展性。
- 檢查配置選項主要包括以下內容:
資訊 | 描述 | 選項 |
---|---|---|
檢查類型 | 可分為:變數名、字元串常量、或兩個都檢查 | 必選 |
密碼密鑰的類型 | 用於區分不同類型的密碼密鑰;同時用於告警時區分具體檢測到的密碼密鑰類型 | 必選 |
正則表達式 | 主要設定匹配的長度,每個字元的可選類型 | 必選 |
熵值的閾值 | 用於精確的識別密碼密鑰的類型,降低誤報 | 可選 |
例如:
- 檢查硬編碼的口令:檢查變數名中包含:password、passwd、pwd的變數,且變數等於字元串常量;正則表達式可以設置成為:”.*(password|passwd|pwd)$”。
- 檢查GitHub的個人憑證:檢查字元串常量;這個憑證是以”ghp_”開頭的,跟隨長度為36的字元串,且每個字元可以為數字和字母;正則表達式可以設置成為:「ghp_[0-9a-zA-Z]{36}」。
2.2.3. 密碼密鑰過濾
靜態分析能很大程度上減少了人工審核的工作量,但由於檢查模式的不確定性,也會帶來不少的誤報。誤報會給用戶在審核過程中帶來很大的負面情緒,從而不願繼續使用工具。為了進一步降低誤報,我們可以通過下面的方式來降低誤報:
-
密碼密鑰熵值的計算
前面討論過密碼密鑰的特點,可以通過檢測密碼密鑰的資訊熵的方式來降低誤報。有些密碼密鑰設定了最低的閾值,但還是有很多密碼密鑰並未給出具體的閾值,這個就需要通過經驗積累來設定,目前業界也有通過機器學習來完善這個閾值的設定。 -
污點分析
在程式碼中,對於口令的變數的取名上,很多並不會遵守可讀性和可維護性來設定變數名,通過前面正則表達式的方式來查找硬編碼密碼的方式,會造成很多的漏報。這裡還可以通過污點分析的方法,來推導出密碼是否採用了硬編碼。例如檢查jdbc連接的密碼參數,查看該參數是否為字元串常量。
2.2.4. 報告輸出
將經過過濾後的結果,輸出告警,給出可能泄露的文件名和變數或可能為密碼密鑰的常量字元串位置,便於人工的排查。
2.2.5. 參考工具
- 華為雲程式碼檢查 CodeCheck://www.huaweicloud.com/product/codecheck.html
3. 參考
- 柯克霍夫原則: //en.wikipedia.org/wiki/Kerckhoffs’s_principle
- 香農資訊熵://en.wikipedia.org/wiki/Entropy_(information_theory)
- Verizon 2022年數據泄露報告://www.verizon.com/business/resources/reports/dbir/2022/master-guide/
- Verizon 2021年數據泄露報告: //www.verizon.com/business/resources/reports/dbir/2021/master-guide/
- Verizon 2020年數據泄露報告: //www.verizon.com/business/resources/reports/dbir/2020/master-guide/
- GitHub 秘密掃描://docs.github.com/cn/code-security/secret-scanning/about-secret-scanning)