自訂 Script

自訂 Script 可讓您進行更動態化的 DAST 掃描。您可以在掃描期間新增 JavaScript,以在要求送出至伺服器之前或收到回應之後操作 HTTP 要求與回應。

概觀

自訂 Script 會在 AppScan 內執行,並操作記憶體中的要求與回應模型,讓您能以程式化方式調整要求與檢查回應,而無需建立擴充功能或使用 Proxy。

概覽

要求前:在 HTTP 要求傳送至您的應用程式之前,執行指令碼來修改該要求(新增/移除標頭、加密資料、處理記號等)。

回應後:在從您的應用程式收到 HTTP 回應之後進行處理(解密內容、擷取值等)。

參數:僅使用 JavaScript 為多步驟作業和表單填寫工具產生值。

睿智運用此功能,便可提升掃描效率和效益。在上線部署前,先於控制環境中測試 Script,以確保如預期運作。

範本範例路徑:C:\Program Files (x86)\HCL\AppScan Standard\Templates。

自訂指令碼範例:GitHub 儲存庫。
  • 驗證指令碼 - 動態計算、產生並附加所需的安全憑證或權杖:
    • 基本
      • Basic HTTP 驗證 - 將使用者名稱和密碼編碼為 Base64 字串,並將其注入請求標頭。
    • 認證
      • 新增憑證 - 在 POST 請求的主體中包含格式化為 JSON 物件的使用者名稱和密碼。
      • 注入標頭 - 手動將 "Authorization" 欄位插入 HTTP 請求中。
      • 剖析回應 - 檢查伺服器的回應主體以擷取授權權杖。
  • 密碼學 - 對請求承載或標頭套用加密或雜湊演算法。
    • HMAC
      • HMACSHA512 - 使用 SHA-512 產生金鑰雜湊訊息鑑別碼。
    • SHA
      • SHA256_RSA - 透過使用 SHA-256 對資料進行雜湊處理,並使用 RSA 私密金鑰進行加密,以建立數位簽章。
      • SHA512 - 產生高度安全的 512 位元固定長度密碼雜湊。
      • SHA256 - 產生標準的 256 位元雜湊。

新增自訂 Script

依下列步驟,將自訂 JavaScript 新增至 DAST 掃描:
  1. 選擇配置設定的「進階」區段下的「自訂 Script」。
  2. 按一下 + 新增按鈕。
  3. 視需要,編輯預先填入的「名稱」欄位。
  4. 為您的指令碼選擇適當的 Execution context:
    1. 「要求前」適用於在傳送要求前執行的 Script。
    2. 「回應後」適用於在接收回應後執行的 Script。
    3. 「參數」適用於需要多步驟或填寫表單的作業。您可以將此環境定義與 setResultValue 方法搭配使用,讓指令碼將結果傳回AppScan。
  5. 在 JavaScript 欄位(程式碼編輯器)輸入 JavaScript,並在 AppScan 環境中使用 Ctrl+Space 以取得自動完成的建議。
  6. 按一下新增以儲存 Script。Script 會新增至清單且預設為啟用。
  7. 您可利用啟用勾選框,從自訂 Script 清單選擇啟用或停用 Script。
  8. 使用「啟用」勾選框管理 Script,或在 Script 清單中編輯、刪除或重新排序。「選項」功能表
  9. 按一下開始完整掃描以執行掃描。
  10. 結果:已啟用的腳本會針對每個要求或回應執行。您可以從資料 > 要求標籤 > 要求/回應,檢視 Script 的詳細資料。

    驗證 Script 的語法是否準確,以及是否與 AppScan 支援的 JavaScript 版本相容。如需詳細指南,請參閱 JavaScript 程式碼。

    註:
    • 自訂 Script 會儲存在掃描配置檔中,並可與範本一併匯出。它們不會包含在掃描結果中。
    • 自訂 Script 在匯出至範本時會預設加密。

啟用自訂 Script

自訂 Script 預設為已啟用,並會在錄製與掃描期間隨每個 HTTP 要求與回應執行。

