在軟體開發過程中,測試是確保軟體品質和可靠性的關鍵環節。傳統的測試方法通常耗時且依賴大量手動操作,容易造成效率瓶頸。本文探討了各種測試方法,包括長時間穩定性測試,用於檢測記憶體洩漏等問題;模糊測試,透過異常輸入資料發現隱藏錯誤;以及靜態程式碼分析,用於檢查程式碼的規範性和安全性。這些測試方法各有其挑戰,例如長時間穩定性測試需要大量的時間和資源,模糊測試需要自動化處理大量隨機輸入,而靜態程式碼分析則需要專業的工具和技能。隨著 DevOps 的興起,自動化測試和持續整合/持續佈署(CI/CD)的理念逐漸被廣泛採用,大幅提升了測試效率和軟體交付速度。透過 GitLab CI/CD 等工具,可以自動化執行各種測試,並將測試結果整合到統一的儀錶板中,方便開發者快速識別和修復問題。此外,安全測試也變得越來越重要,涵蓋了靜態應用程式安全測試(SAST)、動態應用程式安全測試(DAST)、互動式應用程式安全測試(IAST)以及軟體組合分析(SCA)等多個方面。這些安全測試方法可以幫助開發者及早發現和修復安全漏洞,降低軟體安全風險。從傳統的手動測試到 DevOps 的自動化流程,軟體測試的演進不斷提升著軟體的品質和安全性,也推動著軟體開發流程的持續改進。

測試的挑戰與解決方案

在進入 DevOps 之前,瞭解如何有效地測試軟體是非常重要的。這不僅涉及功能測試,還包括效能、穩定性和資源管理等多個方面。以下是一些常見的測試方法及其挑戰。

長時間穩定性測試

長時間穩定性測試(soak tests)是一種讓應用程式執行數小時或數天的測試方法,以檢查其在長時間執行下的穩定性和效能。這種測試可以揭示記憶體洩漏、過度日誌記錄或未關閉的背景程式等問題。然而,這種測試需要大量的時間和硬體資源來執行和監控。

模糊測試

模糊測試(fuzz testing)是一種強大但被低估的測試方法,它透過輸入有效但異常的資料來揭示傳統功能測試可能忽略的錯誤。例如,尋求建立一個包含1000個字母的使用者名稱或使用克林貢字母Unicode字元在發貨地址中。模糊測試引入了強烈的隨機性,這使得它們必須自動化以便處理大量隨機輸入值。

靜態程式碼分析

靜態程式碼分析是一種不執行程式碼而檢查原始碼的方法。它可以檢查程式碼是否遵守編寫最佳實踐和語言習慣。例如,它可以檢查未分配值的變數或未使用的變數。雖然這些問題可能不會導致程式碼當機,但會影響程式碼的可讀性和維護性。

手動構建與驗證程式碼

在執行所有這些不同型別的測試後,您可能會面臨如何解析、處理和報告結果的挑戰。通常,您需要將多個測試工具整合到一個自動更新的儀錶板中,但這並不總是可行的。此外,這些測試需要反覆執行以捕捉迴歸或“閃爍”測試,這些測試取決於網路條件、伺服器負載等因素。

測試頻率與效率

由於執行測試既費時又困難,開發者傾向於盡可能少地執行它們。這導致了在開發週期結束時才進行測試,使得錯誤更難診斷和修復。為了提高效率,開發者需要考慮何時及如何頻繁地執行這些測試。

以下是一些具體案例:

案例1:記憶體洩漏問題

假設您有一個應用程式,隨著時間推移會消耗越來越多的記憶體。這可能是由於某些物件未被釋放所致。透過長時間穩定性測試,您可以檢測到這些問題並進行修復。

import time
import gc

def leak_memory():
    while True:
        # 模擬記憶體洩漏
        data = [i for i in range(1000000)]
        time.sleep(1)
        gc.collect()

# 請注意:以下程式碼會導致記憶體洩漏
leak_memory()

內容解密:

上述程式碼模擬了一個記憶體洩漏問題。leak_memory 函式在每次迴圈中建立一個大列表並保持在記憶體中,而不進行釋放。隨著時間推移,應用程式將消耗越來越多的記憶體。

為了修復這個問題,我們可以在每次迴圈結束後手動釋放資源:

import time
import gc

def leak_memory():
    while True:
        # 模擬記憶體洩漏
        data = [i for i in range(1000000)]
        time.sleep(1)
        del data  # 手動釋放資源
        gc.collect()

leak_memory()

案例2:模糊測試發現錯誤

假設您有一個處理PDF檔案轉換為HTML檔案的應用程式。透過模糊測試,您可以發現處理非法PDF檔案時可能會出現錯誤。

from fpdf import FPDF

def convert_pdf_to_html(pdf_data):
    pdf = FPDF()
    try:
        pdf.set_auto_page_break(auto=True, margin=15)
        pdf.add_page()
        pdf.set_font("Arial", size=12)
        pdf.multi_cell(0, 10, pdf_data)
        return pdf.output(dest='S').decode('latin-1')
    except Exception as e:
        return str(e)

