自訂 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")