若要停用 Script 執行,請前往工具 > 選項 > 進階標籤,並將 CustomScripts.Enable 設定為「False」。

執行內容

要求前

用途:變更傳出的 HTTP 要求。

資料:僅要求和環境物件可用,其他物件將為 null。
// 從儲存的全域變數附加 Authorization
var token = environment.globalVariables.get("Authorization");
if (token) request.headers.set("Authorization", token);

回應後

用途:檢查回應並保存值。

資料:僅回應和環境資料物件可用。其他物件將為空值。
// 從 JSON 回應擷取權杖並儲存以供重複使用
try {
		var result = JSON.parse(response.body);
		if (result && result.Authorization) {
			environment.globalVariables.addOrUpdate("Authorization", result.Authorization);
		}
} catch(e) { log(e); }

參數

用途:為多步驟作業和表單填寫程式產生值。
  • 此內容沒有可用的資料物件(要求或回應);若使用,將傳回空值。
  • 使用 setResultValue(value) 傳回值。

在表單填寫器中,當您選取值來源 > 自訂 Script時,清單僅會顯示執行內容為參數的 Script。

在多步驟作業中,您也可以使用參數 Script 提供動態值。
// 範例:為註冊流程傳回唯一的電子郵件
function random(n) { return Math.random().toString(36).slice(2, 2+n); }
var email = "test_" + Date.now() + "_" + random(6) + "@example.test";
setResultValue(email);

API 參照

要求物件(要求前內容)-完整 API
// 範例:
// 處理標頭
request.headers.set("Authorization", "Bearer");
request.headers.add("X-Trace", "abc");

// 操作主體
request.body = JSON.stringify( { user:"admin" });
if(request.body.includes("admin") ){ ... }

// 讀取路徑
if (request.path.startsWith("/api/v1")) { ... }

// 編輯查詢
request.query.addOrUpdate("lang","en")
request.query.remove("debug")
request.query.getQueryString()

// 讀取方法
if (request.method == "GET") { ... }
注意:
由於 Script 可能依要求而執行多次,建議使用 set 方法新增標頭,此方法會取代現有標頭且防止重複。僅在需要相同名稱但不同值的多個標頭時使用 add 方法。若已存在相同名稱與值的標頭,add 方法將不會再新增。請小心避免重複標頭,否則可能導致錯誤。
回應物件(回應之後環境)- 完整 API
// 範例:
// 處理標頭
response.headers.set("Authorization", "Bearer");
response.headers.add("X-Trace", "abc");

// 讀取主體(因為它是唯讀的)
var j = JSON.parse(response.body);

// 讀取路徑
if (response.path.startsWith("/api/v1")) { ... }

// 讀取回應
if (response.status.statusCode == 200) { ... }

// 讀取父請求
if (response.request.path.startsWith("/api/v1")) { ... }
參數環境 - 完整 API
// 將結果更新回 AppScan
setResultValue("ABC-" + Date.now());

環境資料

短期資料物件僅在任務結束前可用,例如在您暫停、停止或完成掃描、登入或結束手動測試時。每次開始新掃描或重新掃描時,系統會清除該資料。

  1. 全域變數:全域變數資料是一種鍵值資料結構,用於儲存使用者特定的資料配對。金鑰為獨一無二(表示兩個元素不會有相同金鑰,但多重元素可共用相同值),這一點對於在執行 Script 之間共用資料而言非常實用。
  2. 掃描狀態:包含目前掃描的相關資訊。
    // 檢查掃描的狀態
    environment.status.isInExplore();
    environment.status.isInTest();

記錄-完整 API

在 Script 中啟用記錄功能,以協助除錯與疑難排解。此環境不支援標準的 console.log 指令。請改用 log 函數搭配引號括住的訊息,例如:log("Your message here")。若在執行期間發生與 JavaScript 語法或使用無關的執行階段錯誤,該錯誤將記錄於引擎追蹤記錄中以供進一步分析。

日誌會寫入 AppScan 記載資料夾中的 UserScript.log 檔案。

依預設,該資料夾為 [UserDataFolder]\AppData\Roaming\HCL\AppScan Standard\logs。