# 模擬模糊輸入
invalid_pdf_data = "This is not a valid PDF data"
print(convert_pdf_to_html(invalid_pdf_data))

內容解密:

上述程式碼展示了一個簡單的PDF轉換HTML函式。透過模糊輸入(例如非法PDF資料),我們可以發現函式在處理非法資料時可能會出現錯誤。這有助於我們改進錯誤處理邏輯並確保應用程式更加穩定。

DevOps 之前的軟體開發生活

在 DevOps 出現之前,軟體開發的過程充滿了挑戰和困難。瞭解這些挑戰,有助於我們更好地欣賞 DevOps 帶來的變革。以下是一些關鍵點:

測試的重要性與挑戰

在 DevOps 時代之前,軟體開發者需要面對各種測試的繁瑣流程。不同形式的測試,如單元測試、整合測試和系統測試,都需要投入大量時間和精力。設定測試環境本身就是一項複雜的任務,而手動執行那些無法自動化的使用者測試更是令開發者頭痛。處理和報告測試結果也需要花費大量時間,而找到並修復隱藏在大量程式碼中的漏洞更是困難重重。

在這個過程中,理解如何簡化測試流程和提高測試效率,是 DevOps 帶來的巨大改變之一。當你瞭解了 GitLab CI/CD Pipeline如何簡化不同種類別測試的執行和結果檢視時,你會對以前那種繁瑣的測試流程感到同情。早期和頻繁的測試使得問題更容易被發現,並且修復起來也更加便宜。

安全測試

功能測試只是其中一種測試形式,另一種重要的形式是安全測試。安全測試通常由專門的團隊進行,這些團隊與傳統的 QA 部門是分開的。安全測試可以分為三大類別:

  1. 檢查原始碼:檢查原始碼中的非標準和不安全的編碼實踐。
  2. 與執行中的程式碼互動:透過使用應用程式介面、傳送 REST API 請求或存取 Web 應用程式的不同 URL 來互動。
  3. 檢查專案使用的第三方依賴:檢查依賴中的已知漏洞。

靜態碼分析

靜態碼分析是透過檢查原始碼來找到不安全編碼實踐的一種方法。例如,如果你從使用者那裡取得輸入並使用該輸入來查詢資料函式庫,這樣可能會導致 SQL 注入攻擊。專業的程式碼審查員可以立即發現這類別問題並提出解決方案。

employee_name = get_user_input()
sql = "SELECT salary FROM employee_records WHERE employee_name = $employee_name"
call_database(sql)

內容解密:

上述範例展示瞭如何從使用者那裡取得輸入但沒有驗證輸入就直接使用它在 SQL 陳述式中,這樣可能會導致 SQL 注入攻擊。較為危險的是使用者可能會輸入像 Smith OR (0 = 0) 這樣的惡意值來篡改 SQL 陳述式。

靜態碼分析也可以自動進行。許多整合開發環境(IDE)提供內建的靜態碼分析功能,能夠檢測到非標準或不安全的程式碼並顯示警告。

秘密檢測

秘密檢測是靜態碼分析的一種特殊形式。它用於檢查原始碼中是否包含敏感資料,如密碼、佈署鑰匙、社交安全號碼等。

String bethSSN = "555-12-1212";
if (customerSSN.equals(bethSSN))) {
    System.out.println("Welcome, Beth!");
}

內容解密:

上述 Java 範例中包含了一個社會安全號碼(SSN),這樣任何有讀取程式碼許可權的人都可以看到它。秘密檢測工具可以掃描原始碼以找到這類別敏感資料並建議將其移至更安全的位置儲存。

動態分析

動態分析是透過與執行中的程式碼互動來找到軟體缺陷的一種方法。這種互動可以透過使用應用程式介面、傳送 REST API 請求或存取 Web 應用程式的不同 URL 來進行。

例如,你的 Web 伺服器可能會在每個回應頭中包含其版本號。這看似無害,但可能會提供給惡意行為者有關哪些 Web 伺服器針對性攻擊可能有效或無效的線索。

puts 'how many hats do you have?'
num_hats = gets.to_i
puts 'how many cats do you have?'
num_cats = gets.to_i
puts "you have #{num_hats / num_cats} hats per cat"

內容解密:

上述 Ruby 範例中,當 num_cats 的值為零時會產生除零錯誤(ZeroDivisionError),這可能會導致程式當機。雖然這看似簡單的錯誤,但惡意行為者可能會利用它來暴露資料或造成拒絕服務攻擊。

依賴掃描

依賴掃描是比較你產品所依賴專案的名稱和版本號與已知漏洞資料函式庫的一種做法。它有助於識別哪些依賴需要升級或移除以提高軟體的安全性。幾乎每一個非平凡軟體都依賴於數十甚至數百個外部函式庫和框架。

理解 DevOps 出現之前軟體開發所面臨的挑戰,有助於我們更好地欣賞 GitLab 和其他 DevOps 工具帶來的變革。無論是在自動化Pipeline、持續整合/持續佈署(CI/CD),還是在提升安全性方面,DevOps 已經顯著改善了軟體開發和交付流程。

