術中分析與術間分析

術中分析與術間分析:為何差異決定了你的發現

對存在 SQL 注入漏洞的 JavaScript 檔案執行 ESLint,它會順利通過。而對同一程式碼庫執行 CodeQL,它會標記出注入漏洞,並精確指出檔案、行號和資料流路徑。兩者都是靜態分析工具,它們都檢查相同的原始程式碼,但不執行程式碼。它們發現的差異並非規則配置或語言支援的問題,而是工具檢查範圍的根本差異。 ESLint 一次只檢查一個函數,並報告在該函數範圍內發現的問題。而 CodeQL 則會追蹤資料在函數呼叫、模組以及整個呼叫圖中的路徑,直到找到危險代碼為止。

區分過程內分析與過程間分析,是理解不同靜態分析工具為何發現不同問題的最重要技術概念。它解釋了為什麼程式碼檢查工具(linter)可能對程式碼庫發出「健康」的警告,而更複雜的工具卻能在同一程式碼中發現關鍵的安全漏洞。它解釋了為什麼安全靜態應用安全測試(SAST)工具運作成本高。它解釋了為什麼「快速」工具的漏檢率通常高於「慢速」工具。企業團隊在評估任何靜態分析工具鏈之前,都必須先理解這個概念。

查看完整的呼叫圖,而不僅僅是一個文件

SMART TS XL 追蹤 COBOL、Java、Python 以及您發布的每種語言的資料流。

更多資訊

什麼是術中分析?

過程內分析只關注單一函數、方法或過程內部發生的情況,而不考慮該函數呼叫其他函數或被其他函數呼叫時發生的情況。分析邊界就是函數邊界。當過程內分析工具遇到函數呼叫時,它會停止執行,不會追蹤呼叫過程進入被呼叫函數,也不會了解被呼叫函數如何處理接收到的資料。

程式內分析的典型例子是程式碼檢查和單一函數內的基本資料流:

蟒蛇

def process_input(user_data):
    # Intraprocedural analysis sees: user_data used here
    query = "SELECT * FROM users WHERE id = " + user_data  # flagged: string concat in SQL
    return query

一個簡單的規則可以標記 SQL 查詢中的字串拼接,它在過程內部有效,因為這種危險模式在單一函數中顯而易見。但稍微改變一下程式碼結構:

蟒蛇

def get_user_id(request):
    return request.args.get("id")  # untrusted input

def build_query(user_id):
    return "SELECT * FROM users WHERE id = " + user_id  # looks safe in isolation

def handle_request(request):
    uid = get_user_id(request)
    query = build_query(uid)    # vulnerability spans three functions
    db.execute(query)

