本文分享自華為雲社群《靜態分析工具的評估測試》,作者: Uncle_Tom。
1. 垂直極限
還是先說故事。那是 2014 年參加的一個測試驅動(TDD)的培訓,培訓是 TDD 推廣的志願者組織的,在一個咖啡館裡搞的,週末兩天的免費培訓。
培訓過程中的一張圖和一個影片讓我至今記憶尤新。
1.1. 一張圖
上面的兩個圖(原來培訓的那個圖找不到了,自己隨手塗鴉了一下)。
左邊是經過完整的系統的測試的軟體產品,每個節點都透過測試,這樣一層層的搭建起來的系統。看著就堅實可靠。
右邊的測試則是隨意的,很多地方都缺失了。任何一個風吹草動,一個異常都可能造成整個大廈傾覆。
不用說大家立刻就可以看懂,那個軟體產品更可靠,更讓人放心。
1.2. 一段影像
影片是電影《垂直極限》中開頭的一段。雖然過去 20 多年了,但這部電影仍然被奉做山難電影的經典之作,有興趣的朋友還是可以找來看一看的。
一個風和日麗的日子裡,老爸帶著兒子、女兒在一個高聳、陡峭的山峰上攀巖。他們已經來到了山峰的一半,在他們的上面個還有另一群攀巖愛好者。
老爸一邊整理著自己的安全栓(攀巖過程中打在岩石中,然後利用登山繩固定自己的安全扣),一邊對兒子說:“檢查下你妹妹的安全栓。”
兒子看向下面的妹妹,妹妹說:“告訴他我們已經不是小孩子了。”
兒子打趣的對老爸說:“爸,她還需要一個安全栓。”
爸爸看向下面的兄妹兩人說:“萬一出事,那樣可撐不住。安妮,我不管你有多老練,聰明的攀巖者都會做好安全措施,兩個栓才安全,三個更好。在巖壁上再放個安全栓,我們才繼續攀。”
女兒生氣的對爸爸說:“爸,你別聽哥哥的,他在開玩笑,我放了三個安全栓。”
電影總是這樣,一段舒緩的節奏後面,總會有段緊張的讓人喘不過氣來的場面。不出意外,意外就會發生。
話音未落,先是一個登山包從山上面墜落下來,快速的經過三人。上面的菜鳥在登山的過程中揹包意外脫落,幫著的聖山包的登山繩造成一連串的連鎖反應,兩個登山者也不幸地被連帶著墜落下去。老爸、兒子也被著一連串的變故拖累到掛在了半空中,女兒的三個安全栓無法承擔三個人的重量,一個、兩個先後崩飛,只靠著最後的一個安全栓勉強的維持著女兒,以及掛在半空的老爸和兒子。最後在老爸的要求下,兒子不得不割斷了繩索,女兒和兒子看著老爸墜了下去。
看到這裡,大家都不禁須臾不已,再看看上面的圖,對系統的完整的測試有了更深刻的認識。
1.3. 思考
在軟體的開發過程中,每一個節點的測試都是在為系統增加一個安全栓。由這樣一層層搭建起來的系統,整個系統才是安全、可靠的。否則任何一個意外都可能將整個系統帶飛。做工具和做單點能力驗證不同,需要更多的測試節點來保障工具的穩定性和可靠性。這個從學院裡出來的大部分朋友還沒有意識到這個問題,加之專案的管理的問題,就會把各種坑坑窪窪帶到生產中,使後期維護陷入無盡的深淵。
大家都喜歡做 0 到 1 的事情,出彩啊。很少有人願意做為 0 到 1 填坑的基礎工作,但一個工具要生存,能夠贏得最終的勝利,需要無數的這些幕後英雄。就像長津湖戰役一樣,指揮很重要,但更多的是需要後面無數的無名英雄,那些人才是撐起整個勝利的英雄。
那麼對於靜態分析工具,我們該如何構建一個穩定、可靠的靜態分析工具?如何評價一個靜態分析工具的檢查能力?
自從程式的誕生,程式分析便緊隨其後,人們檢視透過一個程式來分析編制的程式,保證編制程式執行結果的正確性。儘管後面的萊斯定理給出了這個問題的“不可判定性”,但並不會妨礙程式分析在這方面的卓越表現。
儘管現在大模型能夠幫助我們生成程式程式碼和對已有程式進行問題檢查,這似乎可以繞過我們一值以來使用的模式匹配的檢查方式,可以簡化模式的提煉,和根據模式再編寫檢查規則。 但如何保障生成程式碼的安全性和可靠性,以及如何評估大模型的能力,仍然將是人工智慧在今後很長一段時間需要解決的問題。
透過下面對三組測試用例的分析,希望能夠給靜態分析工具的測試和評估給大家一個指導性的啟發。
2. Juliet Java、C/CPP 測試用例集
2.1. 建立背景
2005 年美國國家標準與技術研究院(National Institute of Standards and Technology (NIST)),簡稱NIST,下屬的軟體質量組,成立了軟體保障指標和工具評估專案(Software Assurance Metrics And Tool Evaluation (SAMATE)),簡稱SAMATE專案。專案組的主要目的是透過開發支援軟體工具評估的方法、衡量工具和技術的有效性以及識別工具和方法中的差距來改善軟體保障,主要工作包括定義錯誤類,收集具有已知錯誤的程式語料庫,以及更好地瞭解工具的有效性。
圍繞這個目標,軟體保障指標和工具評估專案(SAMATE)建立了兩個子專案:
-
軟體保障參考資料集(Software Assurance Reference Dataset (SARD)),簡稱SARD,用於收集整理C、C++、Java、PHP 和 C#針對軟體弱點的測試用例集;
-
軟體工具博覽會(Static Analysis Tool Exposition (SATE)),簡稱SATE,用於工具製造商對用例進行測試和工具研討。截至 2021 年,已經舉辦了六場 SATE 活動。
2.2. Juliet 用例集簡介
Juliet 測試用例集就是軟體保障參考資料集(SARD)下的一個是用於檢測 C/C++ 和 Java 程式已知缺陷的集合。
2010 年 12 月 1.0版本。Juliet 測試用例集最早的 1.0 釋出於2010 年 12 月,由軟體保障指標和工具評估專案(SAMATE)的開發團隊完成,名字選取了當時軟體保障參考資料集(SARD)的第十個貢獻者:國際無線電(International Radiotelephony)的字母表中的第十個單詞 “Juliet” 而的得名。
2011 年 1.1 - 1.1.1 版本。Juliet 1.1 版本 是 Juliet 的開發團隊根據多個因素為選定的缺陷建立了測試用例,包括團隊的經驗、缺陷的重要性或嚴重性以及其出現的頻率。測試用例涵蓋了 2011 年 CWE/SAN TOP 25 個最危險的程式設計錯誤中的 14 個。剩下的 11 個缺陷是設計問題,例如 CWE-862 授權機制缺失 和 CWE-250 帶著不必要的許可權執行,這些問題不適合用靜態分析的方式來檢測,所以未包含在測試用例中。
2012 年 1.2 版本。2012 年的 Juliet 1.2 版本。基本形成了現在使用版本。是我們現在使用最多的版本,程式分析、自動修復、深度學習的論文基本上都使用了這裡面的用例,用於證明理論、工具的有效性。
2017 年 1.3 版本。只對1.2 版本的個別錯誤進行了修正。
- Juliet C/C++ 涵蓋 118 個 CWE 問題,1689 個場景,用例數: 181,140, 其中正例:117,041, 反例:64,099。
- Juliet Java 涵蓋 112 個 CWE 問題,933 個場景,用例數: 96,537, 其中正例:67,656, 反例:28,881。
注:關於 SARIF請參考:
2.3. Juliet 用例構造的特點
2.3.1. 統一明確的命名方式
測試用例使用CWE作為命名和組織的基礎。測試用例力求對目標缺陷使用最具體的CWE條目。每個測試用例檔案與一個CWE條目相關聯。
2.3.1.1. 測試用例檔名命名規範
測試用例名由四個元素的組合唯一標識:
組成部分 | 描述 | 是否強制 |
---|---|---|
CWE | 字元 | 是 |
CWE ID | CWE的編號 | 是 |
_ | 分隔,下劃線 | 是 |
CWE短描述 | CWE英文名稱的縮短版本,單詞之間用下劃線"_"連線 | 是 |
__ | 分隔,兩個下劃線"_" | 是 |
問題場景 | 表示用例場景。場景描述單詞之間用下劃線"_"連線 | 是 |
_ | 分隔,下劃線,問題場景和案例編號之間進行連線 | 是 |
案例編號 | 基礎案例編號採用“01” | 是 |
一個案例多檔案定義 | 對同一個案例,如使用多個檔案,可採用a,b等進行區別,或採用下劃線加不同的作用定義,例如_good或_bad,表示正例或反例 | 否 |
例如:
單檔案用例:
CWE190_Integer_Overflow__byte_console_readLine_add_01.java- CWE190: CWE 的編號,可以在CWE網站上查詢到這個編號對應的問題:https://cwe.mitre.org/data/definitions/190.html;
- Integer_Overflow:CWE190 描述的簡寫,整型溢位。CWE190 的完整描述是:整型溢位或越界折返;
- byte_console_readLine_add: 場景描述:透過readLine函式從控制介面讀取位元組流;
- 01:代表基礎用例。
多檔案用例, 下面的一組檔案構成了一個用例:
- CWE190_Integer_Overflow__byte_console_readLine_add_81_bad.java
- CWE190_Integer_Overflow__byte_console_readLine_add_81_base.java
- CWE190_Integer_Overflow__byte_console_readLine_add_81_goodB2G.java
- CWE190_Integer_Overflow__byte_console_readLine_add_81_goodG2B.java
- CWE190_Integer_Overflow__byte_console_readLine_add_81a.java
- CWE190: CWE 的編號,可以在CWE網站上查詢到這個編號對應的問題:https://cwe.mitre.org/data/definitions/190.html;
- Integer_Overflow:CWE190 描述的簡寫,整型溢位。CWE190 的完整描述是:整型溢位或越界折返;
- byte_console_readLine_add: 場景描述:透過readLine函式從控制介面讀取位元組流;
- 81: 資料流案例,案例將引數傳遞給透過引用呼叫的抽象方法;
- _bad: 表示這是個反例的檔案;
- _base: 表示這個是用例的主檔案;
- _goodB2G: good 表示這是個正例的檔案,同時 B2G 表示汙點(Bad)會經過清洗到達(2(to))爆發點,但由於做了清洗,變成了無汙染的,不會引起問題(Good);
- _goodG2B: good 表示這是個正例的檔案,同時 G2B 表示汙點經過清洗變成無汙染(Good)到達(2(to))爆發點(Bad),但由於是無汙染的資料,也不會引起問題;
- a: 表示這是個輔助檔案。
2.3.1.2. 測試用例函式名命名規範
有問題(反例)函式,通常以bad命名, 並可使用正規表示式匹配:^bad$
;
沒問題(正例)的函式,通常以good命名, 並可使用正規表示式匹配:^good$
;
^good(\d+|G2B\d*|B2G\d*)$
;
- 預設或通用的方式,採用例如: good1()、good2()、good3()的命名方式;
- 當一個好的源將安全資料傳遞到一個潛在的壞接收器時,採用例如:goodG2B()、goodG2B1()、goodG2B2()、goodG2B3() 的方式命名;
- 當不良源將不安全或潛在不安全的資料傳遞給良好源時,採用例如:goodB2G()、goodB2G1()、goodB2G2()、goodB2G3()的方式命名;
- 對於資料流用例還遵守以下命名方式:有問題的汙染源,採用正規表示式:
^badSource$;
有問題的爆發點,採用正規表示式:^badSink$;
無問題的汙染源,採用正規表示式:^good(G2B\d*|B2G\d*)?Source$;
無問題的爆發點,採用正規表示式:^good(G2B\d*|B2G\d*)?Sink$
。
2.3.1.3. 命名規則在測試中帶來的好處
從檔名就能判斷出檢測問題、場景、適用的案例型別,有的還可以知道檔案是正例、還是反例;
從發生問題的函式名就可以知道,所處的函式是正例,還是反例;
通常檢測工具都會給出問題所在的檔案和函式,這樣在檢視問題的時候能夠立刻判斷出檢測結果的有效性;
嚴格的命名方式,也便於使用程式自動化的檢查結果做出快速的判斷。2.3.2. 每個問題給出發生問題的可能場景
Juliet 還為每個問題(CWE)列舉了一些發生場景,一些場景還使用模板結合控制流和資料流進行了列舉,從而測試更加廣泛的問題發生的可能性,以提高問題的覆蓋率。
Juliet 一共採用了三類别範本:
- 控制流模板(point-flaw)
- 單一汙點源和爆發點模板(source-sink);
- 多汙點源和爆發點模板(sources-sinks);
Juliet Java
- Juliet Java 一共 122 個 CWE, 覆蓋 933 個場景。
- 用模板生成的場景 823 個,這包括:point-flaw: 92 個場景;sources-sink: 197 個場景;sources-sinks: 534 個場景;
- 單一場景 110 個。
Juliet C/C++
- Juliet C/C++ 一共 118 個 CWE, 覆蓋 1689 個場景。
- 用模板生成的場景 1509 個,這包括:
Template | C | CPP | Total |
---|---|---|---|
point-flaw | 259 | 18 | 277 |
sources-sink | 499 | 233 | 723 |
sources-sinks | 357 | 152 | 509 |
Total | 1106 | 403 | 1509 |
而單一場景 180 個。
2.3.3. 每個場景使用不同的案例進行覆蓋
Juliet 用例還真對每個場景結合控制流和資料流進行了列舉,以達到每個場景在不同程式碼條件下的檢測能力的覆蓋。
從下表我們看到型別基本上涵蓋了程式語言通常所需要的基本語法,例如條件判斷(if),條件分支(swith),迴圈(while,for),函式間和程式間不同層數的呼叫,以及不同型別的引數傳遞方式。
案例型別明細
案列編號 | 案例型別(流型別) | 型別描述 | C | CPP | JAVA |
---|---|---|---|---|---|
01 | 無 | 基線——最簡單的缺陷形式 | Y | Y | Y |
02 | 控制流 | if(true) 和 if(false) | Y | Y | Y |
03 | 控制流 | if(5==5) 和 if(5!=5) | Y | Y | Y |
04 | 控制流 | if(PRIVATE_STATIC_FINAL_TRUE) 和 if(PRIVATE_STATIC_FINAL_FALSE) | Y | Y | Y |
05 | 控制流 | if(privateTrue) 和 if(privateFalse) | Y | Y | Y |
06 | 控制流 | if(PRIVATE_STATIC_FINAL_FIVE==5) 和 if(PRIVATE_STATIC_FINAL_FIVE!=5) | Y | Y | Y |
07 | 控制流 | if(privateFive==5) 和 if(privateFive!=5) | Y | Y | Y |
08 | 控制流 | if(privateReturnsTrue()) 和 if(privateReturnsFalse()) | Y | Y | Y |
09 | 控制流 | if(IO.STATIC_FINAL_TRUE) 和 if(IO.STATIC_FINAL_FALSE) | Y | Y | Y |
10 | 控制流 | if(IO.staticTrue) 和 if(IO.staticFalse) | Y | Y | Y |
11 | 控制流 | if(IO.staticReturnsTrue()) 和 if(IO.staticReturnsFalse()) | Y | Y | Y |
12 | 控制流 | if(IO.staticReturnsTrueOrFalse()) | Y | Y | Y |
13 | 控制流 | if(IO.STATIC_FINAL_FIVE==5) 和 if(IO.STATIC_FINAL_FIVE!=5) | Y | Y | Y |
14 | 控制流 | if(IO.staticFive==5) 和 if(IO.staticFive!=5) | Y | Y | Y |
15 | 控制流 | switch(6) and switch(7) | Y | Y | Y |
16 | 控制流 | while(true) | Y | Y | Y |
17 | 控制流 | for 迴圈 | Y | Y | Y |
18 | 控制流 | goto 語句 | Y | Y | N |
21 | 控制流 | 流由私有變數的值控制。所有方法都包含在一個檔案中。 | Y | Y | Y |
22 | 控制流 | 流由公共靜態變數的值控制。source和sink在兩個不同的檔案裡。 | Y | Y | Y |
31 | 資料流 | 在同一方法中使用資料副本流動 | Y | Y | Y |
32 | 資料流 | 在同一個函式中使用兩個指向同一個值的指標的資料流 | Y | Y | N |
33 | 資料流 | 在同一函式中使用對資料的 C++ 引用 | N | Y | N |
34 | 資料流 | 使用包含兩個訪問相同資料的方法的聯合(在同一函式內) | Y | Y | N |
41 | 資料流 | 作為引數從一個方法傳遞到同一類中的另一個方法 | Y | Y | Y |
42 | 資料流 | 在同一類中從一個方法返回到另一個方法 | Y | Y | Y |
43 | 資料流 | 資料使用 C++ 引用從同一原始檔中的一個函式流向另一個函式 | N | Y | N |
44 | 資料流 | 資料作為引數從一個函式傳遞到透過函式指標呼叫的同一原始檔中的函式 | Y | Y | N |
45 | 資料流 | 作為私有類成員變數從一個方法傳遞到同一類中的另一個方法 | Y | Y | Y |
51 | 資料流 | 在同一個包的不同類中作為引數從一個方法傳遞到另一個方法 | Y | Y | Y |
52 | 資料流 | 在同一個包的三個不同類中作為引數從一個方法傳遞到另一個方法 | Y | Y | Y |
53 | 資料流 | 在同一個包的四個不同類中作為引數從一個方法傳遞到另一個方法 | Y | Y | Y |
54 | 資料流 | 在同一個包的五個不同類中作為引數從一個方法傳遞到另一個方法 | Y | Y | Y |
61 | 資料流 | 在同一個包的不同類中透過一個方法的返回值傳遞到另一個方法 | Y | Y | Y |
62 | 資料流 | 使用 C++ 引用從不同原始檔中的一個函式到另一個函式的資料流 | N | Y | N |
63 | 資料流 | 指向不同原始檔中從一個函式傳遞到另一個函式的資料的指標 | Y | Y | N |
64 | 資料流 | void 指向不同原始檔中從一個函式傳遞到另一個函式的資料的指標 | Y | Y | N |
65 | 資料流 | 資料作為引數從一個函式傳遞到透過函式指標呼叫的不同原始檔中的函式 | Y | Y | N |
66 | 資料流 | 在同一個包中的不同類中透過陣列從一個方法傳遞到另一個方法 | Y | Y | Y |
67 | 資料流 | 在同一個包中的不同類中透過類從一個方法傳遞到另一個方法 | Y | Y | Y |
68 | 資料流 | 在同一個包中的不同類中透過類的成員變數從一個方法傳遞到另一個方法 | Y | Y | Y |
71 | 資料流 | 在同一個包中的不同類中透過類的引用從一個方法傳遞到另一個方法 | N | N | Y |
72 | 資料流 | 在同一個包中的不同類中將向量從一個方法傳遞到另一個方法 | N | Y | Y |
73 | 資料流 | 在同一個包中的不同類中將LinkedList 從一個方法傳遞到另一個方法 | N | Y | Y |
74 | 資料流 | 在同一個包中的不同類中將HashMap從一個方法傳遞到另一個方法 | N | Y | Y |
75 | 資料流 | 將序列化物件從一個方法傳遞到同一包中不同類中的另一個方法 | N | N | Y |
81 | 資料流 | 將引數傳遞給透過引用呼叫的抽象方法 | N | Y | Y |
82 | 資料流 | 將引數傳遞給透過指標呼叫的虛擬函式的資料 | N | Y | N |
83 | 資料流 | 透過在堆疊上宣告類物件將資料傳遞給類建構函式和解構函式 | N | Y | N |
84 | 資料流 | 透過在堆上宣告類物件並在使用後將其刪除來傳遞給類建構函式和解構函式的資料 | N | Y | N |
2.3.4. 缺點
Juliet 可以說是堪稱完美的測試用例集,但隨著時間的推移,程式語言的迭代,它也顯現出一些缺點:
- 一些用例的寫法開始變得老舊,可能不能勝任新的場景;
- 沒有對語言的所有語法特點做覆蓋,特別是新的一些語法型別;比如:lamda 表示式;
- 用例的設計存在大量的重疊,特別是對資料流的檢測,每個場景都使用了相同的模板,產生了大量的用例。其實對於資料流的處理,完全可以分成:汙染源、汙染傳遞、汙染清理、以及爆發四個不同的維度進行分別測試,這樣可以大大提升測試效率。
3. OwaspBenchmark 測試用例集
3.1. 用例背景
OWASP(Open Worldwide Application Security Project (OWASP)) 基金會致力於透過其社群主導的開源軟體專案、全球數百個分會、數萬名成員以及舉辦當地和全球會議來提高軟體的安全性。
3.2. 用例簡介
OWASP Benchmark Project 是一個 Java 測試套件,旨在評估自動化軟體漏洞檢測工具的準確性、覆蓋率和速度。
- 1.0 版本於 2015年4月15日釋出,共有 20983 個測試用例。
- 1.1 版本於 2015年5月23日釋出。1.1版本在前一版本的基礎上進行了改進,確保每個漏洞區域都有真陽性和假陽性。
- 1.2 版本於 2016年6月5日首次釋出(1.2測試版於2015年8月15日釋出)。自那以後,v1.2版本一直在不斷調整。1.2 版本將測試用例數量控制在小於3000個測試用例,以便快速得到測試結果。
3.3. 用例設計和組成方式
下面以 1.2 版本為例進行說明。
3.3.1. 用例問題的覆蓋
從下面這個表可以看出 Benchmark 更多的注重覆蓋 Web 類的問題的檢查,同時重點覆蓋了 OWASP TOP 10 中的主要能夠透過靜態檢查工具檢查的問題。關於 OWASP TOP 10 可以參考:CWE 4.6 和 OWASP TOP10(2021)。
Benchmark 用例集主要以加密問題,以及注入類問題為主,這也巧合的與靜態檢查技術相互匹配。加密問題多用抽象語法樹(AST)的遍歷來返現加密函式,並對其做出判斷。而注入類問題多用資料流的汙點分析技術來追蹤外部輸入是否會對爆發點形成可達的路徑。有關汙點分析技術,可以參考:使用汙點分析檢查log4j問題。
檢測問題 | CWE TOP 25(2023) | OWASP TOP 10(2021) | 正確用例 | 錯誤用例 | 用例總數 |
---|---|---|---|---|---|
CWE-22 對路徑名的限制不恰當(路徑遍歷) | 8 | A01:2021-中斷訪問控制 | 135 | 133 | 268 |
CWE-327 使用已被攻破或存在風險的密碼學演算法 | - | A02:2021-加密故障 | 116 | 130 | 246 |
CWE-328 可逆的單向雜湊 | - | A02:2021-加密故障 | 107 | 129 | 236 |
CWE-330 使用不充分的隨機數 | - | A02:2021-加密故障 | 275 | 218 | 493 |
CWE-501 違背信任邊界 | - | A04:2021-不安全的設計 | 43 | 83 | 126 |
CWE-614 HTTPS會話中未設定’Secure’屬性的敏感Cookie | - | A02:2021-加密故障 | 31 | 36 | 67 |
CWE-643 XPath表示式中資料轉義處理不恰當(XPath注入) | - | A03:2021-注入 | 20 | 15 | 35 |
CWE-78 OS命令中使用的特殊元素轉義處理不恰當(OS命令注入) | 5 | A03:2021-注入 | 125 | 126 | 251 |
CWE-79 在Web頁面生成時對輸入的轉義處理不恰當(跨站指令碼) | 2 | A03:2021-注入 | 209 | 246 | 455 |
CWE-89 SQL命令中使用的特殊元素轉義處理不恰當(SQL隱碼攻擊) | 3 | A03:2021-注入 | 232 | 272 | 504 |
CWE-90 LDAP查詢中使用的特殊元素轉義處理不恰當(LDAP注入) | - | A03:2021-注入 | 32 | 27 | 59 |
3.3.2. 問題場景的覆蓋
Benchmark 用例對每個問題採用: 場景列舉 + 組合的方式完成用例的設計,並透過此方法形成問題的覆蓋。
這裡以我們熟悉的:CWE89 SQL隱碼攻擊問題來說明這種用例的設計方式。用例集中CWE 89 SQL隱碼攻擊問題一共有 232 個正例,272 個反例,共計504 個用例。
因為 SQL 是注入是透過外部不可信資料,傳播到 SQL 指令碼執行的位置而導致的安全問題。這個外部資料傳播的過程可以分為:
- 接收資料
用例使用了我們常用的從 http 請求中得到外部資料,然後將資料以不同方法存入不同型別的欄位。用例中列舉了下面 9 種不同的方法。例如放入:字串、列舉、陣列等。
- 資料傳遞
用例接收到資料後,使用不同的傳遞方式,向程式內傳遞,並對資訊採用不同的操作方式進行加工。用例中列舉了下面 10 種不同的方法。例如:透過 建立一個新類然後呼叫函式傳遞、if 條件表示式、內部類等。
- 問題爆發
最終資料會拼裝成 SQL 語句,並透過不同的呼叫方式執行。用例中列舉了 3 類,28 種不同的執行方式。
- 場景組合
得到上面三種基本節點後,透過組合的方式形成用例。下表列舉了 CWE89 SQL隱碼攻擊 272個反例組合的場景:
3.3.3. OwaspBenchmark 用例集的缺點
OwaspBenchmark 應為涵蓋了Web應用的主要安全問題,使用例集基本上成為了Web 應用安全測試的基本用例集。但它也存在一些缺點。
- 用例名只採用了簡單的編號方式,從用例看不出測試目標:用例反應的問題、場景、正例、反例這些基本資訊,而不得不給每個用例加了一個xml檔案來說明這些用例的基礎資訊。
- 用例缺少場景的描述,工具測試後,無法得到覆蓋場景和非覆蓋場景的統計資訊,只知道覆蓋率。具體哪些場景缺失,要一個個用例去自己分析。
- 缺少檢測語言語法級別的場景的覆蓋,例如lamda表示式,工具在資料流的分析過程中,任何一個語法的不適配都會導致分析中斷。
4. Alipay 測試用例集
4.1. 用例背景
針對xAST領域缺乏有效衡量技術能力標準的業界痛點,螞蟻安全團隊聯合螞蟻程式分析團隊、浙江大學網路空間安全學院的20餘位專家學者,共同設計了xAST評價體系及其測試樣本套件Benchmark,致力於成為應用安全測試工具的“度量衡”。
-
目標:打造具備行業共識的xAST能力評價體系技術標準
-
價值:衡量xAST產品技術能力,指引xAST技術發展方向,輔助企業產品選型
4.2. 用例設計和組成方式
用例設計的核心思想是:分層設計,降低評價複雜度。
從底層到上層分成引擎能力、規則能力和產品化能力這三層。對這三層分別設計評價體系和測試樣本,既降低了每一層評價的複雜度,又使測試結果可以直接反映問題出在哪一層。
看的出,用例集的設計者試圖希望結合Juliet、Owaspbenchmark 的優點,在形成一種分層的評估測試方式。來完善前面兩個用例集在語法層面的不足。
目前用例集只推出了一個雛形,還在建設中。
5. 理想的測試用例集
最後再來總結下,理想的測試用例集應該是怎樣的。
- 能夠從用例的命名上,清楚的反映:測試問題、場景、正例、反例;或從目標函式上明確知道在這個函式內的告警是正確的告警,還是誤報;
- 能夠覆蓋業界主要的安全問題,例如:CWE TOP 25、OWASP TOP 10等常見的問題;
- 能夠覆蓋檢測語言的主要語法和語言的主要使用方式;
- 能夠有一定的場景列舉和組合,以增加測試用例的複雜度,這有點類似 fuzzy 測試了。
寫在最後,測試用例集的結果,只能反映一個工具的基礎能力,並不能取代透過實際的工程來打磨檢查工具。
一般的程式設計師只需要在問題和實現上建立一條通道就好,但程式分析的程式設計師卻需要考慮各種程式設計師實現問題的可能性。
6. 參考
- SAMATE: https://www.nist.gov/itl/ssd/software-quality-group/samate
- SARD: https://samate.nist.gov/SARD/
- Juliet C/CPP 1.3: https://samate.nist.gov/SARD/test-suites/112
- Juliet Java 1.3: https://samate.nist.gov/SARD/test-suites/111
- owasp benchmark: https://owasp.org/www-project-benchmark/
- Alipay sast: https://github.com/alipay/ant-application-security-testing-benchmark