安全測試:手動與自動化的考量

在現代軟體開發中,安全性始終是最關鍵的考量之一。手動安全測試是確保軟體安全的重要手段,特別是針對依賴大量第三方開源函式庫的專案。這些函式庫的原始碼常常被駭客仔細檢查,以尋找潛在的漏洞。雖然這些漏洞通常會迅速修復,但如果你的專案使用了舊版、未修補的函式庫,依賴掃描工具可以幫助你識別這些潛在的風險。

依賴掃描

依賴掃描是一種用來檢查專案中使用的第三方函式庫是否存在已知漏洞的方法。以最近的 Log4j 漏洞為例,許多 Java 專案依賴這個開源函式庫來記錄訊息。然而,Log4j 最近被發現存在一個嚴重的漏洞,允許攻擊者遠端執行命令或安裝惡意軟體。這對於任何使用 Log4j 的系統來說都是一個巨大的威脅。幸運的是,依賴掃描可以幫助我們識別這類別問題。任何更新過的依賴掃描工具都會告訴你,如果你的軟體直接或間接依賴於未修補版本的 Log4j,並建議你升級到安全版本。

容器掃描

現在許多軟體產品都是以 Docker 映像形式交付的。簡單來說,Docker 映像是一個安裝了你的應用程式並封裝成可由 Docker 或類別似工具執行的 Linux 分發版。如果你構建了一個包含過時 Linux 分發版(可能包含安全漏洞)的 Docker 映像,那麼你的應用程式就不會像應該那樣安全。

容器掃描會檢查你 Docker 化應用程式所使用的基礎 Linux 映像,並查詢已知安全漏洞資料函式庫,以確定你封裝的應用程式是否可能受到攻擊。例如,CentOS 6 在 2020 年停止維護後,其包含的函式庫有許多嚴重的安全漏洞。容器掃描會警告你這個問題,並建議你考慮將應用程式的 Docker 映像升級到使用 CentOS 7 或更高版本作為基礎映像。

自動化工具與 GitLab CI/CD

雖然有些測試可以自動化,但自動化工具仍然具有負擔:需要安裝和組態工具、更新框架和依賴、設定和維護測試環境、整理報告並以整合方式顯示報告。如果將一些任務外包給外部公司或 SaaS 工具,你仍需學習每個工具的不同 GUI、維護不同服務的使用者帳戶和管理多個許可證。

GitLab 的 CI/CD Pipeline將這些繁瑣且多步驟的一連串過程替換為快速、自動化且易於組態和管理的一套流程。後續章節將詳細探討 GitLab 是如何簡化和自動化這些過程。

軟體包裝與佈署

在構建和驗證軟體後,下一步是考慮如何將其包裝和佈署到生產環境中。這個過程也可能成為一項繁瑣且容易出錯的人工任務。如何將應用程式封裝成可佈署狀態取決於它所使用的程式語言及構建管理工具。

例如,如果使用 Maven 工具來管理 Java 專案,則需要執行一組不同於 Gradle 工具所需命令。而將 Ruby 程式碼封裝成 Ruby Gem 需要完全不同的一套流程。包裝通常涉及收集大量檔案、使用適當語言工具進行封裝、雙重檢查檔案和許可檔案、並可能對封裝後程式碼進行加密簽名以顯示其來源可信。

商業授權掃描

在佈署程式碼到生產環境之前,還有一種需要進行的一項檢查:商業授權掃描。商業授權掃描確保所有使用到第三方元件均符合其許可條款規範。未經授權或者誤解適用條款可能會導致法律問題或專案停滯不前。

此圖示展示了不同型別安全測試之間邏輯關係:

  graph TD
    A[靜態應用程式安全測試 (SAST)] --> B[針對原始碼]
    B --> C[早期漏洞識別]
    D[動態應用程式安全測試 (DAST)] --> E[針對執行中系統]
    E --> F[實時漏洞識別]
    G[互動式應用程式安全測試 (IAST)] --> H[運作時間深入分析]
    H --> I[即時威脅處理]
    J[軟體組合分析 (SCA)] --> K[第三方元件風險評估]
    K --> L[依賴管理]
    M[基礎設施即程式碼 (IaC) 測試] --> N[基礎設施組態檢查]
    N --> O[基礎設施強固性]

此圖示內容解密:

此圖示展示了四種主要型別之間邏輯關係:

  1. SAST(靜態應用程式安全測試):SAST 是針對原始碼進行分析以識別早期漏洞。
  2. DAST(動態應用程式安全測試):DAST 主要針對執行中的系統進行實時漏洞識別。
  3. IAST(互動式應用程式安全測試):IAST 在運作時間深入分析系統,並能即時處理威脅。
  4. SCA(軟體組合分析):SCA 主要針對第三方元件進行風險評估並管理依賴。
  5. IaC(基礎設施即程式碼):IaC 測試主要針對基礎設施組態檢查以保障基礎設施強固性。

玄貓希望能藉由介紹以上概念讓大家瞭解如何透過各種方法來提升軟體開發過程中的安全性以及如何有效地減少手動操作所帶來之風險及複雜度!