限制與執行上限

  • 受限函數:eval、Function(建構子)、setTimeout、setlnterval。若使用其中任一函式,Script 將執行失敗。
  • 限制:最長執行時間:5 秒;記憶體限制:64 MB。超出此限制都會導致 Script 失敗。

    失敗時,掃描會暫停,詳細資訊會記錄在 UserScript.log 中。

支援的 JavaScript 版本

支援 ECMAScript ES6 (2015) 至 ES15 (2024),但以下功能除外:
  • ES6:產生器、尾端呼叫
  • ES8:共用記憶體和不可分割運算
  • ES15:Atomics.waitAsync、正規表示式旗標 \v

自動完成中可用的物件

這些物件會出現在 Script 編輯器的自動完成建議中,方便搜尋與使用:

  • 關鍵字
  • 陣列
  • 日期
  • 物件
  • 字串
  • RegExp
  • JSON
  • 對映
  • Math
受支援但不在自動完成中

這些物件已完全受 Script 引擎支援,但不會出現在自動完成清單中。您仍可在編輯器中直接輸入名稱來使用它們:

  • ArrayBuffer
  • BigInt
  • Boolean
  • DataView
  • 錯誤
  • Iterator
  • 號碼
  • Promise
  • Proxy
  • Reflect
  • SharedArrayBuffer
  • Symbol
  • TypedArray

  • WeakMap

  • WeakRef

  • WeakSet

    註:
    「不在自動完成中」表示編輯器不會自動建議這些項目,但這些物件仍已實作,且行為與標準 JavaScript 完全相同。

疑難排解

常見問題與解決方案:
  1. 腳本失敗:檢查日誌資料夾(預設:[UserDataFolder]\AppData\Roaming\HCL\AppScan Standard\logs)以取得錯誤的詳細資訊。若遇到未處理的例外,請使用 try...catch 區塊包覆程式碼,以妥善處理錯誤。
  2. 腳本未執行:請確定已在配置設定中啟用腳本。

    若問題與登入或授權相關,請考慮從配置 → 進階配置 → 安全性套件順序中移除 negotiate 值,防止 AppScan 自動處理這些鑑別。

要求完整 API

表 1. 要求物件-屬性
成員 類型 說明 範例
標頭 物件 封裝目前 HTTP 要求標頭的物件,提供新增、更新、擷取或移除標頭的方法,讓您能動態且精確地控制要求的中繼資料。 request.headers.set("Authorization","Bearer ...")
主體 字串 目前 HTTP 要求的本文。 request.body = JSON.stringify({ user:"jsmith" })
路徑 字串

唯讀🔒

目前 HTTP 要求的相對路徑。

if (request.path.startsWith("/api/v1/")) { ... }
查詢 物件 包含多種方法可在目前的 HTTP 要求中新增、更新或移除查詢參數的物件,讓您能動態操作查詢字串。 request.query.addOrUpdate("lang","en") request.query.remove("debug")
方法 字串

唯讀🔒

目前 HTTP 要求的方法。

if (request.method == "GET") {{ ... }}
表 2. 查詢物件-方法
成員 類型 說明 範例

addOrUpdate

(name: string, value: string)

void 在查詢字串中新增值或更新現有值。 request.query.addOrUpdate("appscan","appscanvalue")
getQueryString() 字串

傳回目前 HTTP 要求的完整查詢字串,其中包含所有參數及其值。

request.query.getQueryString()

包含

(name: string)

布林

檢查目前 HTTP 要求中是否存在指定的查詢參數。

若參數存在則傳回 true,否則傳回 false。

request.query.contains("lang")

移除

(name: string)

布林

從目前的 HTTP 要求中移除特定查詢參數。

若成功移除參數則傳回 true,若未移除則傳回 false。

request.query.remove("lang")
clear() void

從目前的 HTTP 要求中移除所有查詢參數。

request.query.clear()

回應完整 API