現在,受污染資料的來源(request.args.get("id"))與危險的水槽功能不同(db.execute(query)沒有任何單一函數包含足夠的資訊供程式內工具檢測漏洞。 get_user_id 看起來沒問題,它只是從請求中讀取資料。 build_query 看起來沒問題,它接受一個參數並建構一個字串。 handle_request 看起來沒問題,它調用了函數並執行了查詢。 SQL注入是真實存在的,可以被利用,而且對於任何不跨越函數邊界的工具來說都是不可見的。

術中分析的優勢是什麼

過程內分析工具速度極快。由於它們獨立分析每個函數,因此其計算量與程式碼庫規模呈線性關係,分析百萬行程式碼庫的成本與分析萬行程式碼庫的成本成正比。這種速度使它們非常適合以下場景:

  • 預提交鉤子 不到一秒就能跑完
  • IDE 內嵌回饋 開發人員正在打字
  • CI/CD品質門 每次拉取請求
  • 風格和慣例的執行 (命名、格式、複雜度指標)
  • 局部變數誤用 (未使用的變數、未定義的引用、明顯的型別錯誤)
  • 環路複雜度和方法長度純粹的功能內指標

此類工具包括 ESLint、Pylint、Clippy(Rust)、golangci-lint、Checkstyle 以及大多數程式碼檢查工具。它們對於能夠發現的那類問題至關重要且極具價值。它們的限制在於結構層面,而非品質缺陷。

什麼是程序間分析?

過程間(全程式)分析通常是最強大的分析方法,但也最消耗資源。過程間分析透過呼叫圖中的函數呼叫和返回邊來增強控制流程圖。透過建立函數摘要,該分析可以避免多次重複存取相同的函數。

過程內分析止步於函數邊界,而過程間分析則跨越這些邊界。它建立一個呼叫圖,表示哪些函數呼叫了哪些其他函數,然後追蹤資料流、控制流或污點在該圖的邊界上的變化。當過程間分析工具遇到函數呼叫時,它會追​​蹤呼叫路徑進入被呼叫函數,或使用預先計算的被呼叫函數行為摘要,並在呼叫路徑的另一端繼續分析。

以上三個 SQL 注入函數只是一個簡單的範例。真正的過程間分析會處理以下問題:

  • 多跳資料流:未經信任的輸入,在到達危險的接收器之前,會經過五個函數。
  • 回傳值污染函數傳回受污染的資料;呼叫者在不安全的操作中使用該受污染的回傳值。
  • 田間敏感污染儲存在物件欄位中的不受信任的數據,稍後在另一個函數中檢索。
  • 條件傳播污點僅沿著特定的執行路徑流動,取決於運行時條件,分析必須對這些條件進行靜態建模。

Java的

// Interprocedural vulnerability: three-hop taint flow
public class UserController {
    public String handleRequest(HttpServletRequest request) {
        String userId = request.getParameter("id");    // source: untrusted input
        User user = userService.getUser(userId);       // taint flows through call
        return generateReport(user);                   // taint reaches sink
    }
}

public class UserService {
    public User getUser(String id) {
        // id is tainted -- passed to repository
        return userRepository.findByRawId(id);         // taint propagates
    }
}

public class UserRepository {
    public User findByRawId(String id) {
        String query = "SELECT * FROM users WHERE id = " + id;  // sink: SQL injection
        return db.execute(query);
    }
}

一種用於檢查的術中工具 UserRepository.findByRawId 單獨來看,這是一個參數 id 將它們連接成一個 SQL 查詢。這看起來可能不安全,但該工具無法知道它們會在哪裡出現。 id 資料來源可能是內部程式碼中經過驗證的可信值。因此,過程內檢測工具要么將其標記為潛在問題(對程式碼庫中所有參數化但結構化的查詢都產生誤報),要么忽略它(對真正的漏洞產生漏報)。過程間污染分析可以偵測不受信任的資料是否流經函數到達敏感操作。它會追蹤來自以下位置的數據: request.getParameter("id") 通過 userService.getUser()通過 userRepository.findByRawId()到 db.execute(query)能精確確定污染流向,無假陽性,無假陰性。

分析的三個層次:一個實用的光譜

實際應用中的靜態分析工具,其功能涵蓋了從純粹的程序內分析到完全的程序間分析的各個層面。了解每種工具的功能定位,有助於理解它能夠發現什麼,又會遺漏什麼。

分析層級範圍它所看到的它缺少什麼工具
詞彙/句法單一標記和語法格式、命名、基本語法語意的一切更漂亮,rustfmt,gofmt
術中單一功能函數內部的本地資料流、複雜性與控制流跨職能漏洞、架構問題ESLint、Pylint、Clippy、golangci-lint
表淺性手術間期一級呼叫深度直接的呼叫者-被呼叫者關係深度多跳流一些 Semgrep 規則,基本的 SAST 規則
完全間斷完整呼叫圖遍歷多跳污染流,情境相關分析運行時多態性(部分),反射調用CodeQL、Facebook Infer、 SMART TS XL
全程序/語義完整程序語義形式上可證明的性質超大型程式碼庫的可擴充性Infer、Kani(Rust)、形式化驗證工具

過程間分析具有較高的潛在有效性和相對較低的潛在性能。由於其豐富的程序模型,它也更不容易出現漏報和誤報,因此具有較高的有效性。然而,由於其效能限制,製程間分析並不適合 CI/CD 環境,因為這可能會降低開發人員的開發速度。因此,過程間分析通常作為專用服務提供,運行在伺服器叢集或專用工具上。

程式間分析的工作原理:三種關鍵方法

1. 基於摘要的分析

透過建立函數摘要(有時也稱為模型),分析可以避免多次重複分析相同的函數。摘要以簡潔的形式捕捉函數的行為:給定一組具有特定屬性的輸入,函數會產生一組具有特定屬性的輸出。當呼叫者呼叫函數時,分析會實例化該摘要,而不是從頭開始重新分析函數體。

基於摘要的分析使得跨過程分析能夠大規模地進行。如果沒有摘要,分析一個呼叫庫的函數就需要從原始碼開始遞歸地分析整個函式庫,包括該函式庫呼叫的每個函數。而有了摘要,函式庫函數只需預先分析一次,其摘要就可以在每次呼叫時重複使用。

2. 呼叫字串方法(上下文相關分析)

上下文敏感分析能夠區分相同功能的不同呼叫位點。如果 sanitize(input) 如果在一個地方使用可信任資料調用,而在另一個地方使用不可信資料調用,則上下文敏感分析會針對每個調用點生成不同的結果,而不是保守地​​假設所有調用點都出現最壞情況。與情境不敏感分析相比,這顯著降低了誤報率。

IFDS(過程間、有限、分散式、子集)架構將此問題形式化為圖可達性問題。在 IFDS 框架中,污點的流動由有限事實域上的分佈式函數描述,過程間傳播被簡化為圖可達性問題。這種形式化使得 IFDS 分析既精確又高效,FlowDroid(Android 平台)等工具以及多個 CodeQL 查詢都在內部使用了 IFDS。

3. 呼叫圖構建

每次過程間分析都需要一個呼叫圖:這是一個有向圖,其中節點代表函數,邊代表呼叫。建立呼叫圖本身涉及一些棘手的決策:

靜態調度 (Java 非虛函數呼叫、C 函數呼叫、Python 模組級呼叫):建模簡單直接, f() 電話 f.

動態分派(Java 虛擬方法,Python 鴨子類型呼叫):需要類別層次結構分析 (CHA) 或更精確的呼叫圖構造演算法 (RTA, VTA) 來確定在給定呼叫點實際可能呼叫方法的哪些實作。

反射呼叫和函數指標:最難處理的情況。工具要麼過度近似(假設任何匹配的函數都可以被調用,從而產生誤報),要麼近似不足(遺漏一些調用路徑,從而產生漏報)。

呼叫圖的精確度直接決定了過程間分析的精確度。包含過多邊的呼叫圖(過度近似)會產生假陽性結果;而遺漏邊的呼叫圖則會產生假陰性結果。

術間分析發現,術中分析無法做到

安全漏洞(污染分析)

OWASP Top 10 漏洞中的大多數需要進行製程間污染分析才能可靠地檢測出來:

  • SQL注入不受信任的輸入資料流經多個函數最終到達資料庫查詢。
  • XSS不受信任的 HTML 流透過渲染函數到達輸出上下文
  • 命令注入使用者控制的資料範圍 exec() or system() 透過中間職能
  • 路徑遍歷:從使用者輸入派生的檔案路徑到達檔案系統操作
  • SSRF使用者控制的 URL 流向 HTTP 用戶端調用

蟒蛇

# Intraprocedural tool: sees no issue in any individual function
# Interprocedural tool: traces the full taint path and reports SSRF

def get_target_url(request):
    return request.json().get("url")              # source: attacker-controlled

def fetch_resource(url):
    return requests.get(url)                      # sink: SSRF

def api_endpoint(request):
    target = get_target_url(request)
    data = fetch_resource(target)                 # full path: source -> sink
    return data

空指標和記憶體安全問題

Facebook Infer 是用於空安全分析的權威跨過程工具。它使用雙向溯因推理(一種跨過程推理方法)來發現函數的前提條件和後置條件,然後檢查呼叫者是否符合這些前提條件。對於需要知道函數在特定條件下可以傳回 null 的空解參考操作,跨過程工具是不可見的。

Java的

// Intraprocedural tool: no issue in either function
// Infer: finds that processResult() can receive null from fetchData()

String fetchData(boolean condition) {
    if (condition) return getData();
    return null;                    // can return null
}

void processResult(String result) {
    System.out.println(result.length());  // NPE if result is null
}

void run() {
    String data = fetchData(false);   // passes false: fetchData returns null
    processResult(data);              // null dereference
}

免費後使用和資源洩漏

在 C 和 C++ 中,釋放後使用漏洞通常涉及在一個函數中分配的內部存在另一個函數中釋放,而第三個函數仍然持有該記憶體的參考。單函數分析無法偵測到此類錯誤。過程間分析則著重分析控制流的完整性:確保函數呼叫遵循預期模式,從而防止漏洞。

架構耦合和依賴關係違規

除了安全性和正確性之外,過程間分析也應用於架構分析:哪些元件呼叫哪些元件,哪些依賴關係違反了定義的層邊界,以及哪些變更會沿著呼叫圖級聯傳播。這正是過程間分析的應用領域,而諸如此類的工具正是基於此。 SMART TS XL 應用於企業程式碼庫,追蹤一個函數的變更如何透過整個呼叫圖傳播,以確定真正的影響範圍。

當您需要進行程序間分析時

並非每個專案都需要進行全面的流程間分析。這取決於你想要發現什麼以及錯過它會帶來多大的損失。

在以下情況下需要進行程序間分析:

  • 安全性是首要考慮因素,您需要可靠地發現注入漏洞、跨站腳本攻擊 (XSS) 漏洞或伺服器端請求偽造 (SSRF) 漏洞。
  • 您正在分析一個遺留或企業級程式碼庫,其中業務邏輯跨越多個函數和模組。
  • 在進行重構之前,需要進行影響分析,以了解函數變更後會影響哪些方面。
  • 您正在進行合規性或審計分析,這需要證明其嚴謹性。
  • 您正在遷移或現代化程式碼庫,需要了解跨元件依賴關係。

術中分析在以下情況就足夠了:

  • 目標是規範化、遵守慣例,並找出明顯的局部錯誤。
  • 你需要將快速回饋整合到開發循環中(提交前、IDE、每個 PR 的持續整合)。
  • 程式碼庫規模很小,開發人員能夠了解所有的呼叫關係。
  • 安全性在不同的層級(經過驗證的框架、類型系統)得到保障,因此跨功能漏洞的風險較低。

對大多數團隊來說,切實可行的方案是:兩者兼具。過程內工具(ESLint、Pylint、Clippy)在每次提交時運行,以便快速獲得回饋。過程間工具(CodeQL、Infer、帶有污點規則的Semgrep)在拉取請求時運行,或按每日例行計劃運行,以便進行更全面的分析。這兩層工具是互補的,而非競爭的。

程序間分析工具:各自的涵蓋範圍

工具分析方法語言主要用例
代碼QL完全跨程序、基於IFDS的C/C++、Java、JS/TS、Python、Go、Ruby安全漏洞偵測
Facebook 推斷雙側外展,術間Java、C、C++、Objective-C空安全、記憶體洩漏、競態條件
Semgrep(污點模式)表層性程序間污染超過30種語言安全模式匹配與資料流
Snyk 程式碼基於機器學習的程式間Java、JS/TS、Python、C#安全且對開發者友善的回饋
多邊形空間摘要解讀C、C++、Ada安全關鍵型嵌入式系統
框架-C完整的程序間+正式C正式驗證、安全認證
SMART TS XL跨語言、跨系統COBOL、JCL、Java、Python、RPG、SQL企業影響分析、依賴關係映射

跨語言邊界的程式間分析

標準的跨過程工具只能在單一語言環境下運作。例如,CodeQL 查詢在 Java 程式碼庫中追蹤程式碼污染,但當 Java 程式碼呼叫 COBOL 程式或讀取 JCL 批次作業寫入的資料集時,查詢就會停止。這種邊界問題正是企業級分析工具面臨的挑戰,也是單一語言工具無法解決的問題。

SMART TS XL“ 靜態程式碼分析 在企業環境中,對每種語言同時執行過程間依賴分析。例如,當一個 Java 服務呼叫一個 COBOL 程序,該程序會從另一個 JCL 作業填入的 DB2 表中讀取資料時, SMART TS XL 追蹤該依賴鏈並將其表示為統一的依賴模型。 影響分析 然後,該功能應用此跨語言呼叫圖來回答:如果此 COBOL 程式發生更改,哪些 Java 服務、哪些 JCL 作業以及哪些下游消費者會受到影響?

這是在架構層面而非功能層面上應用的跨過程問題,同樣的原則(跨越邊界追蹤依賴關係圖,而不是止步於邊界)應用於整個企業系統,而非單一程序。正如在程式碼視覺化和依賴關係映射的背景下所探討的那樣,使完整的依賴結構可見是任何旨在理解變更影響的分析的先決條件。

對於進行以下活動的組織 遺產現代化呼叫圖 SMART TS XL 跨語言建構與單一語言中的過程間呼叫圖具有相同的目的:它是所有事物如何連接的地圖,如果沒有它,任何對影響、風險或遷移順序的分析都將基於假設而不是證據。

假陽性與假陰性的權衡

過程間分析之所以沒有被廣泛應用,並非因為沒人想到過它,而是因為精度和性能之間存在矛盾。對一個百萬行程式碼庫進行完整的上下文相關的過程間分析可能需要數小時甚至數天。而使用 ESLint 分析同樣的程式碼庫只需幾秒鐘。這種效能差異源自於結構性因素:過程間分析的複雜度會隨著呼叫圖的複雜度而增加,而不僅僅是程式碼庫的大小。

這項投資的好處是可以顯著降低假陽性率和假陰性率:

  • 誤報 (工具誤報錯誤):過程內工具會產生更多誤報,因為它們無法在不了解呼叫者上下文的情況下判斷可疑模式是否真的危險。例如,當一個過程內工具看到一個包含拼接字串的 SQL 查詢時,它無法判斷該字串是來自使用者輸入還是來自可信任的內部來源。
  • 假陰性 (工具無法偵測真正的漏洞):程式內工具會產生更多的漏檢,因為它們無法偵測跨越功能邊界的漏洞,而對於安全相關的分析而言,這才是大多數真正的漏洞。

選擇何時使用不同層級的分析,並將兩者結合在一個分層工具鏈中,這才是安全工程與形式主義合規之間的真正差異。僅僅知道程式碼檢查工具(linter)給了乾淨的結果,並不能真正反映你的安全狀況。而知道你的流程間污染分析已經追蹤了應用程式中從外部輸入來源到敏感目標的每一條路徑,並且沒有發現任何未經驗證的流程,則能提供更多資訊。