表 3. 回應 objectStatus 物件
成員 類型 說明 範例
標頭 物件 封裝目前 HTTP 回應標頭的物件,提供新增、更新、擷取或移除標頭的方法,讓您能動態且精確地控制回應的中繼資料。 response.headers.set("Authorization","Bearer ...")
主體 字串 目前 HTTP 回應的本文。 response.body = JSON.stringify({ user:"jsmith" })
路徑 字串

唯讀🔒

目前 HTTP 回應的相對路徑。

if (response.path.startsWith("/api/v1/")) { ... }
狀態 物件 表示目前 HTTP 回應狀態的物件,包含狀態碼與狀態訊息等屬性。 if (response.status.code == 200) { ... }
要求 物件 表示觸發此回應的要求物件。要求資料為唯讀,且包含先前所述要求物件中定義的所有屬性。 if (response.request.path) { ... }
表 4. 屬性
成員 類型 說明 範例
statusCode 數字

唯讀🔒

目前回應的 HTTP 狀態碼。

response.status.statusCode = 200
statusDescription 字串

唯讀🔒

目前回應的 HTTP 狀態訊息。

response.status.statusDescription = "OK"
statusLine 字串

唯讀🔒

目前回應的 HTTP 狀態行。

response.status.statusLine = "HTTP/1.1 200 OK"

參數完整 API

表 5. 參數物件
成員 類型 說明 範例

setResultValue

(value: string)

void 設定目前的參數內容 Script 結果值。 setResultValue("ABC-" + Date.now())

常見物件

表 6. 標頭物件-屬性
成員 類型 說明 範例
項目 陣列

唯讀🔒

目前 HTTP 要求中所有標頭的陣列。

if (request.headers.items.length > 0) { ... }

if (response.headers.items.length > 0) { ... }

表 7. 標頭物件-方法
成員 類型 說明 範例
新增

(name: string, value: string)

void

在目前的 HTTP 要求中新增一個標頭。若已存在相同名稱與值的標頭,則不會再次新增。

request.headers.add("Authorization", "MyCoolApiKey")

response.headers.add("Authorization",MyCoolApiKey")

集合

(name: string, value: string)

void 設定目前的 HTTP 要求中的一個標頭。若該標頭已存在,將以新值取代舊值。

request.headers.set("Authorization", "SecondApiKey")

response.headers.set("Authorization","SecondApiKey")

移除

(name: string)

void 從目前的 HTTP 要求中移除一個標頭。 request.headers.remove("Authorization")response.headers.remove("Authorization")
[name: string] 字串 依標頭名稱擷取特定標頭的值。若該標頭不存在,則傳回空值。

request.headers['Authorization'] = "myapikey3"

response.headers['Authorization'] = "myapikey3"

環境資料

表 8. 環境物件-屬性
成員 類型 說明 範例
globalVariables 物件 用於儲存使用者特定資料配對的鍵值資料結構。鍵必須唯一,確保不重複,而值可在多個鍵之間共用。此結構可促進執行中 Script 之間的資料共用。 environment.globalVariables.addOrUpdate(key, value)
狀態 物件 關於目前掃描狀態的資訊。 environment.status.isInExplore()
表 9. 廣域變數物件-方法
成員 類型 說明 範例

addOrUpdate

(key: string, value:

string)

void 新增一組鍵值配對至廣域變數,或更新現有鍵的值。 environment.globalVariables.addOrUpdate("mycoolkey", "testValue")

移除

(key: string)

布林 從廣域變數中移除一組鍵值配對。若找到並成功移除鍵則傳回 true,否則傳回 false。 environment.globalVariables.remove("mycoolkey")
get

(key: string)

字串 從廣域變數中擷取特定鍵的值。若該鍵不存在,則傳回空字串。 environment.globalVariables.get("mycoolkey")
表 10. 狀態物件-方法
方法 類型 說明 範例
isInExplore() 布林 檢查掃描目前是否處於探索階段。 environment.status.isInExplore()
isInTest() 布林 檢查掃描目前是否處於測試階段。 environment.status.isInTest()

記載

表 11. 方法
成員 類型 說明 範例
log(message: string) log(message: string, source: string) void 新增一筆包含指定訊息與可選來源的日誌項目。 log("This is a log message")

log("This is a log message", "mySource")